Updated to latest version.
This commit is contained in:
parent
26cd94acab
commit
5977b9e348
1 changed files with 278 additions and 261 deletions
539
app/assets/javascripts/app/lib/base/bootstrap-tokenfield.js
vendored
Executable file → Normal file
539
app/assets/javascripts/app/lib/base/bootstrap-tokenfield.js
vendored
Executable file → Normal file
|
@ -25,7 +25,7 @@
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// Browser globals
|
// Browser globals
|
||||||
factory(jQuery);
|
factory(jQuery, window);
|
||||||
}
|
}
|
||||||
}(function ($, window) {
|
}(function ($, window) {
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
|
|
||||||
// Extend options
|
// Extend options
|
||||||
this.options = $.extend(true, {}, $.fn.tokenfield.defaults, { tokens: this.$element.val() }, this.$element.data(), options)
|
this.options = $.extend(true, {}, $.fn.tokenfield.defaults, { tokens: this.$element.val() }, this.$element.data(), options)
|
||||||
|
|
||||||
// Setup delimiters and trigger keys
|
// Setup delimiters and trigger keys
|
||||||
this._delimiters = (typeof this.options.delimiter === 'string') ? [this.options.delimiter] : this.options.delimiter
|
this._delimiters = (typeof this.options.delimiter === 'string') ? [this.options.delimiter] : this.options.delimiter
|
||||||
this._triggerKeys = $.map(this._delimiters, function (delimiter) {
|
this._triggerKeys = $.map(this._delimiters, function (delimiter) {
|
||||||
|
@ -63,9 +63,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
var specialCharacters = ['\\', '$', '[', '{', '^', '.', '|', '?', '*', '+', '(', ')']
|
var specialCharacters = ['\\', '$', '[', '{', '^', '.', '|', '?', '*', '+', '(', ')']
|
||||||
$.each(this._delimiters, function (index, char) {
|
$.each(this._delimiters, function (index, character) {
|
||||||
var pos = $.inArray(char, specialCharacters)
|
var pos = $.inArray(character, specialCharacters)
|
||||||
if (pos >= 0) _self._delimiters[index] = '\\' + char;
|
if (pos >= 0) _self._delimiters[index] = '\\' + character;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Store original input width
|
// Store original input width
|
||||||
|
@ -86,7 +86,7 @@
|
||||||
var hidingPosition = $('body').css('direction') === 'rtl' ? 'right' : 'left',
|
var hidingPosition = $('body').css('direction') === 'rtl' ? 'right' : 'left',
|
||||||
originalStyles = { position: this.$element.css('position') };
|
originalStyles = { position: this.$element.css('position') };
|
||||||
originalStyles[hidingPosition] = this.$element.css(hidingPosition);
|
originalStyles[hidingPosition] = this.$element.css(hidingPosition);
|
||||||
|
|
||||||
this.$element
|
this.$element
|
||||||
.data('original-styles', originalStyles)
|
.data('original-styles', originalStyles)
|
||||||
.data('original-tabindex', this.$element.prop('tabindex'))
|
.data('original-tabindex', this.$element.prop('tabindex'))
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
|
|
||||||
// Create a new input
|
// Create a new input
|
||||||
var id = this.$element.prop('id') || new Date().getTime() + '' + Math.floor((1 + Math.random()) * 100)
|
var id = this.$element.prop('id') || new Date().getTime() + '' + Math.floor((1 + Math.random()) * 100)
|
||||||
this.$input = $('<input type="text" class="token-input" autocomplete="off" />')
|
this.$input = $('<input type="'+this.options.inputType+'" class="token-input" autocomplete="off" />')
|
||||||
.appendTo( this.$wrapper )
|
.appendTo( this.$wrapper )
|
||||||
.prop( 'placeholder', this.$element.prop('placeholder') )
|
.prop( 'placeholder', this.$element.prop('placeholder') )
|
||||||
.prop( 'id', id + '-tokenfield' )
|
.prop( 'id', id + '-tokenfield' )
|
||||||
|
@ -116,7 +116,7 @@
|
||||||
|
|
||||||
// Set up a copy helper to handle copy & paste
|
// Set up a copy helper to handle copy & paste
|
||||||
this.$copyHelper = $('<input type="text" />').css('position', 'absolute').css(hidingPosition, '-10000px').prop('tabindex', -1).prependTo( this.$wrapper )
|
this.$copyHelper = $('<input type="text" />').css('position', 'absolute').css(hidingPosition, '-10000px').prop('tabindex', -1).prependTo( this.$wrapper )
|
||||||
|
|
||||||
// Set wrapper width
|
// Set wrapper width
|
||||||
if (elStyleWidth) {
|
if (elStyleWidth) {
|
||||||
this.$wrapper.css('width', elStyleWidth);
|
this.$wrapper.css('width', elStyleWidth);
|
||||||
|
@ -134,27 +134,27 @@
|
||||||
this.disable();
|
this.disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set tokenfield readonly, if original input is readonly
|
||||||
|
if (this.$element.prop('readonly')) {
|
||||||
|
this.readonly();
|
||||||
|
}
|
||||||
|
|
||||||
// Set up mirror for input auto-sizing
|
// Set up mirror for input auto-sizing
|
||||||
this.$mirror = $('<span class="js-tokenfieldMirror" style="position:absolute; top:-999px; left:0; white-space:pre;"/>');
|
this.$mirror = $('<span style="position:absolute; top:-999px; left:0; white-space:pre;"/>');
|
||||||
this.$input.css('min-width', this.options.minWidth + 'px')
|
this.$input.css('min-width', this.options.minWidth + 'px')
|
||||||
$.each([
|
$.each([
|
||||||
'fontFamily',
|
'fontFamily',
|
||||||
'fontSize',
|
'fontSize',
|
||||||
'fontWeight',
|
'fontWeight',
|
||||||
'fontStyle',
|
'fontStyle',
|
||||||
'letterSpacing',
|
'letterSpacing',
|
||||||
'textTransform',
|
'textTransform',
|
||||||
'wordSpacing',
|
'wordSpacing',
|
||||||
'textIndent'
|
'textIndent'
|
||||||
], function (i, val) {
|
], function (i, val) {
|
||||||
_self.$mirror[0].style[val] = _self.$input.css(val);
|
_self.$mirror[0].style[val] = _self.$input.css(val);
|
||||||
});
|
});
|
||||||
if (!$('.js-tokenfieldMirror').get(0)) {
|
this.$mirror.appendTo( 'body' )
|
||||||
this.$mirror.appendTo( 'body' )
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.$mirror = $('.js-tokenfieldMirror')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert tokenfield to HTML
|
// Insert tokenfield to HTML
|
||||||
this.$wrapper.insertBefore( this.$element )
|
this.$wrapper.insertBefore( this.$element )
|
||||||
|
@ -162,9 +162,9 @@
|
||||||
|
|
||||||
// Calculate inner input width
|
// Calculate inner input width
|
||||||
this.update()
|
this.update()
|
||||||
|
|
||||||
// Create initial tokens, if any
|
// Create initial tokens, if any
|
||||||
this.setTokens(this.options.tokens, false, false)
|
this.setTokens(this.options.tokens, false, ! this.$element.val() && this.options.tokens )
|
||||||
|
|
||||||
// Start listening to events
|
// Start listening to events
|
||||||
this.listen()
|
this.listen()
|
||||||
|
@ -172,23 +172,28 @@
|
||||||
// Initialize autocomplete, if necessary
|
// Initialize autocomplete, if necessary
|
||||||
if ( ! $.isEmptyObject( this.options.autocomplete ) ) {
|
if ( ! $.isEmptyObject( this.options.autocomplete ) ) {
|
||||||
var side = this.textDirection === 'rtl' ? 'right' : 'left'
|
var side = this.textDirection === 'rtl' ? 'right' : 'left'
|
||||||
var autocompleteOptions = $.extend({
|
, autocompleteOptions = $.extend({
|
||||||
minLength: this.options.showAutocompleteOnFocus ? 0 : null,
|
minLength: this.options.showAutocompleteOnFocus ? 0 : null,
|
||||||
position: { my: side + " top", at: side + " bottom", of: this.$wrapper }
|
position: { my: side + " top", at: side + " bottom", of: this.$wrapper }
|
||||||
}, this.options.autocomplete )
|
}, this.options.autocomplete )
|
||||||
|
|
||||||
this.$input.autocomplete( autocompleteOptions )
|
this.$input.autocomplete( autocompleteOptions )
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize typeahead, if necessary
|
// Initialize typeahead, if necessary
|
||||||
if ( ! $.isEmptyObject( this.options.typeahead ) ) {
|
if ( ! $.isEmptyObject( this.options.typeahead ) ) {
|
||||||
var typeaheadOptions = $.extend({
|
|
||||||
minLength: this.options.showAutocompleteOnFocus ? 0 : null
|
var typeaheadOptions = this.options.typeahead
|
||||||
}, this.options.typeahead)
|
, defaults = {
|
||||||
this.$input.typeahead( null, typeaheadOptions )
|
minLength: this.options.showAutocompleteOnFocus ? 0 : null
|
||||||
|
}
|
||||||
|
, args = $.isArray( typeaheadOptions ) ? typeaheadOptions : [typeaheadOptions, typeaheadOptions]
|
||||||
|
|
||||||
|
args[0] = $.extend( {}, defaults, args[0] )
|
||||||
|
|
||||||
|
this.$input.typeahead.apply( this.$input, args )
|
||||||
this.typeahead = true
|
this.typeahead = true
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$element.trigger('tokenfield:initialize')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tokenfield.prototype = {
|
Tokenfield.prototype = {
|
||||||
|
@ -196,137 +201,131 @@
|
||||||
constructor: Tokenfield
|
constructor: Tokenfield
|
||||||
|
|
||||||
, createToken: function (attrs, triggerChange) {
|
, createToken: function (attrs, triggerChange) {
|
||||||
|
var _self = this
|
||||||
|
|
||||||
if (typeof attrs === 'string') {
|
if (typeof attrs === 'string') {
|
||||||
attrs = { value: attrs, label: attrs }
|
attrs = { value: attrs, label: attrs }
|
||||||
|
} else {
|
||||||
|
// Copy objects to prevent contamination of data sources.
|
||||||
|
attrs = $.extend( {}, attrs )
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof triggerChange === 'undefined') {
|
if (typeof triggerChange === 'undefined') {
|
||||||
triggerChange = true
|
triggerChange = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var _self = this
|
|
||||||
, value = $.trim(attrs.value)
|
|
||||||
, label = attrs.label && attrs.label.length ? $.trim(attrs.label) : value
|
|
||||||
|
|
||||||
if (!value.length || !label.length || value.length < this.options.minLength) return
|
// Normalize label and value
|
||||||
|
attrs.value = $.trim(attrs.value.toString());
|
||||||
|
attrs.label = attrs.label && attrs.label.length ? $.trim(attrs.label) : attrs.value
|
||||||
|
|
||||||
|
// Bail out if has no value or label, or label is too short
|
||||||
|
if (!attrs.value.length || !attrs.label.length || attrs.label.length <= this.options.minLength) return
|
||||||
|
|
||||||
|
// Bail out if maximum number of tokens is reached
|
||||||
if (this.options.limit && this.getTokens().length >= this.options.limit) return
|
if (this.options.limit && this.getTokens().length >= this.options.limit) return
|
||||||
|
|
||||||
// Allow changing token data before creating it
|
// Allow changing token data before creating it
|
||||||
var prepareEvent = $.Event('tokenfield:preparetoken')
|
var createEvent = $.Event('tokenfield:createtoken', { attrs: attrs })
|
||||||
prepareEvent.token = {
|
this.$element.trigger(createEvent)
|
||||||
value: value,
|
|
||||||
label: label
|
|
||||||
}
|
|
||||||
this.$element.trigger( prepareEvent )
|
|
||||||
|
|
||||||
if (!prepareEvent.token) return
|
// Bail out if there if attributes are empty or event was defaultPrevented
|
||||||
|
if (!createEvent.attrs || createEvent.isDefaultPrevented()) return
|
||||||
|
|
||||||
value = prepareEvent.token.value
|
var $token = $('<div class="token" />')
|
||||||
label = prepareEvent.token.label
|
|
||||||
|
|
||||||
// Check for duplicates
|
|
||||||
if (!this.options.allowDuplicates && $.grep(this.getTokens(), function (token) {
|
|
||||||
return token.value === value
|
|
||||||
}).length) {
|
|
||||||
// Allow listening to when duplicates get prevented
|
|
||||||
var preventDuplicateEvent = $.Event('tokenfield:preventduplicate')
|
|
||||||
preventDuplicateEvent.token = {
|
|
||||||
value: value,
|
|
||||||
label: label
|
|
||||||
}
|
|
||||||
this.$element.trigger( preventDuplicateEvent )
|
|
||||||
// Add duplicate warning class to existing token for 250ms
|
|
||||||
var duplicate = this.$wrapper.find( '.token[data-value="' + value + '"]' ).addClass('duplicate')
|
|
||||||
setTimeout(function() {
|
|
||||||
duplicate.removeClass('duplicate');
|
|
||||||
}, 250)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var token = $('<div class="token" />')
|
|
||||||
.attr('data-value', value)
|
|
||||||
.append('<span class="token-label" />')
|
.append('<span class="token-label" />')
|
||||||
.append('<a href="#" class="close" tabindex="-1">×</a>')
|
.append('<a href="#" class="close" tabindex="-1">×</a>')
|
||||||
|
.data('attrs', attrs)
|
||||||
|
|
||||||
// Insert token into HTML
|
// Insert token into HTML
|
||||||
if (this.$input.hasClass('tt-input')) {
|
if (this.$input.hasClass('tt-input')) {
|
||||||
this.$input.parent().before( token )
|
// If the input has typeahead enabled, insert token before it's parent
|
||||||
|
this.$input.parent().before( $token )
|
||||||
} else {
|
} else {
|
||||||
this.$input.before( token )
|
this.$input.before( $token )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Temporarily set input width to minimum
|
||||||
this.$input.css('width', this.options.minWidth + 'px')
|
this.$input.css('width', this.options.minWidth + 'px')
|
||||||
|
|
||||||
var tokenLabel = token.find('.token-label')
|
var $tokenLabel = $token.find('.token-label')
|
||||||
, closeButton = token.find('.close')
|
, $closeButton = $token.find('.close')
|
||||||
|
|
||||||
// Determine maximum possible token label width
|
// Determine maximum possible token label width
|
||||||
if (!this.maxTokenWidth) {
|
if (!this.maxTokenWidth) {
|
||||||
this.maxTokenWidth =
|
this.maxTokenWidth =
|
||||||
this.$wrapper.width() - closeButton.outerWidth() -
|
this.$wrapper.width() - $closeButton.outerWidth() -
|
||||||
parseInt(closeButton.css('margin-left'), 10) -
|
parseInt($closeButton.css('margin-left'), 10) -
|
||||||
parseInt(closeButton.css('margin-right'), 10) -
|
parseInt($closeButton.css('margin-right'), 10) -
|
||||||
parseInt(token.css('border-left-width'), 10) -
|
parseInt($token.css('border-left-width'), 10) -
|
||||||
parseInt(token.css('border-right-width'), 10) -
|
parseInt($token.css('border-right-width'), 10) -
|
||||||
parseInt(token.css('padding-left'), 10) -
|
parseInt($token.css('padding-left'), 10) -
|
||||||
parseInt(token.css('padding-right'), 10)
|
parseInt($token.css('padding-right'), 10)
|
||||||
parseInt(tokenLabel.css('border-left-width'), 10) -
|
parseInt($tokenLabel.css('border-left-width'), 10) -
|
||||||
parseInt(tokenLabel.css('border-right-width'), 10) -
|
parseInt($tokenLabel.css('border-right-width'), 10) -
|
||||||
parseInt(tokenLabel.css('padding-left'), 10) -
|
parseInt($tokenLabel.css('padding-left'), 10) -
|
||||||
parseInt(tokenLabel.css('padding-right'), 10)
|
parseInt($tokenLabel.css('padding-right'), 10)
|
||||||
parseInt(tokenLabel.css('margin-left'), 10) -
|
parseInt($tokenLabel.css('margin-left'), 10) -
|
||||||
parseInt(tokenLabel.css('margin-right'), 10)
|
parseInt($tokenLabel.css('margin-right'), 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenLabel
|
$tokenLabel.css('max-width', this.maxTokenWidth)
|
||||||
.text(label)
|
if (this.options.html)
|
||||||
.css('max-width', this.maxTokenWidth)
|
$tokenLabel.html(attrs.label)
|
||||||
|
else
|
||||||
|
$tokenLabel.text(attrs.label)
|
||||||
|
|
||||||
// Listen to events
|
// Listen to events on token
|
||||||
token
|
$token
|
||||||
.on('mousedown', function (e) {
|
.on('mousedown', function (e) {
|
||||||
if (_self.disabled) return false;
|
if (_self._disabled || _self._readonly) return false
|
||||||
_self.preventDeactivation = true
|
_self.preventDeactivation = true
|
||||||
})
|
})
|
||||||
.on('click', function (e) {
|
.on('click', function (e) {
|
||||||
if (_self.disabled) return false;
|
if (_self._disabled || _self._readonly) return false
|
||||||
_self.preventDeactivation = false
|
_self.preventDeactivation = false
|
||||||
|
|
||||||
if (e.ctrlKey || e.metaKey) {
|
if (e.ctrlKey || e.metaKey) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
return _self.toggle( token )
|
return _self.toggle( $token )
|
||||||
}
|
}
|
||||||
|
|
||||||
_self.activate( token, e.shiftKey, e.shiftKey )
|
_self.activate( $token, e.shiftKey, e.shiftKey )
|
||||||
})
|
})
|
||||||
.on('dblclick', function (e) {
|
.on('dblclick', function (e) {
|
||||||
if (_self.disabled || !_self.options.allowEditing ) return false;
|
if (_self._disabled || _self._readonly || !_self.options.allowEditing ) return false
|
||||||
_self.edit( token )
|
_self.edit( $token )
|
||||||
})
|
})
|
||||||
|
|
||||||
closeButton
|
$closeButton
|
||||||
.on('click', $.proxy(this.remove, this))
|
.on('click', $.proxy(this.remove, this))
|
||||||
|
|
||||||
var createEvent = $.Event('tokenfield:createtoken')
|
// Trigger createdtoken event on the original field
|
||||||
createEvent.token = prepareEvent.token
|
// indicating that the token is now in the DOM
|
||||||
createEvent.relatedTarget = token.get(0)
|
this.$element.trigger($.Event('tokenfield:createdtoken', {
|
||||||
this.$element.trigger( createEvent )
|
attrs: attrs,
|
||||||
|
relatedTarget: $token.get(0)
|
||||||
|
}))
|
||||||
|
|
||||||
var changeEvent = $.Event('change')
|
// Trigger change event on the original field
|
||||||
changeEvent.initiator = 'tokenfield'
|
|
||||||
if (triggerChange) {
|
if (triggerChange) {
|
||||||
this.$element.val( this.getTokensList() ).trigger( changeEvent )
|
this.$element.val( this.getTokensList() ).trigger( $.Event('change', { initiator: 'tokenfield' }) )
|
||||||
}
|
}
|
||||||
this.update()
|
|
||||||
|
|
||||||
return this.$input.get(0)
|
// Update tokenfield dimensions
|
||||||
}
|
var _self = this
|
||||||
|
setTimeout(function () {
|
||||||
|
_self.update()
|
||||||
|
}, 0)
|
||||||
|
|
||||||
|
// Return original element
|
||||||
|
return this.$element.get(0)
|
||||||
|
}
|
||||||
|
|
||||||
, setTokens: function (tokens, add, triggerChange) {
|
, setTokens: function (tokens, add, triggerChange) {
|
||||||
if (!tokens) return
|
|
||||||
|
|
||||||
if (!add) this.$wrapper.find('.token').remove()
|
if (!add) this.$wrapper.find('.token').remove()
|
||||||
|
|
||||||
|
if (!tokens) return
|
||||||
|
|
||||||
if (typeof triggerChange === 'undefined') {
|
if (typeof triggerChange === 'undefined') {
|
||||||
triggerChange = true
|
triggerChange = true
|
||||||
}
|
}
|
||||||
|
@ -341,20 +340,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
var _self = this
|
var _self = this
|
||||||
$.each(tokens, function (i, token) {
|
$.each(tokens, function (i, attrs) {
|
||||||
_self.createToken(token, triggerChange)
|
_self.createToken(attrs, triggerChange)
|
||||||
})
|
})
|
||||||
|
|
||||||
return this.$element.get(0)
|
return this.$element.get(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
, getTokenData: function(token) {
|
, getTokenData: function($token) {
|
||||||
var data = token.map(function() {
|
var data = $token.map(function() {
|
||||||
var $token = $(this);
|
var $token = $(this);
|
||||||
return {
|
return $token.data('attrs')
|
||||||
value: $token.attr('data-value'),
|
|
||||||
label: $token.find('.token-label').text()
|
|
||||||
}
|
|
||||||
}).get();
|
}).get();
|
||||||
|
|
||||||
if (data.length == 1) {
|
if (data.length == 1) {
|
||||||
|
@ -377,7 +373,7 @@
|
||||||
, getTokensList: function(delimiter, beautify, active) {
|
, getTokensList: function(delimiter, beautify, active) {
|
||||||
delimiter = delimiter || this._firstDelimiter
|
delimiter = delimiter || this._firstDelimiter
|
||||||
beautify = ( typeof beautify !== 'undefined' && beautify !== null ) ? beautify : this.options.beautify
|
beautify = ( typeof beautify !== 'undefined' && beautify !== null ) ? beautify : this.options.beautify
|
||||||
|
|
||||||
var separator = delimiter + ( beautify && delimiter !== ' ' ? ' ' : '')
|
var separator = delimiter + ( beautify && delimiter !== ' ' ? ' ' : '')
|
||||||
return $.map( this.getTokens(active), function (token) {
|
return $.map( this.getTokens(active), function (token) {
|
||||||
return token.value
|
return token.value
|
||||||
|
@ -387,6 +383,16 @@
|
||||||
, getInput: function() {
|
, getInput: function() {
|
||||||
return this.$input.val()
|
return this.$input.val()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
, setInput: function (val) {
|
||||||
|
if (this.$input.hasClass('tt-input')) {
|
||||||
|
// Typeahead acts weird when simply setting input value to empty,
|
||||||
|
// so we set the query to empty instead
|
||||||
|
this.$input.typeahead('val', val)
|
||||||
|
} else {
|
||||||
|
this.$input.val(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
, listen: function () {
|
, listen: function () {
|
||||||
var _self = this
|
var _self = this
|
||||||
|
@ -407,7 +413,7 @@
|
||||||
|
|
||||||
this.$copyHelper
|
this.$copyHelper
|
||||||
.on('focus', $.proxy(this.focus, this))
|
.on('focus', $.proxy(this.focus, this))
|
||||||
.on('blur', $.proxy(this.blur, this))
|
.on('blur', $.proxy(this.blur, this))
|
||||||
.on('keydown', $.proxy(this.keydown, this))
|
.on('keydown', $.proxy(this.keydown, this))
|
||||||
.on('keyup', $.proxy(this.keyup, this))
|
.on('keyup', $.proxy(this.keyup, this))
|
||||||
|
|
||||||
|
@ -420,7 +426,7 @@
|
||||||
.on('autocompletecreate', function() {
|
.on('autocompletecreate', function() {
|
||||||
// Set minimum autocomplete menu width
|
// Set minimum autocomplete menu width
|
||||||
var $_menuElement = $(this).data('ui-autocomplete').menu.element
|
var $_menuElement = $(this).data('ui-autocomplete').menu.element
|
||||||
|
|
||||||
var minWidth = _self.$wrapper.outerWidth() -
|
var minWidth = _self.$wrapper.outerWidth() -
|
||||||
parseInt( $_menuElement.css('border-left-width'), 10 ) -
|
parseInt( $_menuElement.css('border-left-width'), 10 ) -
|
||||||
parseInt( $_menuElement.css('border-right-width'), 10 )
|
parseInt( $_menuElement.css('border-right-width'), 10 )
|
||||||
|
@ -436,7 +442,7 @@
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
.on('typeahead:selected', function (e, datum, dataset) {
|
.on('typeahead:selected typeahead:autocompleted', function (e, datum, dataset) {
|
||||||
// Create token
|
// Create token
|
||||||
if (_self.createToken( datum )) {
|
if (_self.createToken( datum )) {
|
||||||
_self.$input.typeahead('val', '')
|
_self.$input.typeahead('val', '')
|
||||||
|
@ -445,13 +451,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on('typeahead:autocompleted', function (e, datum, dataset) {
|
|
||||||
_self.createToken( _self.$input.val() )
|
|
||||||
_self.$input.typeahead('val', '')
|
|
||||||
if (_self.$input.data( 'edit' )) {
|
|
||||||
_self.unedit(true)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Listen to window resize
|
// Listen to window resize
|
||||||
$(window).on('resize', $.proxy(this.update, this ))
|
$(window).on('resize', $.proxy(this.update, this ))
|
||||||
|
@ -484,7 +483,7 @@
|
||||||
|
|
||||||
case 40: // down arrow
|
case 40: // down arrow
|
||||||
upDown('next')
|
upDown('next')
|
||||||
break
|
break
|
||||||
|
|
||||||
case 65: // a (to handle ctrl + a)
|
case 65: // a (to handle ctrl + a)
|
||||||
if (this.$input.val().length > 0 || !(e.ctrlKey || e.metaKey)) break
|
if (this.$input.val().length > 0 || !(e.ctrlKey || e.metaKey)) break
|
||||||
|
@ -493,15 +492,15 @@
|
||||||
break
|
break
|
||||||
|
|
||||||
case 9: // tab
|
case 9: // tab
|
||||||
case 13: // enter
|
case 13: // enter
|
||||||
|
|
||||||
// We will handle creating tokens from autocomplete in autocomplete events
|
// We will handle creating tokens from autocomplete in autocomplete events
|
||||||
if (this.$input.data('ui-autocomplete') && this.$input.data('ui-autocomplete').menu.element.find("li:has(a.ui-state-focus)").length) break
|
if (this.$input.data('ui-autocomplete') && this.$input.data('ui-autocomplete').menu.element.find("li:has(a.ui-state-focus), li.ui-state-focus").length) break
|
||||||
|
|
||||||
// We will handle creating tokens from typeahead in typeahead events
|
// We will handle creating tokens from typeahead in typeahead events
|
||||||
if (this.$input.hasClass('tt-input') && this.$wrapper.find('.tt-cursor').length ) break
|
if (this.$input.hasClass('tt-input') && this.$wrapper.find('.tt-cursor').length ) break
|
||||||
if (this.$input.hasClass('tt-input') && this.$wrapper.find('.tt-hint').val().length) break
|
if (this.$input.hasClass('tt-input') && this.$wrapper.find('.tt-hint').val() && this.$wrapper.find('.tt-hint').val().length) break
|
||||||
|
|
||||||
// Create token
|
// Create token
|
||||||
if (this.$input.is(document.activeElement) && this.$input.val().length || this.$input.data('edit')) {
|
if (this.$input.is(document.activeElement) && this.$input.val().length || this.$input.data('edit')) {
|
||||||
return this.createTokensFromInput(e, this.$input.data('edit'));
|
return this.createTokensFromInput(e, this.$input.data('edit'));
|
||||||
|
@ -520,13 +519,13 @@
|
||||||
if (_self.$input.val().length > 0) return
|
if (_self.$input.val().length > 0) return
|
||||||
|
|
||||||
direction += 'All'
|
direction += 'All'
|
||||||
var token = _self.$input.hasClass('tt-input') ? _self.$input.parent()[direction]('.token:first') : _self.$input[direction]('.token:first')
|
var $token = _self.$input.hasClass('tt-input') ? _self.$input.parent()[direction]('.token:first') : _self.$input[direction]('.token:first')
|
||||||
if (!token.length) return
|
if (!$token.length) return
|
||||||
|
|
||||||
_self.preventInputFocus = true
|
_self.preventInputFocus = true
|
||||||
_self.preventDeactivation = true
|
_self.preventDeactivation = true
|
||||||
|
|
||||||
_self.activate( token )
|
_self.activate( $token )
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -541,16 +540,16 @@
|
||||||
if (_self.$input.is(document.activeElement)) {
|
if (_self.$input.is(document.activeElement)) {
|
||||||
if (_self.$input.val().length > 0) return
|
if (_self.$input.val().length > 0) return
|
||||||
|
|
||||||
var token = _self.$input.hasClass('tt-input') ? _self.$input.parent()[direction + 'All']('.token:first') : _self.$input[direction + 'All']('.token:first')
|
var $token = _self.$input.hasClass('tt-input') ? _self.$input.parent()[direction + 'All']('.token:first') : _self.$input[direction + 'All']('.token:first')
|
||||||
if (!token.length) return
|
if (!$token.length) return
|
||||||
|
|
||||||
_self.activate( token )
|
_self.activate( $token )
|
||||||
}
|
}
|
||||||
|
|
||||||
var opposite = direction === 'prev' ? 'next' : 'prev'
|
var opposite = direction === 'prev' ? 'next' : 'prev'
|
||||||
, position = direction === 'prev' ? 'first' : 'last'
|
, position = direction === 'prev' ? 'first' : 'last'
|
||||||
|
|
||||||
_self.firstActiveToken[opposite + 'All']('.token').each(function() {
|
_self.$firstActiveToken[opposite + 'All']('.token').each(function() {
|
||||||
_self.deactivate( $(this) )
|
_self.deactivate( $(this) )
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -562,11 +561,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
, keypress: function(e) {
|
, keypress: function(e) {
|
||||||
this.lastKeyPressCode = e.keyCode
|
|
||||||
this.lastKeyPressCharCode = e.charCode
|
|
||||||
|
|
||||||
// Comma
|
// Comma
|
||||||
if ($.inArray( e.charCode, this._triggerKeys) !== -1 && this.$input.is(document.activeElement)) {
|
if ($.inArray( e.which, this._triggerKeys) !== -1 && this.$input.is(document.activeElement)) {
|
||||||
if (this.$input.val()) {
|
if (this.$input.val()) {
|
||||||
this.createTokensFromInput(e)
|
this.createTokensFromInput(e)
|
||||||
}
|
}
|
||||||
|
@ -583,13 +580,13 @@
|
||||||
case 8: // backspace
|
case 8: // backspace
|
||||||
if (this.$input.is(document.activeElement)) {
|
if (this.$input.is(document.activeElement)) {
|
||||||
if (this.$input.val().length || this.lastInputValue.length && this.lastKeyDown === 8) break
|
if (this.$input.val().length || this.lastInputValue.length && this.lastKeyDown === 8) break
|
||||||
|
|
||||||
this.preventDeactivation = true
|
this.preventDeactivation = true
|
||||||
var prev = this.$input.hasClass('tt-input') ? this.$input.parent().prevAll('.token:first') : this.$input.prevAll('.token:first')
|
var $prevToken = this.$input.hasClass('tt-input') ? this.$input.parent().prevAll('.token:first') : this.$input.prevAll('.token:first')
|
||||||
|
|
||||||
if (!prev.length) break
|
if (!$prevToken.length) break
|
||||||
|
|
||||||
this.activate( prev )
|
this.activate( $prevToken )
|
||||||
} else {
|
} else {
|
||||||
this.remove(e)
|
this.remove(e)
|
||||||
}
|
}
|
||||||
|
@ -608,7 +605,7 @@
|
||||||
|
|
||||||
if (this.$input.is(document.activeElement)) {
|
if (this.$input.is(document.activeElement)) {
|
||||||
this.$wrapper.find('.active').removeClass('active')
|
this.$wrapper.find('.active').removeClass('active')
|
||||||
this.firstActiveToken = null
|
this.$firstActiveToken = null
|
||||||
|
|
||||||
if (this.options.showAutocompleteOnFocus) {
|
if (this.options.showAutocompleteOnFocus) {
|
||||||
this.search()
|
this.search()
|
||||||
|
@ -623,29 +620,31 @@
|
||||||
|
|
||||||
if (!this.preventDeactivation && !this.$element.is(document.activeElement)) {
|
if (!this.preventDeactivation && !this.$element.is(document.activeElement)) {
|
||||||
this.$wrapper.find('.active').removeClass('active')
|
this.$wrapper.find('.active').removeClass('active')
|
||||||
this.firstActiveToken = null
|
this.$firstActiveToken = null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.preventCreateTokens && (this.$input.data('edit') && !this.$input.is(document.activeElement) || this.options.createTokensOnBlur )) {
|
if (!this.preventCreateTokens && (this.$input.data('edit') && !this.$input.is(document.activeElement) || this.options.createTokensOnBlur )) {
|
||||||
this.createTokensFromInput(e)
|
this.createTokensFromInput(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.preventDeactivation = false
|
this.preventDeactivation = false
|
||||||
this.preventCreateTokens = false
|
this.preventCreateTokens = false
|
||||||
}
|
}
|
||||||
|
|
||||||
, paste: function (e) {
|
, paste: function (e) {
|
||||||
var _self = this
|
var _self = this
|
||||||
|
|
||||||
// Add tokens to existing ones
|
// Add tokens to existing ones
|
||||||
setTimeout(function () {
|
if (_self.options.allowPasting) {
|
||||||
_self.createTokensFromInput(e)
|
setTimeout(function () {
|
||||||
}, 1)
|
_self.createTokensFromInput(e)
|
||||||
|
}, 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
, change: function (e) {
|
, change: function (e) {
|
||||||
if ( e.initiator === 'tokenfield' ) return // Prevent loops
|
if ( e.initiator === 'tokenfield' ) return // Prevent loops
|
||||||
|
|
||||||
this.setTokens( this.$element.val() )
|
this.setTokens( this.$element.val() )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -655,71 +654,65 @@
|
||||||
|
|
||||||
var tokensBefore = this.getTokensList()
|
var tokensBefore = this.getTokensList()
|
||||||
this.setTokens( this.$input.val(), true )
|
this.setTokens( this.$input.val(), true )
|
||||||
|
|
||||||
if (tokensBefore == this.getTokensList() && this.$input.val().length)
|
if (tokensBefore == this.getTokensList() && this.$input.val().length)
|
||||||
return false // No tokens were added, do nothing (prevent form submit)
|
return false // No tokens were added, do nothing (prevent form submit)
|
||||||
|
|
||||||
if (this.$input.hasClass('tt-input')) {
|
this.setInput('')
|
||||||
// Typeahead acts weird when simply setting input value to empty,
|
|
||||||
// so we set the query to empty instead
|
|
||||||
this.$input.typeahead('val', '')
|
|
||||||
} else {
|
|
||||||
this.$input.val('')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.$input.data( 'edit' )) {
|
if (this.$input.data( 'edit' )) {
|
||||||
this.unedit(focus)
|
this.unedit(focus)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false // Prevent form being submitted
|
return false // Prevent form being submitted
|
||||||
}
|
}
|
||||||
|
|
||||||
, next: function (add) {
|
, next: function (add) {
|
||||||
if (add) {
|
if (add) {
|
||||||
var firstActive = this.$wrapper.find('.active:first')
|
var $firstActiveToken = this.$wrapper.find('.active:first')
|
||||||
, deactivate = firstActive && this.firstActiveToken ? firstActive.index() < this.firstActiveToken.index() : false
|
, deactivate = $firstActiveToken && this.$firstActiveToken ? $firstActiveToken.index() < this.$firstActiveToken.index() : false
|
||||||
|
|
||||||
if (deactivate) return this.deactivate( firstActive )
|
if (deactivate) return this.deactivate( $firstActiveToken )
|
||||||
}
|
}
|
||||||
|
|
||||||
var active = this.$wrapper.find('.active:last')
|
var $lastActiveToken = this.$wrapper.find('.active:last')
|
||||||
, next = active.nextAll('.token:first')
|
, $nextToken = $lastActiveToken.nextAll('.token:first')
|
||||||
|
|
||||||
if (!next.length) {
|
if (!$nextToken.length) {
|
||||||
this.$input.focus()
|
this.$input.focus()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.activate(next, add)
|
this.activate($nextToken, add)
|
||||||
}
|
}
|
||||||
|
|
||||||
, prev: function (add) {
|
, prev: function (add) {
|
||||||
|
|
||||||
if (add) {
|
if (add) {
|
||||||
var lastActive = this.$wrapper.find('.active:last')
|
var $lastActiveToken = this.$wrapper.find('.active:last')
|
||||||
, deactivate = lastActive && this.firstActiveToken ? lastActive.index() > this.firstActiveToken.index() : false
|
, deactivate = $lastActiveToken && this.$firstActiveToken ? $lastActiveToken.index() > this.$firstActiveToken.index() : false
|
||||||
|
|
||||||
if (deactivate) return this.deactivate( lastActive )
|
if (deactivate) return this.deactivate( $lastActiveToken )
|
||||||
}
|
}
|
||||||
|
|
||||||
var active = this.$wrapper.find('.active:first')
|
var $firstActiveToken = this.$wrapper.find('.active:first')
|
||||||
, prev = active.prevAll('.token:first')
|
, $prevToken = $firstActiveToken.prevAll('.token:first')
|
||||||
|
|
||||||
if (!prev.length) {
|
if (!$prevToken.length) {
|
||||||
prev = this.$wrapper.find('.token:first')
|
$prevToken = this.$wrapper.find('.token:first')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!prev.length && !add) {
|
if (!$prevToken.length && !add) {
|
||||||
this.$input.focus()
|
this.$input.focus()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.activate( prev, add )
|
this.activate( $prevToken, add )
|
||||||
}
|
}
|
||||||
|
|
||||||
, activate: function (token, add, multi, remember) {
|
, activate: function ($token, add, multi, remember) {
|
||||||
|
|
||||||
if (!token) return
|
if (!$token) return
|
||||||
|
|
||||||
if (typeof remember === 'undefined') var remember = true
|
if (typeof remember === 'undefined') var remember = true
|
||||||
|
|
||||||
|
@ -730,17 +723,17 @@
|
||||||
if (!add) {
|
if (!add) {
|
||||||
this.$wrapper.find('.active').removeClass('active')
|
this.$wrapper.find('.active').removeClass('active')
|
||||||
if (remember) {
|
if (remember) {
|
||||||
this.firstActiveToken = token
|
this.$firstActiveToken = $token
|
||||||
} else {
|
} else {
|
||||||
delete this.firstActiveToken
|
delete this.$firstActiveToken
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (multi && this.firstActiveToken) {
|
if (multi && this.$firstActiveToken) {
|
||||||
// Determine first active token and the current tokens indicies
|
// Determine first active token and the current tokens indicies
|
||||||
// Account for the 1 hidden textarea by subtracting 1 from both
|
// Account for the 1 hidden textarea by subtracting 1 from both
|
||||||
var i = this.firstActiveToken.index() - 2
|
var i = this.$firstActiveToken.index() - 2
|
||||||
, a = token.index() - 2
|
, a = $token.index() - 2
|
||||||
, _self = this
|
, _self = this
|
||||||
|
|
||||||
this.$wrapper.find('.token').slice( Math.min(i, a) + 1, Math.max(i, a) ).each( function() {
|
this.$wrapper.find('.token').slice( Math.min(i, a) + 1, Math.max(i, a) ).each( function() {
|
||||||
|
@ -748,7 +741,7 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
token.addClass('active')
|
$token.addClass('active')
|
||||||
this.$copyHelper.val( this.getTokensList( null, null, true ) ).select()
|
this.$copyHelper.val( this.getTokensList( null, null, true ) ).select()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -760,67 +753,63 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
, deactivate: function(token) {
|
, deactivate: function($token) {
|
||||||
if (!token) return
|
if (!$token) return
|
||||||
|
|
||||||
token.removeClass('active')
|
$token.removeClass('active')
|
||||||
this.$copyHelper.val( this.getTokensList( null, null, true ) ).select()
|
this.$copyHelper.val( this.getTokensList( null, null, true ) ).select()
|
||||||
}
|
}
|
||||||
|
|
||||||
, toggle: function(token) {
|
, toggle: function($token) {
|
||||||
if (!token) return
|
if (!$token) return
|
||||||
|
|
||||||
token.toggleClass('active')
|
$token.toggleClass('active')
|
||||||
this.$copyHelper.val( this.getTokensList( null, null, true ) ).select()
|
this.$copyHelper.val( this.getTokensList( null, null, true ) ).select()
|
||||||
}
|
}
|
||||||
|
|
||||||
, edit: function (token) {
|
, edit: function ($token) {
|
||||||
if (!token) return
|
if (!$token) return
|
||||||
|
|
||||||
var value = token.data('value')
|
var attrs = $token.data('attrs')
|
||||||
, label = token.find('.token-label').text()
|
|
||||||
|
|
||||||
// Allow changing input value before editing
|
// Allow changing input value before editing
|
||||||
var editEvent = $.Event('tokenfield:edittoken')
|
var options = { attrs: attrs, relatedTarget: $token.get(0) }
|
||||||
editEvent.token = {
|
var editEvent = $.Event('tokenfield:edittoken', options)
|
||||||
value: value,
|
|
||||||
label: label
|
|
||||||
}
|
|
||||||
editEvent.relatedTarget = token.get(0)
|
|
||||||
this.$element.trigger( editEvent )
|
this.$element.trigger( editEvent )
|
||||||
|
|
||||||
if (!editEvent.token) return
|
|
||||||
|
|
||||||
value = editEvent.token.value
|
// Edit event can be cancelled if default is prevented
|
||||||
label = editEvent.token.label
|
if (editEvent.isDefaultPrevented()) return
|
||||||
|
|
||||||
token.find('.token-label').text(value)
|
$token.find('.token-label').text(attrs.value)
|
||||||
var tokenWidth = token.outerWidth()
|
var tokenWidth = $token.outerWidth()
|
||||||
|
|
||||||
var $_input = this.$input.hasClass('tt-input') ? this.$input.parent() : this.$input
|
var $_input = this.$input.hasClass('tt-input') ? this.$input.parent() : this.$input
|
||||||
|
|
||||||
token.replaceWith( $_input )
|
$token.replaceWith( $_input )
|
||||||
|
|
||||||
this.preventCreateTokens = true
|
this.preventCreateTokens = true
|
||||||
|
|
||||||
this.$input.val( value )
|
this.$input.val( attrs.value )
|
||||||
.select()
|
.select()
|
||||||
.data( 'edit', true )
|
.data( 'edit', true )
|
||||||
.width( tokenWidth )
|
.width( tokenWidth )
|
||||||
|
|
||||||
this.update();
|
this.update();
|
||||||
|
|
||||||
|
// Indicate that token is now being edited, and is replaced with an input field in the DOM
|
||||||
|
this.$element.trigger($.Event('tokenfield:editedtoken', options ))
|
||||||
}
|
}
|
||||||
|
|
||||||
, unedit: function (focus) {
|
, unedit: function (focus) {
|
||||||
var $_input = this.$input.hasClass('tt-input') ? this.$input.parent() : this.$input
|
var $_input = this.$input.hasClass('tt-input') ? this.$input.parent() : this.$input
|
||||||
$_input.appendTo( this.$wrapper )
|
$_input.appendTo( this.$wrapper )
|
||||||
|
|
||||||
this.$input.data('edit', false)
|
this.$input.data('edit', false)
|
||||||
this.$mirror.text('')
|
this.$mirror.text('')
|
||||||
|
|
||||||
this.update()
|
this.update()
|
||||||
|
|
||||||
// Because moving the input element around in DOM
|
// Because moving the input element around in DOM
|
||||||
// will cause it to lose focus, we provide an option
|
// will cause it to lose focus, we provide an option
|
||||||
// to re-focus the input after appending it to the wrapper
|
// to re-focus the input after appending it to the wrapper
|
||||||
if (focus) {
|
if (focus) {
|
||||||
|
@ -832,31 +821,35 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
, remove: function (e, direction) {
|
, remove: function (e, direction) {
|
||||||
if (this.$input.is(document.activeElement) || this.disabled) return
|
if (this.$input.is(document.activeElement) || this._disabled || this._readonly) return
|
||||||
|
|
||||||
|
var $token = (e.type === 'click') ? $(e.target).closest('.token') : this.$wrapper.find('.token.active')
|
||||||
|
|
||||||
var token = (e.type === 'click') ? $(e.target).closest('.token') : this.$wrapper.find('.token.active')
|
|
||||||
|
|
||||||
if (e.type !== 'click') {
|
if (e.type !== 'click') {
|
||||||
if (!direction) var direction = 'prev'
|
if (!direction) var direction = 'prev'
|
||||||
this[direction]()
|
this[direction]()
|
||||||
|
|
||||||
// Was this the first token?
|
// Was it the first token?
|
||||||
if (direction === 'prev') var firstToken = token.first().prevAll('.token:first').length === 0
|
if (direction === 'prev') var firstToken = $token.first().prevAll('.token:first').length === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare events
|
// Prepare events and their options
|
||||||
|
var options = { attrs: this.getTokenData( $token ), relatedTarget: $token.get(0) }
|
||||||
|
, removeEvent = $.Event('tokenfield:removetoken', options)
|
||||||
|
|
||||||
var removeEvent = $.Event('tokenfield:removetoken')
|
this.$element.trigger(removeEvent);
|
||||||
removeEvent.token = this.getTokenData( token )
|
|
||||||
|
|
||||||
var changeEvent = $.Event('change')
|
// Remove event can be intercepted and cancelled
|
||||||
changeEvent.initiator = 'tokenfield'
|
if (removeEvent.isDefaultPrevented()) return
|
||||||
|
|
||||||
|
var removedEvent = $.Event('tokenfield:removedtoken', options)
|
||||||
|
, changeEvent = $.Event('change', { initiator: 'tokenfield' })
|
||||||
|
|
||||||
// Remove token from DOM
|
// Remove token from DOM
|
||||||
token.remove()
|
$token.remove()
|
||||||
|
|
||||||
// Trigger events
|
// Trigger events
|
||||||
this.$element.val( this.getTokensList() ).trigger( removeEvent ).trigger( changeEvent )
|
this.$element.val( this.getTokensList() ).trigger( removedEvent ).trigger( changeEvent )
|
||||||
|
|
||||||
// Focus, when necessary:
|
// Focus, when necessary:
|
||||||
// When there are no more tokens, or if this was the first token
|
// When there are no more tokens, or if this was the first token
|
||||||
|
@ -867,15 +860,19 @@
|
||||||
this.$input.css('width', this.options.minWidth + 'px')
|
this.$input.css('width', this.options.minWidth + 'px')
|
||||||
this.update()
|
this.update()
|
||||||
|
|
||||||
|
// Cancel original event handlers
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update tokenfield dimensions
|
||||||
|
*/
|
||||||
, update: function (e) {
|
, update: function (e) {
|
||||||
var value = this.$input.val()
|
var value = this.$input.val()
|
||||||
, inputLeftPadding = parseInt(this.$input.css('padding-left'), 10)
|
, inputPaddingLeft = parseInt(this.$input.css('padding-left'), 10)
|
||||||
, inputRightPadding = parseInt(this.$input.css('padding-right'), 10)
|
, inputPaddingRight = parseInt(this.$input.css('padding-right'), 10)
|
||||||
, inputPadding = inputLeftPadding + inputRightPadding
|
, inputPadding = inputPaddingLeft + inputPaddingRight
|
||||||
|
|
||||||
if (this.$input.data('edit')) {
|
if (this.$input.data('edit')) {
|
||||||
|
|
||||||
|
@ -885,7 +882,7 @@
|
||||||
if (value === this.$mirror.text()) return
|
if (value === this.$mirror.text()) return
|
||||||
|
|
||||||
this.$mirror.text(value)
|
this.$mirror.text(value)
|
||||||
|
|
||||||
var mirrorWidth = this.$mirror.width() + 10;
|
var mirrorWidth = this.$mirror.width() + 10;
|
||||||
if ( mirrorWidth > this.$wrapper.width() ) {
|
if ( mirrorWidth > this.$wrapper.width() ) {
|
||||||
return this.$input.width( this.$wrapper.width() )
|
return this.$input.width( this.$wrapper.width() )
|
||||||
|
@ -894,16 +891,22 @@
|
||||||
this.$input.width( mirrorWidth )
|
this.$input.width( mirrorWidth )
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.$input.css( 'width', this.options.minWidth + 'px' )
|
//temporary reset width to minimal value to get proper results
|
||||||
if (this.textDirection === 'rtl') {
|
this.$input.width(this.options.minWidth);
|
||||||
return this.$input.width( this.$input.offset().left + this.$input.outerWidth() - this.$wrapper.offset().left - parseInt(this.$wrapper.css('padding-left'), 10) - inputPadding - 1 )
|
|
||||||
}
|
var w = (this.textDirection === 'rtl')
|
||||||
this.$input.width( this.$wrapper.offset().left + this.$wrapper.width() + parseInt(this.$wrapper.css('padding-left'), 10) - this.$input.offset().left - inputPadding )
|
? this.$input.offset().left + this.$input.outerWidth() - this.$wrapper.offset().left - parseInt(this.$wrapper.css('padding-left'), 10) - inputPadding - 1
|
||||||
|
: this.$wrapper.offset().left + this.$wrapper.width() + parseInt(this.$wrapper.css('padding-left'), 10) - this.$input.offset().left - inputPadding;
|
||||||
|
//
|
||||||
|
// some usecases pre-render widget before attaching to DOM,
|
||||||
|
// dimensions returned by jquery will be NaN -> we default to 100%
|
||||||
|
// so placeholder won't be cut off.
|
||||||
|
isNaN(w) ? this.$input.width('100%') : this.$input.width(w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
, focusInput: function (e) {
|
, focusInput: function (e) {
|
||||||
if ($(e.target).closest('.token').length || $(e.target).closest('.token-input').length) return
|
if ( $(e.target).closest('.token').length || $(e.target).closest('.token-input').length || $(e.target).closest('.tt-dropdown-menu').length ) return
|
||||||
// Focus only after the current call stack has cleared,
|
// Focus only after the current call stack has cleared,
|
||||||
// otherwise has no effect.
|
// otherwise has no effect.
|
||||||
// Reason: mousedown is too early - input will lose focus
|
// Reason: mousedown is too early - input will lose focus
|
||||||
|
@ -922,27 +925,36 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
, disable: function () {
|
, disable: function () {
|
||||||
this.disabled = true;
|
this.setProperty('disabled', true);
|
||||||
this.$input.prop('disabled', true);
|
|
||||||
this.$element.prop('disabled', true);
|
|
||||||
this.$wrapper.addClass('disabled');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
, enable: function () {
|
, enable: function () {
|
||||||
this.disabled = false;
|
this.setProperty('disabled', false);
|
||||||
this.$input.prop('disabled', false);
|
|
||||||
this.$element.prop('disabled', false);
|
|
||||||
this.$wrapper.removeClass('disabled');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
, readonly: function () {
|
||||||
|
this.setProperty('readonly', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
, writeable: function () {
|
||||||
|
this.setProperty('readonly', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
, setProperty: function(property, value) {
|
||||||
|
this['_' + property] = value;
|
||||||
|
this.$input.prop(property, value);
|
||||||
|
this.$element.prop(property, value);
|
||||||
|
this.$wrapper[ value ? 'addClass' : 'removeClass' ](property);
|
||||||
|
}
|
||||||
|
|
||||||
, destroy: function() {
|
, destroy: function() {
|
||||||
// Set field value
|
// Set field value
|
||||||
this.$element.val( this.getTokensList() );
|
this.$element.val( this.getTokensList() );
|
||||||
// Restore styles and properties
|
// Restore styles and properties
|
||||||
this.$element.css( this.$element.data('original-styles') );
|
this.$element.css( this.$element.data('original-styles') );
|
||||||
this.$element.prop( 'tabindex', this.$element.data('original-tabindex') );
|
this.$element.prop( 'tabindex', this.$element.data('original-tabindex') );
|
||||||
|
|
||||||
// Re-route tokenfield labele to original input
|
// Re-route tokenfield label to original input
|
||||||
var $label = $( 'label[for="' + this.$input.prop('id') + '"]' )
|
var $label = $( 'label[for="' + this.$input.prop('id') + '"]' )
|
||||||
if ( $label.length ) {
|
if ( $label.length ) {
|
||||||
$label.prop( 'for', this.$element.prop('id') )
|
$label.prop( 'for', this.$element.prop('id') )
|
||||||
|
@ -952,15 +964,15 @@
|
||||||
this.$element.insertBefore( this.$wrapper );
|
this.$element.insertBefore( this.$wrapper );
|
||||||
|
|
||||||
// Remove tokenfield-related data
|
// Remove tokenfield-related data
|
||||||
this.$element.removeData('original-styles');
|
this.$element.removeData('original-styles')
|
||||||
this.$element.removeData('original-tabindex');
|
.removeData('original-tabindex')
|
||||||
this.$element.removeData('bs.tokenfield');
|
.removeData('bs.tokenfield');
|
||||||
|
|
||||||
// Remove tokenfield from DOM
|
// Remove tokenfield from DOM
|
||||||
this.$wrapper.remove();
|
this.$wrapper.remove();
|
||||||
|
this.$mirror.remove();
|
||||||
|
|
||||||
var $_element = this.$element;
|
var $_element = this.$element;
|
||||||
delete this;
|
|
||||||
|
|
||||||
return $_element;
|
return $_element;
|
||||||
}
|
}
|
||||||
|
@ -976,7 +988,7 @@
|
||||||
$.fn.tokenfield = function (option, param) {
|
$.fn.tokenfield = function (option, param) {
|
||||||
var value
|
var value
|
||||||
, args = []
|
, args = []
|
||||||
|
|
||||||
Array.prototype.push.apply( args, arguments );
|
Array.prototype.push.apply( args, arguments );
|
||||||
|
|
||||||
var elements = this.each(function () {
|
var elements = this.each(function () {
|
||||||
|
@ -988,7 +1000,10 @@
|
||||||
args.shift()
|
args.shift()
|
||||||
value = data[option].apply(data, args)
|
value = data[option].apply(data, args)
|
||||||
} else {
|
} else {
|
||||||
if (!data && typeof option !== 'string' && !param) $this.data('bs.tokenfield', (data = new Tokenfield(this, options)))
|
if (!data && typeof option !== 'string' && !param) {
|
||||||
|
$this.data('bs.tokenfield', (data = new Tokenfield(this, options)))
|
||||||
|
$this.trigger('tokenfield:initialize')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -998,15 +1013,17 @@
|
||||||
$.fn.tokenfield.defaults = {
|
$.fn.tokenfield.defaults = {
|
||||||
minWidth: 60,
|
minWidth: 60,
|
||||||
minLength: 0,
|
minLength: 0,
|
||||||
allowDuplicates: false,
|
html: true,
|
||||||
allowEditing: true,
|
allowEditing: true,
|
||||||
|
allowPasting: true,
|
||||||
limit: 0,
|
limit: 0,
|
||||||
autocomplete: {},
|
autocomplete: {},
|
||||||
typeahead: {},
|
typeahead: {},
|
||||||
showAutocompleteOnFocus: false,
|
showAutocompleteOnFocus: false,
|
||||||
createTokensOnBlur: false,
|
createTokensOnBlur: false,
|
||||||
delimiter: ',',
|
delimiter: ',',
|
||||||
beautify: true
|
beautify: true,
|
||||||
|
inputType: 'text'
|
||||||
}
|
}
|
||||||
|
|
||||||
$.fn.tokenfield.Constructor = Tokenfield
|
$.fn.tokenfield.Constructor = Tokenfield
|
||||||
|
|
Loading…
Reference in a new issue