bootstrap_tokenfield_rails 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d6119607e3aa9f6a73b3e134b85f0e55edbbc817
4
- data.tar.gz: 9e71a84b6bc48258d8f6ef9c0da426276c313590
3
+ metadata.gz: a99164ca4c75845c6e28ebbf78eeade419aedefe
4
+ data.tar.gz: b72c6367bc12f6d3bd9539888c076df50b28f02f
5
5
  SHA512:
6
- metadata.gz: aefa95d223aa22025b5ea4d87aebb3192ce5975c057c17fffbdbb18ee611239f4f203ccdf588a0d238c4f5072a3beba4af091d123d664ebb5de4cb3608829db2
7
- data.tar.gz: 8a960a84a56ff32acb3b67b9cad4ef18544818aeb3801d80a165c47a3d1e00277cfcb24ebfa42056d649067a05347d122347409383376baddee358ba31324a2a
6
+ metadata.gz: 92a5cd5a835202b06661c05920d01fe4b2d6e7e315e379240abdfcba0e2bec2e705aa3544f7e86d209c8efcca0e05f54f6c6e4e40652d5412fe46a9f41121bba
7
+ data.tar.gz: af2ed1bfee6c48ce218239f58903e704f69e8849bbeee818b73f93786a67eaff786dab9c47965f64e2c851c7cdb6b3b96d6b6e0f28bc8fbced31d1c873fca147
@@ -1,3 +1,3 @@
1
1
  module BootstrapTokenfieldRails
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.7"
3
3
  end
@@ -25,7 +25,7 @@
25
25
  };
26
26
  } else {
27
27
  // Browser globals
28
- factory(jQuery);
28
+ factory(jQuery, window);
29
29
  }
30
30
  }(function ($, window) {
31
31
 
@@ -63,9 +63,9 @@
63
63
  }
64
64
 
65
65
  var specialCharacters = ['\\', '$', '[', '{', '^', '.', '|', '?', '*', '+', '(', ')']
66
- $.each(this._delimiters, function (index, char) {
67
- var pos = $.inArray(char, specialCharacters)
68
- if (pos >= 0) _self._delimiters[index] = '\\' + char;
66
+ $.each(this._delimiters, function (index, character) {
67
+ var pos = $.inArray(character, specialCharacters)
68
+ if (pos >= 0) _self._delimiters[index] = '\\' + character;
69
69
  });
70
70
 
71
71
  // Store original input width
@@ -102,7 +102,7 @@
102
102
 
103
103
  // Create a new input
104
104
  var id = this.$element.prop('id') || new Date().getTime() + '' + Math.floor((1 + Math.random()) * 100)
105
- this.$input = $('<input type="text" class="token-input" autocomplete="off" />')
105
+ this.$input = $('<input type="'+this.options.inputType+'" class="token-input" autocomplete="off" />')
106
106
  .appendTo( this.$wrapper )
107
107
  .prop( 'placeholder', this.$element.prop('placeholder') )
108
108
  .prop( 'id', id + '-tokenfield' )
@@ -134,6 +134,11 @@
134
134
  this.disable();
135
135
  }
136
136
 
137
+ // Set tokenfield readonly, if original input is readonly
138
+ if (this.$element.prop('readonly')) {
139
+ this.readonly();
140
+ }
141
+
137
142
  // Set up mirror for input auto-sizing
138
143
  this.$mirror = $('<span style="position:absolute; top:-999px; left:0; white-space:pre;"/>');
139
144
  this.$input.css('min-width', this.options.minWidth + 'px')
@@ -159,7 +164,7 @@
159
164
  this.update()
160
165
 
161
166
  // Create initial tokens, if any
162
- this.setTokens(this.options.tokens, false, false)
167
+ this.setTokens(this.options.tokens, false, ! this.$element.val() && this.options.tokens )
163
168
 
164
169
  // Start listening to events
165
170
  this.listen()
@@ -167,23 +172,28 @@
167
172
  // Initialize autocomplete, if necessary
168
173
  if ( ! $.isEmptyObject( this.options.autocomplete ) ) {
169
174
  var side = this.textDirection === 'rtl' ? 'right' : 'left'
170
- var autocompleteOptions = $.extend({
171
- minLength: this.options.showAutocompleteOnFocus ? 0 : null,
172
- position: { my: side + " top", at: side + " bottom", of: this.$wrapper }
173
- }, this.options.autocomplete )
175
+ , autocompleteOptions = $.extend({
176
+ minLength: this.options.showAutocompleteOnFocus ? 0 : null,
177
+ position: { my: side + " top", at: side + " bottom", of: this.$wrapper }
178
+ }, this.options.autocomplete )
179
+
174
180
  this.$input.autocomplete( autocompleteOptions )
175
181
  }
176
182
 
177
183
  // Initialize typeahead, if necessary
178
184
  if ( ! $.isEmptyObject( this.options.typeahead ) ) {
179
- var typeaheadOptions = $.extend({
180
- minLength: this.options.showAutocompleteOnFocus ? 0 : null
181
- }, this.options.typeahead)
182
- this.$input.typeahead( null, typeaheadOptions )
185
+
186
+ var typeaheadOptions = this.options.typeahead
187
+ , defaults = {
188
+ minLength: this.options.showAutocompleteOnFocus ? 0 : null
189
+ }
190
+ , args = $.isArray( typeaheadOptions ) ? typeaheadOptions : [typeaheadOptions, typeaheadOptions]
191
+
192
+ args[0] = $.extend( {}, defaults, args[0] )
193
+
194
+ this.$input.typeahead.apply( this.$input, args )
183
195
  this.typeahead = true
184
196
  }
185
-
186
- this.$element.trigger('tokenfield:initialize')
187
197
  }
188
198
 
189
199
  Tokenfield.prototype = {
@@ -191,130 +201,119 @@
191
201
  constructor: Tokenfield
192
202
 
193
203
  , createToken: function (attrs, triggerChange) {
204
+ var _self = this
205
+
194
206
  if (typeof attrs === 'string') {
195
207
  attrs = { value: attrs, label: attrs }
208
+ } else {
209
+ // Copy objects to prevent contamination of data sources.
210
+ attrs = $.extend( {}, attrs )
196
211
  }
197
212
 
198
- if (typeof triggerChange === 'undefined') {
213
+ if (typeof triggerChange === 'undefined') {
199
214
  triggerChange = true
200
- }
215
+ }
201
216
 
202
- var _self = this
203
- , value = $.trim(attrs.value)
204
- , label = attrs.label && attrs.label.length ? $.trim(attrs.label) : value
217
+ // Normalize label and value
218
+ attrs.value = $.trim(attrs.value.toString());
219
+ attrs.label = attrs.label && attrs.label.length ? $.trim(attrs.label) : attrs.value
205
220
 
206
- if (!value.length || !label.length || value.length < this.options.minLength) return
221
+ // Bail out if has no value or label, or label is too short
222
+ if (!attrs.value.length || !attrs.label.length || attrs.label.length <= this.options.minLength) return
207
223
 
224
+ // Bail out if maximum number of tokens is reached
208
225
  if (this.options.limit && this.getTokens().length >= this.options.limit) return
209
226
 
210
227
  // Allow changing token data before creating it
211
- var prepareEvent = $.Event('tokenfield:preparetoken')
212
- prepareEvent.token = {
213
- value: value,
214
- label: label
215
- }
216
- this.$element.trigger( prepareEvent )
217
-
218
- if (!prepareEvent.token) return
219
-
220
- value = prepareEvent.token.value
221
- label = prepareEvent.token.label
222
-
223
- // Check for duplicates
224
- if (!this.options.allowDuplicates && $.grep(this.getTokens(), function (token) {
225
- return token.value === value
226
- }).length) {
227
- // Allow listening to when duplicates get prevented
228
- var preventDuplicateEvent = $.Event('tokenfield:preventduplicate')
229
- preventDuplicateEvent.token = {
230
- value: value,
231
- label: label
232
- }
233
- this.$element.trigger( preventDuplicateEvent )
234
- // Add duplicate warning class to existing token for 250ms
235
- var duplicate = this.$wrapper.find( '.token[data-value="' + value + '"]' ).addClass('duplicate')
236
- setTimeout(function() {
237
- duplicate.removeClass('duplicate');
238
- }, 250)
239
- return false
240
- }
228
+ var createEvent = $.Event('tokenfield:createtoken', { attrs: attrs })
229
+ this.$element.trigger(createEvent)
241
230
 
242
- var token = $('<div class="token" />')
243
- .attr('data-value', value)
231
+ // Bail out if there if attributes are empty or event was defaultPrevented
232
+ if (!createEvent.attrs || createEvent.isDefaultPrevented()) return
233
+
234
+ var $token = $('<div class="token" />')
244
235
  .append('<span class="token-label" />')
245
236
  .append('<a href="#" class="close" tabindex="-1">&times;</a>')
237
+ .data('attrs', attrs)
246
238
 
247
239
  // Insert token into HTML
248
240
  if (this.$input.hasClass('tt-input')) {
249
- this.$input.parent().before( token )
241
+ // If the input has typeahead enabled, insert token before it's parent
242
+ this.$input.parent().before( $token )
250
243
  } else {
251
- this.$input.before( token )
244
+ this.$input.before( $token )
252
245
  }
246
+
247
+ // Temporarily set input width to minimum
253
248
  this.$input.css('width', this.options.minWidth + 'px')
254
249
 
255
- var tokenLabel = token.find('.token-label')
256
- , closeButton = token.find('.close')
250
+ var $tokenLabel = $token.find('.token-label')
251
+ , $closeButton = $token.find('.close')
257
252
 
258
253
  // Determine maximum possible token label width
259
254
  if (!this.maxTokenWidth) {
260
255
  this.maxTokenWidth =
261
- this.$wrapper.width() - closeButton.outerWidth() -
262
- parseInt(closeButton.css('margin-left'), 10) -
263
- parseInt(closeButton.css('margin-right'), 10) -
264
- parseInt(token.css('border-left-width'), 10) -
265
- parseInt(token.css('border-right-width'), 10) -
266
- parseInt(token.css('padding-left'), 10) -
267
- parseInt(token.css('padding-right'), 10)
268
- parseInt(tokenLabel.css('border-left-width'), 10) -
269
- parseInt(tokenLabel.css('border-right-width'), 10) -
270
- parseInt(tokenLabel.css('padding-left'), 10) -
271
- parseInt(tokenLabel.css('padding-right'), 10)
272
- parseInt(tokenLabel.css('margin-left'), 10) -
273
- parseInt(tokenLabel.css('margin-right'), 10)
274
- }
275
-
276
- tokenLabel
277
- .text(label)
256
+ this.$wrapper.width() - $closeButton.outerWidth() -
257
+ parseInt($closeButton.css('margin-left'), 10) -
258
+ parseInt($closeButton.css('margin-right'), 10) -
259
+ parseInt($token.css('border-left-width'), 10) -
260
+ parseInt($token.css('border-right-width'), 10) -
261
+ parseInt($token.css('padding-left'), 10) -
262
+ parseInt($token.css('padding-right'), 10)
263
+ parseInt($tokenLabel.css('border-left-width'), 10) -
264
+ parseInt($tokenLabel.css('border-right-width'), 10) -
265
+ parseInt($tokenLabel.css('padding-left'), 10) -
266
+ parseInt($tokenLabel.css('padding-right'), 10)
267
+ parseInt($tokenLabel.css('margin-left'), 10) -
268
+ parseInt($tokenLabel.css('margin-right'), 10)
269
+ }
270
+
271
+ $tokenLabel
272
+ .text(attrs.label)
278
273
  .css('max-width', this.maxTokenWidth)
279
274
 
280
- // Listen to events
281
- token
275
+ // Listen to events on token
276
+ $token
282
277
  .on('mousedown', function (e) {
283
- if (_self.disabled) return false;
278
+ if (_self._disabled || _self._readonly) return false
284
279
  _self.preventDeactivation = true
285
280
  })
286
281
  .on('click', function (e) {
287
- if (_self.disabled) return false;
282
+ if (_self._disabled || _self._readonly) return false
288
283
  _self.preventDeactivation = false
289
284
 
290
285
  if (e.ctrlKey || e.metaKey) {
291
286
  e.preventDefault()
292
- return _self.toggle( token )
287
+ return _self.toggle( $token )
293
288
  }
294
289
 
295
- _self.activate( token, e.shiftKey, e.shiftKey )
290
+ _self.activate( $token, e.shiftKey, e.shiftKey )
296
291
  })
297
292
  .on('dblclick', function (e) {
298
- if (_self.disabled || !_self.options.allowEditing ) return false;
299
- _self.edit( token )
293
+ if (_self._disabled || _self._readonly || !_self.options.allowEditing ) return false
294
+ _self.edit( $token )
300
295
  })
301
296
 
302
- closeButton
297
+ $closeButton
303
298
  .on('click', $.proxy(this.remove, this))
304
299
 
305
- var createEvent = $.Event('tokenfield:createtoken')
306
- createEvent.token = prepareEvent.token
307
- createEvent.relatedTarget = token.get(0)
308
- this.$element.trigger( createEvent )
300
+ // Trigger createdtoken event on the original field
301
+ // indicating that the token is now in the DOM
302
+ this.$element.trigger($.Event('tokenfield:createdtoken', {
303
+ attrs: attrs,
304
+ relatedTarget: $token.get(0)
305
+ }))
309
306
 
310
- var changeEvent = $.Event('change')
311
- changeEvent.initiator = 'tokenfield'
307
+ // Trigger change event on the original field
312
308
  if (triggerChange) {
313
- this.$element.val( this.getTokensList() ).trigger( changeEvent )
309
+ this.$element.val( this.getTokensList() ).trigger( $.Event('change', { initiator: 'tokenfield' }) )
314
310
  }
311
+
312
+ // Update tokenfield dimensions
315
313
  this.update()
316
314
 
317
- return this.$input.get(0)
315
+ // Return original element
316
+ return this.$element.get(0)
318
317
  }
319
318
 
320
319
  , setTokens: function (tokens, add, triggerChange) {
@@ -336,20 +335,17 @@
336
335
  }
337
336
 
338
337
  var _self = this
339
- $.each(tokens, function (i, token) {
340
- _self.createToken(token, triggerChange)
338
+ $.each(tokens, function (i, attrs) {
339
+ _self.createToken(attrs, triggerChange)
341
340
  })
342
341
 
343
342
  return this.$element.get(0)
344
343
  }
345
344
 
346
- , getTokenData: function(token) {
347
- var data = token.map(function() {
345
+ , getTokenData: function($token) {
346
+ var data = $token.map(function() {
348
347
  var $token = $(this);
349
- return {
350
- value: $token.attr('data-value'),
351
- label: $token.find('.token-label').text()
352
- }
348
+ return $token.data('attrs')
353
349
  }).get();
354
350
 
355
351
  if (data.length == 1) {
@@ -431,7 +427,7 @@
431
427
  }
432
428
  return false
433
429
  })
434
- .on('typeahead:selected', function (e, datum, dataset) {
430
+ .on('typeahead:selected typeahead:autocompleted', function (e, datum, dataset) {
435
431
  // Create token
436
432
  if (_self.createToken( datum )) {
437
433
  _self.$input.typeahead('val', '')
@@ -440,13 +436,6 @@
440
436
  }
441
437
  }
442
438
  })
443
- .on('typeahead:autocompleted', function (e, datum, dataset) {
444
- _self.createToken( _self.$input.val() )
445
- _self.$input.typeahead('val', '')
446
- if (_self.$input.data( 'edit' )) {
447
- _self.unedit(true)
448
- }
449
- })
450
439
 
451
440
  // Listen to window resize
452
441
  $(window).on('resize', $.proxy(this.update, this ))
@@ -491,11 +480,11 @@
491
480
  case 13: // enter
492
481
 
493
482
  // We will handle creating tokens from autocomplete in autocomplete events
494
- if (this.$input.data('ui-autocomplete') && this.$input.data('ui-autocomplete').menu.element.find("li:has(a.ui-state-focus)").length) break
483
+ 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
495
484
 
496
485
  // We will handle creating tokens from typeahead in typeahead events
497
486
  if (this.$input.hasClass('tt-input') && this.$wrapper.find('.tt-cursor').length ) break
498
- if (this.$input.hasClass('tt-input') && this.$wrapper.find('.tt-hint').val().length) break
487
+ if (this.$input.hasClass('tt-input') && this.$wrapper.find('.tt-hint').val() && this.$wrapper.find('.tt-hint').val().length) break
499
488
 
500
489
  // Create token
501
490
  if (this.$input.is(document.activeElement) && this.$input.val().length || this.$input.data('edit')) {
@@ -515,13 +504,13 @@
515
504
  if (_self.$input.val().length > 0) return
516
505
 
517
506
  direction += 'All'
518
- var token = _self.$input.hasClass('tt-input') ? _self.$input.parent()[direction]('.token:first') : _self.$input[direction]('.token:first')
519
- if (!token.length) return
507
+ var $token = _self.$input.hasClass('tt-input') ? _self.$input.parent()[direction]('.token:first') : _self.$input[direction]('.token:first')
508
+ if (!$token.length) return
520
509
 
521
510
  _self.preventInputFocus = true
522
511
  _self.preventDeactivation = true
523
512
 
524
- _self.activate( token )
513
+ _self.activate( $token )
525
514
  e.preventDefault()
526
515
 
527
516
  } else {
@@ -536,16 +525,16 @@
536
525
  if (_self.$input.is(document.activeElement)) {
537
526
  if (_self.$input.val().length > 0) return
538
527
 
539
- var token = _self.$input.hasClass('tt-input') ? _self.$input.parent()[direction + 'All']('.token:first') : _self.$input[direction + 'All']('.token:first')
540
- if (!token.length) return
528
+ var $token = _self.$input.hasClass('tt-input') ? _self.$input.parent()[direction + 'All']('.token:first') : _self.$input[direction + 'All']('.token:first')
529
+ if (!$token.length) return
541
530
 
542
- _self.activate( token )
531
+ _self.activate( $token )
543
532
  }
544
533
 
545
534
  var opposite = direction === 'prev' ? 'next' : 'prev'
546
535
  , position = direction === 'prev' ? 'first' : 'last'
547
536
 
548
- _self.firstActiveToken[opposite + 'All']('.token').each(function() {
537
+ _self.$firstActiveToken[opposite + 'All']('.token').each(function() {
549
538
  _self.deactivate( $(this) )
550
539
  })
551
540
 
@@ -557,11 +546,9 @@
557
546
  }
558
547
 
559
548
  , keypress: function(e) {
560
- this.lastKeyPressCode = e.keyCode
561
- this.lastKeyPressCharCode = e.charCode
562
549
 
563
550
  // Comma
564
- if ($.inArray( e.charCode, this._triggerKeys) !== -1 && this.$input.is(document.activeElement)) {
551
+ if ($.inArray( e.which, this._triggerKeys) !== -1 && this.$input.is(document.activeElement)) {
565
552
  if (this.$input.val()) {
566
553
  this.createTokensFromInput(e)
567
554
  }
@@ -580,11 +567,11 @@
580
567
  if (this.$input.val().length || this.lastInputValue.length && this.lastKeyDown === 8) break
581
568
 
582
569
  this.preventDeactivation = true
583
- var prev = this.$input.hasClass('tt-input') ? this.$input.parent().prevAll('.token:first') : this.$input.prevAll('.token:first')
570
+ var $prevToken = this.$input.hasClass('tt-input') ? this.$input.parent().prevAll('.token:first') : this.$input.prevAll('.token:first')
584
571
 
585
- if (!prev.length) break
572
+ if (!$prevToken.length) break
586
573
 
587
- this.activate( prev )
574
+ this.activate( $prevToken )
588
575
  } else {
589
576
  this.remove(e)
590
577
  }
@@ -603,7 +590,7 @@
603
590
 
604
591
  if (this.$input.is(document.activeElement)) {
605
592
  this.$wrapper.find('.active').removeClass('active')
606
- this.firstActiveToken = null
593
+ this.$firstActiveToken = null
607
594
 
608
595
  if (this.options.showAutocompleteOnFocus) {
609
596
  this.search()
@@ -618,7 +605,7 @@
618
605
 
619
606
  if (!this.preventDeactivation && !this.$element.is(document.activeElement)) {
620
607
  this.$wrapper.find('.active').removeClass('active')
621
- this.firstActiveToken = null
608
+ this.$firstActiveToken = null
622
609
  }
623
610
 
624
611
  if (!this.preventCreateTokens && (this.$input.data('edit') && !this.$input.is(document.activeElement) || this.options.createTokensOnBlur )) {
@@ -633,9 +620,11 @@
633
620
  var _self = this
634
621
 
635
622
  // Add tokens to existing ones
636
- setTimeout(function () {
637
- _self.createTokensFromInput(e)
638
- }, 1)
623
+ if (_self.options.allowPasting) {
624
+ setTimeout(function () {
625
+ _self.createTokensFromInput(e)
626
+ }, 1)
627
+ }
639
628
  }
640
629
 
641
630
  , change: function (e) {
@@ -671,52 +660,50 @@
671
660
 
672
661
  , next: function (add) {
673
662
  if (add) {
674
- var firstActive = this.$wrapper.find('.active:first')
675
- , deactivate = firstActive && this.firstActiveToken ? firstActive.index() < this.firstActiveToken.index() : false
663
+ var $firstActiveToken = this.$wrapper.find('.active:first')
664
+ , deactivate = $firstActiveToken && this.$firstActiveToken ? $firstActiveToken.index() < this.$firstActiveToken.index() : false
676
665
 
677
- if (deactivate) return this.deactivate( firstActive )
666
+ if (deactivate) return this.deactivate( $firstActiveToken )
678
667
  }
679
668
 
680
- var active = this.$wrapper.find('.active:last')
681
- , next = active.nextAll('.token:first')
669
+ var $lastActiveToken = this.$wrapper.find('.active:last')
670
+ , $nextToken = $lastActiveToken.nextAll('.token:first')
682
671
 
683
- if (!next.length) {
672
+ if (!$nextToken.length) {
684
673
  this.$input.focus()
685
674
  return
686
675
  }
687
676
 
688
- this.activate(next, add)
677
+ this.activate($nextToken, add)
689
678
  }
690
679
 
691
680
  , prev: function (add) {
692
681
 
693
682
  if (add) {
694
- var lastActive = this.$wrapper.find('.active:last')
695
- , deactivate = lastActive && this.firstActiveToken ? lastActive.index() > this.firstActiveToken.index() : false
683
+ var $lastActiveToken = this.$wrapper.find('.active:last')
684
+ , deactivate = $lastActiveToken && this.$firstActiveToken ? $lastActiveToken.index() > this.$firstActiveToken.index() : false
696
685
 
697
- if (deactivate) return this.deactivate( lastActive )
686
+ if (deactivate) return this.deactivate( $lastActiveToken )
698
687
  }
699
688
 
700
- var active = this.$wrapper.find('.active:first')
701
- , prev = active.prevAll('.token:first')
689
+ var $firstActiveToken = this.$wrapper.find('.active:first')
690
+ , $prevToken = $firstActiveToken.prevAll('.token:first')
702
691
 
703
- if (!prev.length) {
704
- prev = this.$wrapper.find('.token:first')
692
+ if (!$prevToken.length) {
693
+ $prevToken = this.$wrapper.find('.token:first')
705
694
  }
706
695
 
707
- if (!prev.length && !add) {
696
+ if (!$prevToken.length && !add) {
708
697
  this.$input.focus()
709
698
  return
710
699
  }
711
700
 
712
- this.activate( prev, add )
701
+ this.activate( $prevToken, add )
713
702
  }
714
703
 
715
- , activate: function (token, add, multi, remember) {
716
-
717
- if (!token) return
704
+ , activate: function ($token, add, multi, remember) {
718
705
 
719
- if (this.$wrapper.find('.token.active').length === this.$wrapper.find('.token').length) return
706
+ if (!$token) return
720
707
 
721
708
  if (typeof remember === 'undefined') var remember = true
722
709
 
@@ -727,17 +714,17 @@
727
714
  if (!add) {
728
715
  this.$wrapper.find('.active').removeClass('active')
729
716
  if (remember) {
730
- this.firstActiveToken = token
717
+ this.$firstActiveToken = $token
731
718
  } else {
732
- delete this.firstActiveToken
719
+ delete this.$firstActiveToken
733
720
  }
734
721
  }
735
722
 
736
- if (multi && this.firstActiveToken) {
723
+ if (multi && this.$firstActiveToken) {
737
724
  // Determine first active token and the current tokens indicies
738
725
  // Account for the 1 hidden textarea by subtracting 1 from both
739
- var i = this.firstActiveToken.index() - 2
740
- , a = token.index() - 2
726
+ var i = this.$firstActiveToken.index() - 2
727
+ , a = $token.index() - 2
741
728
  , _self = this
742
729
 
743
730
  this.$wrapper.find('.token').slice( Math.min(i, a) + 1, Math.max(i, a) ).each( function() {
@@ -745,7 +732,7 @@
745
732
  })
746
733
  }
747
734
 
748
- token.addClass('active')
735
+ $token.addClass('active')
749
736
  this.$copyHelper.val( this.getTokensList( null, null, true ) ).select()
750
737
  }
751
738
 
@@ -757,55 +744,51 @@
757
744
  })
758
745
  }
759
746
 
760
- , deactivate: function(token) {
761
- if (!token) return
747
+ , deactivate: function($token) {
748
+ if (!$token) return
762
749
 
763
- token.removeClass('active')
750
+ $token.removeClass('active')
764
751
  this.$copyHelper.val( this.getTokensList( null, null, true ) ).select()
765
752
  }
766
753
 
767
- , toggle: function(token) {
768
- if (!token) return
754
+ , toggle: function($token) {
755
+ if (!$token) return
769
756
 
770
- token.toggleClass('active')
757
+ $token.toggleClass('active')
771
758
  this.$copyHelper.val( this.getTokensList( null, null, true ) ).select()
772
759
  }
773
760
 
774
- , edit: function (token) {
775
- if (!token) return
761
+ , edit: function ($token) {
762
+ if (!$token) return
776
763
 
777
- var value = token.data('value')
778
- , label = token.find('.token-label').text()
764
+ var attrs = $token.data('attrs')
779
765
 
780
766
  // Allow changing input value before editing
781
- var editEvent = $.Event('tokenfield:edittoken')
782
- editEvent.token = {
783
- value: value,
784
- label: label
785
- }
786
- editEvent.relatedTarget = token.get(0)
767
+ var options = { attrs: attrs, relatedTarget: $token.get(0) }
768
+ var editEvent = $.Event('tokenfield:edittoken', options)
787
769
  this.$element.trigger( editEvent )
788
770
 
789
- if (!editEvent.token) return
790
-
791
- value = editEvent.token.value
792
- label = editEvent.token.label
771
+ // Edit event can be cancelled if default is prevented
772
+ if (editEvent.isDefaultPrevented()) return
793
773
 
794
- token.find('.token-label').text(value)
795
- var tokenWidth = token.outerWidth()
774
+ $token.find('.token-label').text(attrs.value)
775
+ var tokenWidth = $token.outerWidth()
796
776
 
797
777
  var $_input = this.$input.hasClass('tt-input') ? this.$input.parent() : this.$input
798
778
 
799
- token.replaceWith( $_input )
779
+ $token.replaceWith( $_input )
800
780
 
801
781
  this.preventCreateTokens = true
802
782
 
803
- this.$input.val( value )
783
+ this.$input.val( attrs.value )
804
784
  .select()
805
785
  .data( 'edit', true )
806
786
  .width( tokenWidth )
807
787
 
808
788
  this.update();
789
+
790
+ // Indicate that token is now being edited, and is replaced with an input field in the DOM
791
+ this.$element.trigger($.Event('tokenfield:editedtoken', options ))
809
792
  }
810
793
 
811
794
  , unedit: function (focus) {
@@ -829,31 +812,35 @@
829
812
  }
830
813
 
831
814
  , remove: function (e, direction) {
832
- if (this.$input.is(document.activeElement) || this.disabled) return
815
+ if (this.$input.is(document.activeElement) || this._disabled || this._readonly) return
833
816
 
834
- var token = (e.type === 'click') ? $(e.target).closest('.token') : this.$wrapper.find('.token.active')
817
+ var $token = (e.type === 'click') ? $(e.target).closest('.token') : this.$wrapper.find('.token.active')
835
818
 
836
819
  if (e.type !== 'click') {
837
820
  if (!direction) var direction = 'prev'
838
821
  this[direction]()
839
822
 
840
- // Was this the first token?
841
- if (direction === 'prev') var firstToken = token.first().prevAll('.token:first').length === 0
823
+ // Was it the first token?
824
+ if (direction === 'prev') var firstToken = $token.first().prevAll('.token:first').length === 0
842
825
  }
843
826
 
844
- // Prepare events
827
+ // Prepare events and their options
828
+ var options = { attrs: this.getTokenData( $token ), relatedTarget: $token.get(0) }
829
+ , removeEvent = $.Event('tokenfield:removetoken', options)
830
+
831
+ this.$element.trigger(removeEvent);
845
832
 
846
- var removeEvent = $.Event('tokenfield:removetoken')
847
- removeEvent.token = this.getTokenData( token )
833
+ // Remove event can be intercepted and cancelled
834
+ if (removeEvent.isDefaultPrevented()) return
848
835
 
849
- var changeEvent = $.Event('change')
850
- changeEvent.initiator = 'tokenfield'
836
+ var removedEvent = $.Event('tokenfield:removedtoken', options)
837
+ , changeEvent = $.Event('change', { initiator: 'tokenfield' })
851
838
 
852
839
  // Remove token from DOM
853
- token.remove()
840
+ $token.remove()
854
841
 
855
842
  // Trigger events
856
- this.$element.val( this.getTokensList() ).trigger( removeEvent ).trigger( changeEvent )
843
+ this.$element.val( this.getTokensList() ).trigger( removedEvent ).trigger( changeEvent )
857
844
 
858
845
  // Focus, when necessary:
859
846
  // When there are no more tokens, or if this was the first token
@@ -864,15 +851,19 @@
864
851
  this.$input.css('width', this.options.minWidth + 'px')
865
852
  this.update()
866
853
 
854
+ // Cancel original event handlers
867
855
  e.preventDefault()
868
856
  e.stopPropagation()
869
857
  }
870
858
 
859
+ /**
860
+ * Update tokenfield dimensions
861
+ */
871
862
  , update: function (e) {
872
863
  var value = this.$input.val()
873
- , inputLeftPadding = parseInt(this.$input.css('padding-left'), 10)
874
- , inputRightPadding = parseInt(this.$input.css('padding-right'), 10)
875
- , inputPadding = inputLeftPadding + inputRightPadding
864
+ , inputPaddingLeft = parseInt(this.$input.css('padding-left'), 10)
865
+ , inputPaddingRight = parseInt(this.$input.css('padding-right'), 10)
866
+ , inputPadding = inputPaddingLeft + inputPaddingRight
876
867
 
877
868
  if (this.$input.data('edit')) {
878
869
 
@@ -891,16 +882,19 @@
891
882
  this.$input.width( mirrorWidth )
892
883
  }
893
884
  else {
894
- this.$input.css( 'width', this.options.minWidth + 'px' )
895
- if (this.textDirection === 'rtl') {
896
- return this.$input.width( this.$input.offset().left + this.$input.outerWidth() - this.$wrapper.offset().left - parseInt(this.$wrapper.css('padding-left'), 10) - inputPadding - 1 )
897
- }
898
- this.$input.width( this.$wrapper.offset().left + this.$wrapper.width() + parseInt(this.$wrapper.css('padding-left'), 10) - this.$input.offset().left - inputPadding )
885
+ var w = (this.textDirection === 'rtl')
886
+ ? this.$input.offset().left + this.$input.outerWidth() - this.$wrapper.offset().left - parseInt(this.$wrapper.css('padding-left'), 10) - inputPadding - 1
887
+ : this.$wrapper.offset().left + this.$wrapper.width() + parseInt(this.$wrapper.css('padding-left'), 10) - this.$input.offset().left - inputPadding;
888
+ //
889
+ // some usecases pre-render widget before attaching to DOM,
890
+ // dimensions returned by jquery will be NaN -> we default to 100%
891
+ // so placeholder won't be cut off.
892
+ isNaN(w) ? this.$input.width('100%') : this.$input.width(w);
899
893
  }
900
894
  }
901
895
 
902
896
  , focusInput: function (e) {
903
- if ($(e.target).closest('.token').length || $(e.target).closest('.token-input').length) return
897
+ if ( $(e.target).closest('.token').length || $(e.target).closest('.token-input').length || $(e.target).closest('.tt-dropdown-menu').length ) return
904
898
  // Focus only after the current call stack has cleared,
905
899
  // otherwise has no effect.
906
900
  // Reason: mousedown is too early - input will lose focus
@@ -919,19 +913,28 @@
919
913
  }
920
914
 
921
915
  , disable: function () {
922
- this.disabled = true;
923
- this.$input.prop('disabled', true);
924
- this.$element.prop('disabled', true);
925
- this.$wrapper.addClass('disabled');
916
+ this.setProperty('disabled', true);
926
917
  }
927
918
 
928
919
  , enable: function () {
929
- this.disabled = false;
930
- this.$input.prop('disabled', false);
931
- this.$element.prop('disabled', false);
932
- this.$wrapper.removeClass('disabled');
920
+ this.setProperty('disabled', false);
921
+ }
922
+
923
+ , readonly: function () {
924
+ this.setProperty('readonly', true);
933
925
  }
934
926
 
927
+ , writeable: function () {
928
+ this.setProperty('readonly', false);
929
+ }
930
+
931
+ , setProperty: function(property, value) {
932
+ this['_' + property] = value;
933
+ this.$input.prop(property, value);
934
+ this.$element.prop(property, value);
935
+ this.$wrapper[ value ? 'addClass' : 'removeClass' ](property);
936
+ }
937
+
935
938
  , destroy: function() {
936
939
  // Set field value
937
940
  this.$element.val( this.getTokensList() );
@@ -939,7 +942,7 @@
939
942
  this.$element.css( this.$element.data('original-styles') );
940
943
  this.$element.prop( 'tabindex', this.$element.data('original-tabindex') );
941
944
 
942
- // Re-route tokenfield labele to original input
945
+ // Re-route tokenfield label to original input
943
946
  var $label = $( 'label[for="' + this.$input.prop('id') + '"]' )
944
947
  if ( $label.length ) {
945
948
  $label.prop( 'for', this.$element.prop('id') )
@@ -949,15 +952,15 @@
949
952
  this.$element.insertBefore( this.$wrapper );
950
953
 
951
954
  // Remove tokenfield-related data
952
- this.$element.removeData('original-styles');
953
- this.$element.removeData('original-tabindex');
954
- this.$element.removeData('bs.tokenfield');
955
+ this.$element.removeData('original-styles')
956
+ .removeData('original-tabindex')
957
+ .removeData('bs.tokenfield');
955
958
 
956
959
  // Remove tokenfield from DOM
957
960
  this.$wrapper.remove();
961
+ this.$mirror.remove();
958
962
 
959
963
  var $_element = this.$element;
960
- delete this;
961
964
 
962
965
  return $_element;
963
966
  }
@@ -985,7 +988,10 @@
985
988
  args.shift()
986
989
  value = data[option].apply(data, args)
987
990
  } else {
988
- if (!data && typeof option !== 'string' && !param) $this.data('bs.tokenfield', (data = new Tokenfield(this, options)))
991
+ if (!data && typeof option !== 'string' && !param) {
992
+ $this.data('bs.tokenfield', (data = new Tokenfield(this, options)))
993
+ $this.trigger('tokenfield:initialize')
994
+ }
989
995
  }
990
996
  })
991
997
 
@@ -995,15 +1001,16 @@
995
1001
  $.fn.tokenfield.defaults = {
996
1002
  minWidth: 60,
997
1003
  minLength: 0,
998
- allowDuplicates: false,
999
1004
  allowEditing: true,
1005
+ allowPasting: true,
1000
1006
  limit: 0,
1001
1007
  autocomplete: {},
1002
1008
  typeahead: {},
1003
1009
  showAutocompleteOnFocus: false,
1004
1010
  createTokensOnBlur: false,
1005
1011
  delimiter: ',',
1006
- beautify: true
1012
+ beautify: true,
1013
+ inputType: 'text'
1007
1014
  }
1008
1015
 
1009
1016
  $.fn.tokenfield.Constructor = Tokenfield
@@ -3,7 +3,7 @@
3
3
  * https://github.com/sliptree/bootstrap-tokenfield
4
4
  * Copyright 2013-2014 Sliptree and other contributors; Licensed MIT
5
5
  */
6
- @-webkit-keyframes 'blink' {
6
+ @-webkit-keyframes blink {
7
7
  0% {
8
8
  border-color: #ededed;
9
9
  }
@@ -11,7 +11,7 @@
11
11
  border-color: #b94a48;
12
12
  }
13
13
  }
14
- @-moz-keyframes 'blink' {
14
+ @-moz-keyframes blink {
15
15
  0% {
16
16
  border-color: #ededed;
17
17
  }
@@ -19,7 +19,7 @@
19
19
  border-color: #b94a48;
20
20
  }
21
21
  }
22
- @keyframes 'blink' {
22
+ @keyframes blink {
23
23
  0% {
24
24
  border-color: #ededed;
25
25
  }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bootstrap_tokenfield_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Akash Devaraju
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-16 00:00:00.000000000 Z
11
+ date: 2014-10-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -76,3 +76,4 @@ signing_key:
76
76
  specification_version: 4
77
77
  summary: A jQuery tagging / tokenizer input plugin for Twitter's Bootstrap
78
78
  test_files: []
79
+ has_rdoc: