select2-rails 4.0.2 → 4.0.3

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