select2-rails 4.0.2 → 4.0.3

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/select2-rails/version.rb +1 -1
  4. data/vendor/assets/javascripts/select2-full.js +173 -28
  5. data/vendor/assets/javascripts/select2.js +173 -28
  6. data/vendor/assets/javascripts/select2_locale_ar.js +1 -1
  7. data/vendor/assets/javascripts/select2_locale_az.js +1 -1
  8. data/vendor/assets/javascripts/select2_locale_bg.js +1 -1
  9. data/vendor/assets/javascripts/select2_locale_ca.js +1 -1
  10. data/vendor/assets/javascripts/select2_locale_cs.js +1 -1
  11. data/vendor/assets/javascripts/select2_locale_da.js +1 -1
  12. data/vendor/assets/javascripts/select2_locale_de.js +1 -1
  13. data/vendor/assets/javascripts/select2_locale_el.js +3 -0
  14. data/vendor/assets/javascripts/select2_locale_en.js +1 -1
  15. data/vendor/assets/javascripts/select2_locale_es.js +1 -1
  16. data/vendor/assets/javascripts/select2_locale_et.js +1 -1
  17. data/vendor/assets/javascripts/select2_locale_eu.js +1 -1
  18. data/vendor/assets/javascripts/select2_locale_fa.js +1 -1
  19. data/vendor/assets/javascripts/select2_locale_fi.js +1 -1
  20. data/vendor/assets/javascripts/select2_locale_fr.js +2 -2
  21. data/vendor/assets/javascripts/select2_locale_gl.js +1 -1
  22. data/vendor/assets/javascripts/select2_locale_he.js +1 -1
  23. data/vendor/assets/javascripts/select2_locale_hi.js +1 -1
  24. data/vendor/assets/javascripts/select2_locale_hr.js +1 -1
  25. data/vendor/assets/javascripts/select2_locale_hu.js +1 -1
  26. data/vendor/assets/javascripts/select2_locale_id.js +1 -1
  27. data/vendor/assets/javascripts/select2_locale_is.js +1 -1
  28. data/vendor/assets/javascripts/select2_locale_it.js +1 -1
  29. data/vendor/assets/javascripts/select2_locale_ja.js +1 -1
  30. data/vendor/assets/javascripts/select2_locale_km.js +3 -0
  31. data/vendor/assets/javascripts/select2_locale_ko.js +1 -1
  32. data/vendor/assets/javascripts/select2_locale_lt.js +2 -2
  33. data/vendor/assets/javascripts/select2_locale_lv.js +1 -1
  34. data/vendor/assets/javascripts/select2_locale_mk.js +1 -1
  35. data/vendor/assets/javascripts/select2_locale_ms.js +1 -1
  36. data/vendor/assets/javascripts/select2_locale_nb.js +2 -2
  37. data/vendor/assets/javascripts/select2_locale_nl.js +1 -1
  38. data/vendor/assets/javascripts/select2_locale_pl.js +1 -1
  39. data/vendor/assets/javascripts/select2_locale_pt-BR.js +1 -1
  40. data/vendor/assets/javascripts/select2_locale_pt.js +1 -1
  41. data/vendor/assets/javascripts/select2_locale_ro.js +1 -1
  42. data/vendor/assets/javascripts/select2_locale_ru.js +1 -1
  43. data/vendor/assets/javascripts/select2_locale_sk.js +1 -1
  44. data/vendor/assets/javascripts/select2_locale_sr-Cyrl.js +1 -1
  45. data/vendor/assets/javascripts/select2_locale_sr.js +1 -1
  46. data/vendor/assets/javascripts/select2_locale_sv.js +1 -1
  47. data/vendor/assets/javascripts/select2_locale_th.js +1 -1
  48. data/vendor/assets/javascripts/select2_locale_tr.js +1 -1
  49. data/vendor/assets/javascripts/select2_locale_uk.js +1 -1
  50. data/vendor/assets/javascripts/select2_locale_vi.js +1 -1
  51. data/vendor/assets/javascripts/select2_locale_zh-CN.js +1 -1
  52. data/vendor/assets/javascripts/select2_locale_zh-TW.js +1 -1
  53. data/vendor/assets/stylesheets/select2-bootstrap.css +38 -26
  54. data/vendor/assets/stylesheets/select2.css +2 -0
  55. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0cfb142f6820d03c51ffb2c9d4d6cd5ab7a6de1e
4
- data.tar.gz: 1a2a1cbb0822d5324c9e15d46d0889f67d7ecd04
3
+ metadata.gz: d128112240be89bfb1d73d73f957f94d983edb61
4
+ data.tar.gz: dfdda950dd61f0fa042db94f1ab74914e7fc5b29
5
5
  SHA512:
6
- metadata.gz: f11620f64d9c862f3fd638d80cd6d43c6d615e93e824307dc261541b89d21c882776c7e0e52a01d2613d94e29b3c902b5415e436a571f08da272df432f49e292
7
- data.tar.gz: 6a3acda3038500b082bb9bc3f9b05577206bc63691eab938cbfd03e8de1172ff61b242a31c9df3c73cf709c6e8f60690b41f7ea95f7513fb4b812e1870d1334f
6
+ metadata.gz: ee533ad76c7a28de7f3365beba124c8d1810b7be557a2ffc5ac2474bbf262f1dead42b3f36cbeb5b09c597873f4def72e6a17a6b50cf8ebfb13244ebed0669ef
7
+ data.tar.gz: 76db3585d883fce0d6d0047ce8aa32b48766fb4f0dea49dd21de862f3759939f054ec10c06dcfee7fe2ce1b1eda3188c2e24ebd26739be7088f0528a8598ab4d
data/README.md CHANGED
@@ -50,7 +50,7 @@ Add the following to your `app/assets/javascripts/application.js`:
50
50
 
51
51
  Possible languages:
52
52
 
53
- ar, az, bg, ca, cs, da, de, en, es, et, eu, fa, fi, fr, gl, he, hi, hr, hu, id, is, it, ja, ko, lt, lv, mk, ms, nb, nl, pl, pt, pt-BR, ro, ru, sk, sr, sr-Cyrl, sv, th, tr, uk, vi, zh-CN, zh-TW
53
+ ar, az, bg, ca, cs, da, de, el, en, es, et, eu, fa, fi, fr, gl, he, hi, hr, hu, id, is, it, ja, km, ko, lt, lv, mk, ms, nb, nl, pl, pt, pt-BR, ro, ru, sk, sr, sr-Cyrl, sv, th, tr, uk, vi, zh-CN, zh-TW
54
54
 
55
55
  ## Example
56
56
  Code [here](https://github.com/argerim/select_2_example)
@@ -1,5 +1,5 @@
1
1
  module Select2
2
2
  module Rails
3
- VERSION = '4.0.2'
3
+ VERSION = '4.0.3'
4
4
  end
5
5
  end
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Select2 4.0.2
2
+ * Select2 4.0.3
3
3
  * https://select2.github.io
4
4
  *
5
5
  * Released under the MIT license
@@ -606,9 +606,23 @@ S2.define('select2/utils',[
606
606
 
607
607
  Observable.prototype.trigger = function (event) {
608
608
  var slice = Array.prototype.slice;
609
+ var params = slice.call(arguments, 1);
609
610
 
610
611
  this.listeners = this.listeners || {};
611
612
 
613
+ // Params should always come in as an array
614
+ if (params == null) {
615
+ params = [];
616
+ }
617
+
618
+ // If there are no arguments to the event, use a temporary object
619
+ if (params.length === 0) {
620
+ params.push({});
621
+ }
622
+
623
+ // Set the `_type` of the first object to the event
624
+ params[0]._type = event;
625
+
612
626
  if (event in this.listeners) {
613
627
  this.invoke(this.listeners[event], slice.call(arguments, 1));
614
628
  }
@@ -842,6 +856,25 @@ S2.define('select2/results',[
842
856
  return sorter(data);
843
857
  };
844
858
 
859
+ Results.prototype.highlightFirstItem = function () {
860
+ var $options = this.$results
861
+ .find('.select2-results__option[aria-selected]');
862
+
863
+ var $selected = $options.filter('[aria-selected=true]');
864
+
865
+ // Check if there are any selected options
866
+ if ($selected.length > 0) {
867
+ // If there are selected options, highlight the first
868
+ $selected.first().trigger('mouseenter');
869
+ } else {
870
+ // If there are no selected options, highlight the first option
871
+ // in the dropdown
872
+ $options.first().trigger('mouseenter');
873
+ }
874
+
875
+ this.ensureHighlightVisible();
876
+ };
877
+
845
878
  Results.prototype.setClasses = function () {
846
879
  var self = this;
847
880
 
@@ -869,17 +902,6 @@ S2.define('select2/results',[
869
902
  }
870
903
  });
871
904
 
872
- var $selected = $options.filter('[aria-selected=true]');
873
-
874
- // Check if there are any selected options
875
- if ($selected.length > 0) {
876
- // If there are selected options, highlight the first
877
- $selected.first().trigger('mouseenter');
878
- } else {
879
- // If there are no selected options, highlight the first option
880
- // in the dropdown
881
- $options.first().trigger('mouseenter');
882
- }
883
905
  });
884
906
  };
885
907
 
@@ -990,6 +1012,7 @@ S2.define('select2/results',[
990
1012
 
991
1013
  if (container.isOpen()) {
992
1014
  self.setClasses();
1015
+ self.highlightFirstItem();
993
1016
  }
994
1017
  });
995
1018
 
@@ -1012,6 +1035,7 @@ S2.define('select2/results',[
1012
1035
  }
1013
1036
 
1014
1037
  self.setClasses();
1038
+ self.highlightFirstItem();
1015
1039
  });
1016
1040
 
1017
1041
  container.on('unselect', function () {
@@ -1020,6 +1044,7 @@ S2.define('select2/results',[
1020
1044
  }
1021
1045
 
1022
1046
  self.setClasses();
1047
+ self.highlightFirstItem();
1023
1048
  });
1024
1049
 
1025
1050
  container.on('open', function () {
@@ -1497,6 +1522,12 @@ S2.define('select2/selection/single',[
1497
1522
  // User exits the container
1498
1523
  });
1499
1524
 
1525
+ container.on('focus', function (evt) {
1526
+ if (!container.isOpen()) {
1527
+ self.$selection.focus();
1528
+ }
1529
+ });
1530
+
1500
1531
  container.on('selection:update', function (params) {
1501
1532
  self.update(params.data);
1502
1533
  });
@@ -3436,6 +3467,12 @@ S2.define('select2/data/ajax',[
3436
3467
 
3437
3468
  callback(results);
3438
3469
  }, function () {
3470
+ // Attempt to detect if a request was aborted
3471
+ // Only works if the transport exposes a status property
3472
+ if ($request.status && $request.status === '0') {
3473
+ return;
3474
+ }
3475
+
3439
3476
  self.trigger('results:message', {
3440
3477
  message: 'errorLoading'
3441
3478
  });
@@ -3444,7 +3481,7 @@ S2.define('select2/data/ajax',[
3444
3481
  self._request = $request;
3445
3482
  }
3446
3483
 
3447
- if (this.ajaxOptions.delay && params.term !== '') {
3484
+ if (this.ajaxOptions.delay && params.term != null) {
3448
3485
  if (this._queryTimeout) {
3449
3486
  window.clearTimeout(this._queryTimeout);
3450
3487
  }
@@ -3607,6 +3644,29 @@ S2.define('select2/data/tokenizer',[
3607
3644
  Tokenizer.prototype.query = function (decorated, params, callback) {
3608
3645
  var self = this;
3609
3646
 
3647
+ function createAndSelect (data) {
3648
+ // Normalize the data object so we can use it for checks
3649
+ var item = self._normalizeItem(data);
3650
+
3651
+ // Check if the data object already exists as a tag
3652
+ // Select it if it doesn't
3653
+ var $existingOptions = self.$element.find('option').filter(function () {
3654
+ return $(this).val() === item.id;
3655
+ });
3656
+
3657
+ // If an existing option wasn't found for it, create the option
3658
+ if (!$existingOptions.length) {
3659
+ var $option = self.option(item);
3660
+ $option.attr('data-select2-tag', true);
3661
+
3662
+ self._removeOldTags();
3663
+ self.addOptions([$option]);
3664
+ }
3665
+
3666
+ // Select the item, now that we know there is an option for it
3667
+ select(item);
3668
+ }
3669
+
3610
3670
  function select (data) {
3611
3671
  self.trigger('select', {
3612
3672
  data: data
@@ -3615,7 +3675,7 @@ S2.define('select2/data/tokenizer',[
3615
3675
 
3616
3676
  params.term = params.term || '';
3617
3677
 
3618
- var tokenData = this.tokenizer(params, this.options, select);
3678
+ var tokenData = this.tokenizer(params, this.options, createAndSelect);
3619
3679
 
3620
3680
  if (tokenData.term !== params.term) {
3621
3681
  // Replace the search term if we have the search box
@@ -3880,6 +3940,12 @@ S2.define('select2/dropdown/search',[
3880
3940
  self.$search.val('');
3881
3941
  });
3882
3942
 
3943
+ container.on('focus', function () {
3944
+ if (container.isOpen()) {
3945
+ self.$search.focus();
3946
+ }
3947
+ });
3948
+
3883
3949
  container.on('results:all', function (params) {
3884
3950
  if (params.query.term == null || params.query.term === '') {
3885
3951
  var showSearch = self.showSearch(params);
@@ -4229,7 +4295,7 @@ S2.define('select2/dropdown/attachBody',[
4229
4295
 
4230
4296
  if (newDirection == 'above' ||
4231
4297
  (isCurrentlyAbove && newDirection !== 'below')) {
4232
- css.top = container.top - dropdown.height;
4298
+ css.top = container.top - parentOffset.top - dropdown.height;
4233
4299
  }
4234
4300
 
4235
4301
  if (newDirection != null) {
@@ -4251,6 +4317,7 @@ S2.define('select2/dropdown/attachBody',[
4251
4317
 
4252
4318
  if (this.options.get('dropdownAutoWidth')) {
4253
4319
  css.minWidth = css.width;
4320
+ css.position = 'relative';
4254
4321
  css.width = 'auto';
4255
4322
  }
4256
4323
 
@@ -4317,12 +4384,22 @@ S2.define('select2/dropdown/selectOnClose',[
4317
4384
 
4318
4385
  decorated.call(this, container, $container);
4319
4386
 
4320
- container.on('close', function () {
4321
- self._handleSelectOnClose();
4387
+ container.on('close', function (params) {
4388
+ self._handleSelectOnClose(params);
4322
4389
  });
4323
4390
  };
4324
4391
 
4325
- SelectOnClose.prototype._handleSelectOnClose = function () {
4392
+ SelectOnClose.prototype._handleSelectOnClose = function (_, params) {
4393
+ if (params && params.originalSelect2Event != null) {
4394
+ var event = params.originalSelect2Event;
4395
+
4396
+ // Don't select an item if the close event was triggered from a select or
4397
+ // unselect event
4398
+ if (event._type === 'select' || event._type === 'unselect') {
4399
+ return;
4400
+ }
4401
+ }
4402
+
4326
4403
  var $highlightedResults = this.getHighlightedResults();
4327
4404
 
4328
4405
  // Only select highlighted results
@@ -4375,7 +4452,10 @@ S2.define('select2/dropdown/closeOnSelect',[
4375
4452
  return;
4376
4453
  }
4377
4454
 
4378
- this.trigger('close', {});
4455
+ this.trigger('close', {
4456
+ originalEvent: originalEvent,
4457
+ originalSelect2Event: evt
4458
+ });
4379
4459
  };
4380
4460
 
4381
4461
  return CloseOnSelect;
@@ -5129,10 +5209,15 @@ S2.define('select2/core',[
5129
5209
  });
5130
5210
  });
5131
5211
 
5132
- this._sync = Utils.bind(this._syncAttributes, this);
5212
+ this.$element.on('focus.select2', function (evt) {
5213
+ self.trigger('focus', evt);
5214
+ });
5215
+
5216
+ this._syncA = Utils.bind(this._syncAttributes, this);
5217
+ this._syncS = Utils.bind(this._syncSubtree, this);
5133
5218
 
5134
5219
  if (this.$element[0].attachEvent) {
5135
- this.$element[0].attachEvent('onpropertychange', this._sync);
5220
+ this.$element[0].attachEvent('onpropertychange', this._syncA);
5136
5221
  }
5137
5222
 
5138
5223
  var observer = window.MutationObserver ||
@@ -5142,14 +5227,30 @@ S2.define('select2/core',[
5142
5227
 
5143
5228
  if (observer != null) {
5144
5229
  this._observer = new observer(function (mutations) {
5145
- $.each(mutations, self._sync);
5230
+ $.each(mutations, self._syncA);
5231
+ $.each(mutations, self._syncS);
5146
5232
  });
5147
5233
  this._observer.observe(this.$element[0], {
5148
5234
  attributes: true,
5235
+ childList: true,
5149
5236
  subtree: false
5150
5237
  });
5151
5238
  } else if (this.$element[0].addEventListener) {
5152
- this.$element[0].addEventListener('DOMAttrModified', self._sync, false);
5239
+ this.$element[0].addEventListener(
5240
+ 'DOMAttrModified',
5241
+ self._syncA,
5242
+ false
5243
+ );
5244
+ this.$element[0].addEventListener(
5245
+ 'DOMNodeInserted',
5246
+ self._syncS,
5247
+ false
5248
+ );
5249
+ this.$element[0].addEventListener(
5250
+ 'DOMNodeRemoved',
5251
+ self._syncS,
5252
+ false
5253
+ );
5153
5254
  }
5154
5255
  };
5155
5256
 
@@ -5294,6 +5395,46 @@ S2.define('select2/core',[
5294
5395
  }
5295
5396
  };
5296
5397
 
5398
+ Select2.prototype._syncSubtree = function (evt, mutations) {
5399
+ var changed = false;
5400
+ var self = this;
5401
+
5402
+ // Ignore any mutation events raised for elements that aren't options or
5403
+ // optgroups. This handles the case when the select element is destroyed
5404
+ if (
5405
+ evt && evt.target && (
5406
+ evt.target.nodeName !== 'OPTION' && evt.target.nodeName !== 'OPTGROUP'
5407
+ )
5408
+ ) {
5409
+ return;
5410
+ }
5411
+
5412
+ if (!mutations) {
5413
+ // If mutation events aren't supported, then we can only assume that the
5414
+ // change affected the selections
5415
+ changed = true;
5416
+ } else if (mutations.addedNodes && mutations.addedNodes.length > 0) {
5417
+ for (var n = 0; n < mutations.addedNodes.length; n++) {
5418
+ var node = mutations.addedNodes[n];
5419
+
5420
+ if (node.selected) {
5421
+ changed = true;
5422
+ }
5423
+ }
5424
+ } else if (mutations.removedNodes && mutations.removedNodes.length > 0) {
5425
+ changed = true;
5426
+ }
5427
+
5428
+ // Only re-pull the data if we think there is a change
5429
+ if (changed) {
5430
+ this.dataAdapter.current(function (currentData) {
5431
+ self.trigger('selection:update', {
5432
+ data: currentData
5433
+ });
5434
+ });
5435
+ }
5436
+ };
5437
+
5297
5438
  /**
5298
5439
  * Override the trigger method to automatically trigger pre-events when
5299
5440
  * there are events that can be prevented.
@@ -5440,7 +5581,7 @@ S2.define('select2/core',[
5440
5581
  this.$container.remove();
5441
5582
 
5442
5583
  if (this.$element[0].detachEvent) {
5443
- this.$element[0].detachEvent('onpropertychange', this._sync);
5584
+ this.$element[0].detachEvent('onpropertychange', this._syncA);
5444
5585
  }
5445
5586
 
5446
5587
  if (this._observer != null) {
@@ -5448,10 +5589,15 @@ S2.define('select2/core',[
5448
5589
  this._observer = null;
5449
5590
  } else if (this.$element[0].removeEventListener) {
5450
5591
  this.$element[0]
5451
- .removeEventListener('DOMAttrModified', this._sync, false);
5592
+ .removeEventListener('DOMAttrModified', this._syncA, false);
5593
+ this.$element[0]
5594
+ .removeEventListener('DOMNodeInserted', this._syncS, false);
5595
+ this.$element[0]
5596
+ .removeEventListener('DOMNodeRemoved', this._syncS, false);
5452
5597
  }
5453
5598
 
5454
- this._sync = null;
5599
+ this._syncA = null;
5600
+ this._syncS = null;
5455
5601
 
5456
5602
  this.$element.off('.select2');
5457
5603
  this.$element.attr('tabindex', this.$element.data('old-tabindex'));
@@ -6235,6 +6381,7 @@ S2.define('jquery.select2',[
6235
6381
  return this;
6236
6382
  } else if (typeof options === 'string') {
6237
6383
  var ret;
6384
+ var args = Array.prototype.slice.call(arguments, 1);
6238
6385
 
6239
6386
  this.each(function () {
6240
6387
  var instance = $(this).data('select2');
@@ -6246,8 +6393,6 @@ S2.define('jquery.select2',[
6246
6393
  );
6247
6394
  }
6248
6395
 
6249
- var args = Array.prototype.slice.call(arguments, 1);
6250
-
6251
6396
  ret = instance[options].apply(instance, args);
6252
6397
  });
6253
6398
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Select2 4.0.2
2
+ * Select2 4.0.3
3
3
  * https://select2.github.io
4
4
  *
5
5
  * Released under the MIT license
@@ -606,9 +606,23 @@ S2.define('select2/utils',[
606
606
 
607
607
  Observable.prototype.trigger = function (event) {
608
608
  var slice = Array.prototype.slice;
609
+ var params = slice.call(arguments, 1);
609
610
 
610
611
  this.listeners = this.listeners || {};
611
612
 
613
+ // Params should always come in as an array
614
+ if (params == null) {
615
+ params = [];
616
+ }
617
+
618
+ // If there are no arguments to the event, use a temporary object
619
+ if (params.length === 0) {
620
+ params.push({});
621
+ }
622
+
623
+ // Set the `_type` of the first object to the event
624
+ params[0]._type = event;
625
+
612
626
  if (event in this.listeners) {
613
627
  this.invoke(this.listeners[event], slice.call(arguments, 1));
614
628
  }
@@ -842,6 +856,25 @@ S2.define('select2/results',[
842
856
  return sorter(data);
843
857
  };
844
858
 
859
+ Results.prototype.highlightFirstItem = function () {
860
+ var $options = this.$results
861
+ .find('.select2-results__option[aria-selected]');
862
+
863
+ var $selected = $options.filter('[aria-selected=true]');
864
+
865
+ // Check if there are any selected options
866
+ if ($selected.length > 0) {
867
+ // If there are selected options, highlight the first
868
+ $selected.first().trigger('mouseenter');
869
+ } else {
870
+ // If there are no selected options, highlight the first option
871
+ // in the dropdown
872
+ $options.first().trigger('mouseenter');
873
+ }
874
+
875
+ this.ensureHighlightVisible();
876
+ };
877
+
845
878
  Results.prototype.setClasses = function () {
846
879
  var self = this;
847
880
 
@@ -869,17 +902,6 @@ S2.define('select2/results',[
869
902
  }
870
903
  });
871
904
 
872
- var $selected = $options.filter('[aria-selected=true]');
873
-
874
- // Check if there are any selected options
875
- if ($selected.length > 0) {
876
- // If there are selected options, highlight the first
877
- $selected.first().trigger('mouseenter');
878
- } else {
879
- // If there are no selected options, highlight the first option
880
- // in the dropdown
881
- $options.first().trigger('mouseenter');
882
- }
883
905
  });
884
906
  };
885
907
 
@@ -990,6 +1012,7 @@ S2.define('select2/results',[
990
1012
 
991
1013
  if (container.isOpen()) {
992
1014
  self.setClasses();
1015
+ self.highlightFirstItem();
993
1016
  }
994
1017
  });
995
1018
 
@@ -1012,6 +1035,7 @@ S2.define('select2/results',[
1012
1035
  }
1013
1036
 
1014
1037
  self.setClasses();
1038
+ self.highlightFirstItem();
1015
1039
  });
1016
1040
 
1017
1041
  container.on('unselect', function () {
@@ -1020,6 +1044,7 @@ S2.define('select2/results',[
1020
1044
  }
1021
1045
 
1022
1046
  self.setClasses();
1047
+ self.highlightFirstItem();
1023
1048
  });
1024
1049
 
1025
1050
  container.on('open', function () {
@@ -1497,6 +1522,12 @@ S2.define('select2/selection/single',[
1497
1522
  // User exits the container
1498
1523
  });
1499
1524
 
1525
+ container.on('focus', function (evt) {
1526
+ if (!container.isOpen()) {
1527
+ self.$selection.focus();
1528
+ }
1529
+ });
1530
+
1500
1531
  container.on('selection:update', function (params) {
1501
1532
  self.update(params.data);
1502
1533
  });
@@ -3436,6 +3467,12 @@ S2.define('select2/data/ajax',[
3436
3467
 
3437
3468
  callback(results);
3438
3469
  }, function () {
3470
+ // Attempt to detect if a request was aborted
3471
+ // Only works if the transport exposes a status property
3472
+ if ($request.status && $request.status === '0') {
3473
+ return;
3474
+ }
3475
+
3439
3476
  self.trigger('results:message', {
3440
3477
  message: 'errorLoading'
3441
3478
  });
@@ -3444,7 +3481,7 @@ S2.define('select2/data/ajax',[
3444
3481
  self._request = $request;
3445
3482
  }
3446
3483
 
3447
- if (this.ajaxOptions.delay && params.term !== '') {
3484
+ if (this.ajaxOptions.delay && params.term != null) {
3448
3485
  if (this._queryTimeout) {
3449
3486
  window.clearTimeout(this._queryTimeout);
3450
3487
  }
@@ -3607,6 +3644,29 @@ S2.define('select2/data/tokenizer',[
3607
3644
  Tokenizer.prototype.query = function (decorated, params, callback) {
3608
3645
  var self = this;
3609
3646
 
3647
+ function createAndSelect (data) {
3648
+ // Normalize the data object so we can use it for checks
3649
+ var item = self._normalizeItem(data);
3650
+
3651
+ // Check if the data object already exists as a tag
3652
+ // Select it if it doesn't
3653
+ var $existingOptions = self.$element.find('option').filter(function () {
3654
+ return $(this).val() === item.id;
3655
+ });
3656
+
3657
+ // If an existing option wasn't found for it, create the option
3658
+ if (!$existingOptions.length) {
3659
+ var $option = self.option(item);
3660
+ $option.attr('data-select2-tag', true);
3661
+
3662
+ self._removeOldTags();
3663
+ self.addOptions([$option]);
3664
+ }
3665
+
3666
+ // Select the item, now that we know there is an option for it
3667
+ select(item);
3668
+ }
3669
+
3610
3670
  function select (data) {
3611
3671
  self.trigger('select', {
3612
3672
  data: data
@@ -3615,7 +3675,7 @@ S2.define('select2/data/tokenizer',[
3615
3675
 
3616
3676
  params.term = params.term || '';
3617
3677
 
3618
- var tokenData = this.tokenizer(params, this.options, select);
3678
+ var tokenData = this.tokenizer(params, this.options, createAndSelect);
3619
3679
 
3620
3680
  if (tokenData.term !== params.term) {
3621
3681
  // Replace the search term if we have the search box
@@ -3880,6 +3940,12 @@ S2.define('select2/dropdown/search',[
3880
3940
  self.$search.val('');
3881
3941
  });
3882
3942
 
3943
+ container.on('focus', function () {
3944
+ if (container.isOpen()) {
3945
+ self.$search.focus();
3946
+ }
3947
+ });
3948
+
3883
3949
  container.on('results:all', function (params) {
3884
3950
  if (params.query.term == null || params.query.term === '') {
3885
3951
  var showSearch = self.showSearch(params);
@@ -4229,7 +4295,7 @@ S2.define('select2/dropdown/attachBody',[
4229
4295
 
4230
4296
  if (newDirection == 'above' ||
4231
4297
  (isCurrentlyAbove && newDirection !== 'below')) {
4232
- css.top = container.top - dropdown.height;
4298
+ css.top = container.top - parentOffset.top - dropdown.height;
4233
4299
  }
4234
4300
 
4235
4301
  if (newDirection != null) {
@@ -4251,6 +4317,7 @@ S2.define('select2/dropdown/attachBody',[
4251
4317
 
4252
4318
  if (this.options.get('dropdownAutoWidth')) {
4253
4319
  css.minWidth = css.width;
4320
+ css.position = 'relative';
4254
4321
  css.width = 'auto';
4255
4322
  }
4256
4323
 
@@ -4317,12 +4384,22 @@ S2.define('select2/dropdown/selectOnClose',[
4317
4384
 
4318
4385
  decorated.call(this, container, $container);
4319
4386
 
4320
- container.on('close', function () {
4321
- self._handleSelectOnClose();
4387
+ container.on('close', function (params) {
4388
+ self._handleSelectOnClose(params);
4322
4389
  });
4323
4390
  };
4324
4391
 
4325
- SelectOnClose.prototype._handleSelectOnClose = function () {
4392
+ SelectOnClose.prototype._handleSelectOnClose = function (_, params) {
4393
+ if (params && params.originalSelect2Event != null) {
4394
+ var event = params.originalSelect2Event;
4395
+
4396
+ // Don't select an item if the close event was triggered from a select or
4397
+ // unselect event
4398
+ if (event._type === 'select' || event._type === 'unselect') {
4399
+ return;
4400
+ }
4401
+ }
4402
+
4326
4403
  var $highlightedResults = this.getHighlightedResults();
4327
4404
 
4328
4405
  // Only select highlighted results
@@ -4375,7 +4452,10 @@ S2.define('select2/dropdown/closeOnSelect',[
4375
4452
  return;
4376
4453
  }
4377
4454
 
4378
- this.trigger('close', {});
4455
+ this.trigger('close', {
4456
+ originalEvent: originalEvent,
4457
+ originalSelect2Event: evt
4458
+ });
4379
4459
  };
4380
4460
 
4381
4461
  return CloseOnSelect;
@@ -5129,10 +5209,15 @@ S2.define('select2/core',[
5129
5209
  });
5130
5210
  });
5131
5211
 
5132
- this._sync = Utils.bind(this._syncAttributes, this);
5212
+ this.$element.on('focus.select2', function (evt) {
5213
+ self.trigger('focus', evt);
5214
+ });
5215
+
5216
+ this._syncA = Utils.bind(this._syncAttributes, this);
5217
+ this._syncS = Utils.bind(this._syncSubtree, this);
5133
5218
 
5134
5219
  if (this.$element[0].attachEvent) {
5135
- this.$element[0].attachEvent('onpropertychange', this._sync);
5220
+ this.$element[0].attachEvent('onpropertychange', this._syncA);
5136
5221
  }
5137
5222
 
5138
5223
  var observer = window.MutationObserver ||
@@ -5142,14 +5227,30 @@ S2.define('select2/core',[
5142
5227
 
5143
5228
  if (observer != null) {
5144
5229
  this._observer = new observer(function (mutations) {
5145
- $.each(mutations, self._sync);
5230
+ $.each(mutations, self._syncA);
5231
+ $.each(mutations, self._syncS);
5146
5232
  });
5147
5233
  this._observer.observe(this.$element[0], {
5148
5234
  attributes: true,
5235
+ childList: true,
5149
5236
  subtree: false
5150
5237
  });
5151
5238
  } else if (this.$element[0].addEventListener) {
5152
- this.$element[0].addEventListener('DOMAttrModified', self._sync, false);
5239
+ this.$element[0].addEventListener(
5240
+ 'DOMAttrModified',
5241
+ self._syncA,
5242
+ false
5243
+ );
5244
+ this.$element[0].addEventListener(
5245
+ 'DOMNodeInserted',
5246
+ self._syncS,
5247
+ false
5248
+ );
5249
+ this.$element[0].addEventListener(
5250
+ 'DOMNodeRemoved',
5251
+ self._syncS,
5252
+ false
5253
+ );
5153
5254
  }
5154
5255
  };
5155
5256
 
@@ -5294,6 +5395,46 @@ S2.define('select2/core',[
5294
5395
  }
5295
5396
  };
5296
5397
 
5398
+ Select2.prototype._syncSubtree = function (evt, mutations) {
5399
+ var changed = false;
5400
+ var self = this;
5401
+
5402
+ // Ignore any mutation events raised for elements that aren't options or
5403
+ // optgroups. This handles the case when the select element is destroyed
5404
+ if (
5405
+ evt && evt.target && (
5406
+ evt.target.nodeName !== 'OPTION' && evt.target.nodeName !== 'OPTGROUP'
5407
+ )
5408
+ ) {
5409
+ return;
5410
+ }
5411
+
5412
+ if (!mutations) {
5413
+ // If mutation events aren't supported, then we can only assume that the
5414
+ // change affected the selections
5415
+ changed = true;
5416
+ } else if (mutations.addedNodes && mutations.addedNodes.length > 0) {
5417
+ for (var n = 0; n < mutations.addedNodes.length; n++) {
5418
+ var node = mutations.addedNodes[n];
5419
+
5420
+ if (node.selected) {
5421
+ changed = true;
5422
+ }
5423
+ }
5424
+ } else if (mutations.removedNodes && mutations.removedNodes.length > 0) {
5425
+ changed = true;
5426
+ }
5427
+
5428
+ // Only re-pull the data if we think there is a change
5429
+ if (changed) {
5430
+ this.dataAdapter.current(function (currentData) {
5431
+ self.trigger('selection:update', {
5432
+ data: currentData
5433
+ });
5434
+ });
5435
+ }
5436
+ };
5437
+
5297
5438
  /**
5298
5439
  * Override the trigger method to automatically trigger pre-events when
5299
5440
  * there are events that can be prevented.
@@ -5440,7 +5581,7 @@ S2.define('select2/core',[
5440
5581
  this.$container.remove();
5441
5582
 
5442
5583
  if (this.$element[0].detachEvent) {
5443
- this.$element[0].detachEvent('onpropertychange', this._sync);
5584
+ this.$element[0].detachEvent('onpropertychange', this._syncA);
5444
5585
  }
5445
5586
 
5446
5587
  if (this._observer != null) {
@@ -5448,10 +5589,15 @@ S2.define('select2/core',[
5448
5589
  this._observer = null;
5449
5590
  } else if (this.$element[0].removeEventListener) {
5450
5591
  this.$element[0]
5451
- .removeEventListener('DOMAttrModified', this._sync, false);
5592
+ .removeEventListener('DOMAttrModified', this._syncA, false);
5593
+ this.$element[0]
5594
+ .removeEventListener('DOMNodeInserted', this._syncS, false);
5595
+ this.$element[0]
5596
+ .removeEventListener('DOMNodeRemoved', this._syncS, false);
5452
5597
  }
5453
5598
 
5454
- this._sync = null;
5599
+ this._syncA = null;
5600
+ this._syncS = null;
5455
5601
 
5456
5602
  this.$element.off('.select2');
5457
5603
  this.$element.attr('tabindex', this.$element.data('old-tabindex'));
@@ -5524,6 +5670,7 @@ S2.define('jquery.select2',[
5524
5670
  return this;
5525
5671
  } else if (typeof options === 'string') {
5526
5672
  var ret;
5673
+ var args = Array.prototype.slice.call(arguments, 1);
5527
5674
 
5528
5675
  this.each(function () {
5529
5676
  var instance = $(this).data('select2');
@@ -5535,8 +5682,6 @@ S2.define('jquery.select2',[
5535
5682
  );
5536
5683
  }
5537
5684
 
5538
- var args = Array.prototype.slice.call(arguments, 1);
5539
-
5540
5685
  ret = instance[options].apply(instance, args);
5541
5686
  });
5542
5687