bootstrap_tokenfield_rails 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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: