select2-rails 4.0.0 → 4.0.4

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 (59) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +18 -2
  3. data/lib/select2-rails/source_file.rb +10 -8
  4. data/lib/select2-rails/version.rb +1 -1
  5. data/select2-rails.gemspec +1 -1
  6. data/vendor/assets/javascripts/select2-full.js +6457 -0
  7. data/vendor/assets/javascripts/select2.js +546 -203
  8. data/vendor/assets/javascripts/select2_locale_ar.js +3 -0
  9. data/vendor/assets/javascripts/select2_locale_az.js +1 -1
  10. data/vendor/assets/javascripts/select2_locale_bg.js +1 -1
  11. data/vendor/assets/javascripts/select2_locale_ca.js +1 -1
  12. data/vendor/assets/javascripts/select2_locale_cs.js +1 -1
  13. data/vendor/assets/javascripts/select2_locale_da.js +2 -2
  14. data/vendor/assets/javascripts/select2_locale_de.js +2 -2
  15. data/vendor/assets/javascripts/select2_locale_el.js +3 -0
  16. data/vendor/assets/javascripts/select2_locale_en.js +1 -1
  17. data/vendor/assets/javascripts/select2_locale_es.js +1 -1
  18. data/vendor/assets/javascripts/select2_locale_et.js +1 -1
  19. data/vendor/assets/javascripts/select2_locale_eu.js +1 -1
  20. data/vendor/assets/javascripts/select2_locale_fa.js +1 -1
  21. data/vendor/assets/javascripts/select2_locale_fi.js +2 -2
  22. data/vendor/assets/javascripts/select2_locale_fr.js +2 -2
  23. data/vendor/assets/javascripts/select2_locale_gl.js +2 -2
  24. data/vendor/assets/javascripts/select2_locale_he.js +2 -2
  25. data/vendor/assets/javascripts/select2_locale_hi.js +1 -1
  26. data/vendor/assets/javascripts/select2_locale_hr.js +2 -2
  27. data/vendor/assets/javascripts/select2_locale_hu.js +2 -2
  28. data/vendor/assets/javascripts/select2_locale_hy.js +3 -0
  29. data/vendor/assets/javascripts/select2_locale_id.js +2 -2
  30. data/vendor/assets/javascripts/select2_locale_is.js +1 -1
  31. data/vendor/assets/javascripts/select2_locale_it.js +1 -1
  32. data/vendor/assets/javascripts/select2_locale_ja.js +3 -0
  33. data/vendor/assets/javascripts/select2_locale_km.js +3 -0
  34. data/vendor/assets/javascripts/select2_locale_ko.js +1 -1
  35. data/vendor/assets/javascripts/select2_locale_lt.js +2 -2
  36. data/vendor/assets/javascripts/select2_locale_lv.js +1 -1
  37. data/vendor/assets/javascripts/select2_locale_mk.js +1 -1
  38. data/vendor/assets/javascripts/select2_locale_ms.js +3 -0
  39. data/vendor/assets/javascripts/select2_locale_nb.js +2 -2
  40. data/vendor/assets/javascripts/select2_locale_nl.js +2 -2
  41. data/vendor/assets/javascripts/select2_locale_pl.js +2 -2
  42. data/vendor/assets/javascripts/select2_locale_pt-BR.js +1 -1
  43. data/vendor/assets/javascripts/select2_locale_pt.js +2 -2
  44. data/vendor/assets/javascripts/select2_locale_ro.js +2 -2
  45. data/vendor/assets/javascripts/select2_locale_ru.js +1 -1
  46. data/vendor/assets/javascripts/select2_locale_sk.js +1 -1
  47. data/vendor/assets/javascripts/select2_locale_sl.js +3 -0
  48. data/vendor/assets/javascripts/select2_locale_sr-Cyrl.js +3 -0
  49. data/vendor/assets/javascripts/select2_locale_sr.js +2 -2
  50. data/vendor/assets/javascripts/select2_locale_sv.js +1 -1
  51. data/vendor/assets/javascripts/select2_locale_th.js +2 -2
  52. data/vendor/assets/javascripts/select2_locale_tr.js +2 -2
  53. data/vendor/assets/javascripts/select2_locale_uk.js +1 -1
  54. data/vendor/assets/javascripts/select2_locale_vi.js +1 -1
  55. data/vendor/assets/javascripts/select2_locale_zh-CN.js +1 -1
  56. data/vendor/assets/javascripts/select2_locale_zh-TW.js +1 -1
  57. data/vendor/assets/stylesheets/select2-bootstrap.css +90 -44
  58. data/vendor/assets/stylesheets/select2.css +79 -26
  59. metadata +15 -7
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Select2 4.0.0
2
+ * Select2 4.0.4
3
3
  * https://select2.github.io
4
4
  *
5
5
  * Released under the MIT license
@@ -9,19 +9,33 @@
9
9
  if (typeof define === 'function' && define.amd) {
10
10
  // AMD. Register as an anonymous module.
11
11
  define(['jquery'], factory);
12
- } else if (typeof exports === 'object') {
12
+ } else if (typeof module === 'object' && module.exports) {
13
13
  // Node/CommonJS
14
- factory(require('jquery'));
14
+ module.exports = function (root, jQuery) {
15
+ if (jQuery === undefined) {
16
+ // require('jQuery') returns a factory that requires window to
17
+ // build a jQuery instance, we normalize how we use modules
18
+ // that require this pattern but the window provided is a noop
19
+ // if it's defined (how jquery works)
20
+ if (typeof window !== 'undefined') {
21
+ jQuery = require('jquery');
22
+ }
23
+ else {
24
+ jQuery = require('jquery')(root);
25
+ }
26
+ }
27
+ factory(jQuery);
28
+ return jQuery;
29
+ };
15
30
  } else {
16
31
  // Browser globals
17
32
  factory(jQuery);
18
33
  }
19
- }(function (jQuery) {
34
+ } (function (jQuery) {
20
35
  // This is needed so we can catch the AMD loader configuration and use it
21
36
  // The inner file should be wrapped (by `banner.start.js`) in a function that
22
37
  // returns the AMD loader references.
23
- var S2 =
24
- (function () {
38
+ var S2 =(function () {
25
39
  // Restore the Select2 AMD loader so it can be used
26
40
  // Needed mostly in the language files, where the loader is not inserted
27
41
  if (jQuery && jQuery.fn && jQuery.fn.select2 && jQuery.fn.select2.amd) {
@@ -30,13 +44,11 @@
30
44
  var S2;(function () { if (!S2 || !S2.requirejs) {
31
45
  if (!S2) { S2 = {}; } else { require = S2; }
32
46
  /**
33
- * @license almond 0.2.9 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved.
34
- * Available via the MIT or new BSD license.
35
- * see: http://github.com/jrburke/almond for details
47
+ * @license almond 0.3.3 Copyright jQuery Foundation and other contributors.
48
+ * Released under MIT license, http://github.com/requirejs/almond/LICENSE
36
49
  */
37
50
  //Going sloppy to avoid 'use strict' string cost, but strict practices should
38
51
  //be followed.
39
- /*jslint sloppy: true */
40
52
  /*global setTimeout: false */
41
53
 
42
54
  var requirejs, require, define;
@@ -64,62 +76,58 @@ var requirejs, require, define;
64
76
  */
65
77
  function normalize(name, baseName) {
66
78
  var nameParts, nameSegment, mapValue, foundMap, lastIndex,
67
- foundI, foundStarMap, starI, i, j, part,
79
+ foundI, foundStarMap, starI, i, j, part, normalizedBaseParts,
68
80
  baseParts = baseName && baseName.split("/"),
69
81
  map = config.map,
70
82
  starMap = (map && map['*']) || {};
71
83
 
72
84
  //Adjust any relative paths.
73
- if (name && name.charAt(0) === ".") {
74
- //If have a base name, try to normalize against it,
75
- //otherwise, assume it is a top-level require that will
76
- //be relative to baseUrl in the end.
77
- if (baseName) {
85
+ if (name) {
86
+ name = name.split('/');
87
+ lastIndex = name.length - 1;
88
+
89
+ // If wanting node ID compatibility, strip .js from end
90
+ // of IDs. Have to do this here, and not in nameToUrl
91
+ // because node allows either .js or non .js to map
92
+ // to same file.
93
+ if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
94
+ name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
95
+ }
96
+
97
+ // Starts with a '.' so need the baseName
98
+ if (name[0].charAt(0) === '.' && baseParts) {
78
99
  //Convert baseName to array, and lop off the last part,
79
- //so that . matches that "directory" and not name of the baseName's
80
- //module. For instance, baseName of "one/two/three", maps to
81
- //"one/two/three.js", but we want the directory, "one/two" for
100
+ //so that . matches that 'directory' and not name of the baseName's
101
+ //module. For instance, baseName of 'one/two/three', maps to
102
+ //'one/two/three.js', but we want the directory, 'one/two' for
82
103
  //this normalization.
83
- baseParts = baseParts.slice(0, baseParts.length - 1);
84
- name = name.split('/');
85
- lastIndex = name.length - 1;
86
-
87
- // Node .js allowance:
88
- if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
89
- name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
90
- }
104
+ normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
105
+ name = normalizedBaseParts.concat(name);
106
+ }
91
107
 
92
- name = baseParts.concat(name);
93
-
94
- //start trimDots
95
- for (i = 0; i < name.length; i += 1) {
96
- part = name[i];
97
- if (part === ".") {
98
- name.splice(i, 1);
99
- i -= 1;
100
- } else if (part === "..") {
101
- if (i === 1 && (name[2] === '..' || name[0] === '..')) {
102
- //End of the line. Keep at least one non-dot
103
- //path segment at the front so it can be mapped
104
- //correctly to disk. Otherwise, there is likely
105
- //no path mapping for a path starting with '..'.
106
- //This can still fail, but catches the most reasonable
107
- //uses of ..
108
- break;
109
- } else if (i > 0) {
110
- name.splice(i - 1, 2);
111
- i -= 2;
112
- }
108
+ //start trimDots
109
+ for (i = 0; i < name.length; i++) {
110
+ part = name[i];
111
+ if (part === '.') {
112
+ name.splice(i, 1);
113
+ i -= 1;
114
+ } else if (part === '..') {
115
+ // If at the start, or previous value is still ..,
116
+ // keep them so that when converted to a path it may
117
+ // still work when converted to a path, even though
118
+ // as an ID it is less than ideal. In larger point
119
+ // releases, may be better to just kick out an error.
120
+ if (i === 0 || (i === 1 && name[2] === '..') || name[i - 1] === '..') {
121
+ continue;
122
+ } else if (i > 0) {
123
+ name.splice(i - 1, 2);
124
+ i -= 2;
113
125
  }
114
126
  }
115
- //end trimDots
116
-
117
- name = name.join("/");
118
- } else if (name.indexOf('./') === 0) {
119
- // No baseName, so this is ID is resolved relative
120
- // to baseUrl, pull off the leading dot.
121
- name = name.substring(2);
122
127
  }
128
+ //end trimDots
129
+
130
+ name = name.join('/');
123
131
  }
124
132
 
125
133
  //Apply map config if available.
@@ -181,7 +189,15 @@ var requirejs, require, define;
181
189
  //A version of a require function that passes a moduleName
182
190
  //value for items that may need to
183
191
  //look up paths relative to the moduleName
184
- return req.apply(undef, aps.call(arguments, 0).concat([relName, forceSync]));
192
+ var args = aps.call(arguments, 0);
193
+
194
+ //If first arg is not require('string'), and there is only
195
+ //one arg, it is the array form without a callback. Insert
196
+ //a null so that the following concat is correct.
197
+ if (typeof args[0] !== 'string' && args.length === 1) {
198
+ args.push(null);
199
+ }
200
+ return req.apply(undef, args.concat([relName, forceSync]));
185
201
  };
186
202
  }
187
203
 
@@ -224,32 +240,39 @@ var requirejs, require, define;
224
240
  return [prefix, name];
225
241
  }
226
242
 
243
+ //Creates a parts array for a relName where first part is plugin ID,
244
+ //second part is resource ID. Assumes relName has already been normalized.
245
+ function makeRelParts(relName) {
246
+ return relName ? splitPrefix(relName) : [];
247
+ }
248
+
227
249
  /**
228
250
  * Makes a name map, normalizing the name, and using a plugin
229
251
  * for normalization if necessary. Grabs a ref to plugin
230
252
  * too, as an optimization.
231
253
  */
232
- makeMap = function (name, relName) {
254
+ makeMap = function (name, relParts) {
233
255
  var plugin,
234
256
  parts = splitPrefix(name),
235
- prefix = parts[0];
257
+ prefix = parts[0],
258
+ relResourceName = relParts[1];
236
259
 
237
260
  name = parts[1];
238
261
 
239
262
  if (prefix) {
240
- prefix = normalize(prefix, relName);
263
+ prefix = normalize(prefix, relResourceName);
241
264
  plugin = callDep(prefix);
242
265
  }
243
266
 
244
267
  //Normalize according
245
268
  if (prefix) {
246
269
  if (plugin && plugin.normalize) {
247
- name = plugin.normalize(name, makeNormalize(relName));
270
+ name = plugin.normalize(name, makeNormalize(relResourceName));
248
271
  } else {
249
- name = normalize(name, relName);
272
+ name = normalize(name, relResourceName);
250
273
  }
251
274
  } else {
252
- name = normalize(name, relName);
275
+ name = normalize(name, relResourceName);
253
276
  parts = splitPrefix(name);
254
277
  prefix = parts[0];
255
278
  name = parts[1];
@@ -296,13 +319,14 @@ var requirejs, require, define;
296
319
  };
297
320
 
298
321
  main = function (name, deps, callback, relName) {
299
- var cjsModule, depName, ret, map, i,
322
+ var cjsModule, depName, ret, map, i, relParts,
300
323
  args = [],
301
324
  callbackType = typeof callback,
302
325
  usingExports;
303
326
 
304
327
  //Use name if no relName
305
328
  relName = relName || name;
329
+ relParts = makeRelParts(relName);
306
330
 
307
331
  //Call the callback to define the module, if necessary.
308
332
  if (callbackType === 'undefined' || callbackType === 'function') {
@@ -311,7 +335,7 @@ var requirejs, require, define;
311
335
  //Default to [require, exports, module] if no deps
312
336
  deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
313
337
  for (i = 0; i < deps.length; i += 1) {
314
- map = makeMap(deps[i], relName);
338
+ map = makeMap(deps[i], relParts);
315
339
  depName = map.f;
316
340
 
317
341
  //Fast path CommonJS standard dependencies.
@@ -367,7 +391,7 @@ var requirejs, require, define;
367
391
  //deps arg is the module name, and second arg (if passed)
368
392
  //is just the relName.
369
393
  //Normalize module name, if it contains . or ..
370
- return callDep(makeMap(deps, callback).f);
394
+ return callDep(makeMap(deps, makeRelParts(callback)).f);
371
395
  } else if (!deps.splice) {
372
396
  //deps is a config object, not an array.
373
397
  config = deps;
@@ -431,6 +455,9 @@ var requirejs, require, define;
431
455
  requirejs._defined = defined;
432
456
 
433
457
  define = function (name, deps, callback) {
458
+ if (typeof name !== 'string') {
459
+ throw new Error('See almond README: incorrect module build, no module name');
460
+ }
434
461
 
435
462
  //This module may not have dependencies
436
463
  if (!deps.splice) {
@@ -597,9 +624,23 @@ S2.define('select2/utils',[
597
624
 
598
625
  Observable.prototype.trigger = function (event) {
599
626
  var slice = Array.prototype.slice;
627
+ var params = slice.call(arguments, 1);
600
628
 
601
629
  this.listeners = this.listeners || {};
602
630
 
631
+ // Params should always come in as an array
632
+ if (params == null) {
633
+ params = [];
634
+ }
635
+
636
+ // If there are no arguments to the event, use a temporary object
637
+ if (params.length === 0) {
638
+ params.push({});
639
+ }
640
+
641
+ // Set the `_type` of the first object to the event
642
+ params[0]._type = event;
643
+
603
644
  if (event in this.listeners) {
604
645
  this.invoke(this.listeners[event], slice.call(arguments, 1));
605
646
  }
@@ -773,7 +814,8 @@ S2.define('select2/results',[
773
814
  this.hideLoading();
774
815
 
775
816
  var $message = $(
776
- '<li role="treeitem" class="select2-results__option"></li>'
817
+ '<li role="treeitem" aria-live="assertive"' +
818
+ ' class="select2-results__option"></li>'
777
819
  );
778
820
 
779
821
  var message = this.options.get('translations').get(params.message);
@@ -784,9 +826,15 @@ S2.define('select2/results',[
784
826
  )
785
827
  );
786
828
 
829
+ $message[0].className += ' select2-results__message';
830
+
787
831
  this.$results.append($message);
788
832
  };
789
833
 
834
+ Results.prototype.hideMessages = function () {
835
+ this.$results.find('.select2-results__message').remove();
836
+ };
837
+
790
838
  Results.prototype.append = function (data) {
791
839
  this.hideLoading();
792
840
 
@@ -826,6 +874,25 @@ S2.define('select2/results',[
826
874
  return sorter(data);
827
875
  };
828
876
 
877
+ Results.prototype.highlightFirstItem = function () {
878
+ var $options = this.$results
879
+ .find('.select2-results__option[aria-selected]');
880
+
881
+ var $selected = $options.filter('[aria-selected=true]');
882
+
883
+ // Check if there are any selected options
884
+ if ($selected.length > 0) {
885
+ // If there are selected options, highlight the first
886
+ $selected.first().trigger('mouseenter');
887
+ } else {
888
+ // If there are no selected options, highlight the first option
889
+ // in the dropdown
890
+ $options.first().trigger('mouseenter');
891
+ }
892
+
893
+ this.ensureHighlightVisible();
894
+ };
895
+
829
896
  Results.prototype.setClasses = function () {
830
897
  var self = this;
831
898
 
@@ -853,17 +920,6 @@ S2.define('select2/results',[
853
920
  }
854
921
  });
855
922
 
856
- var $selected = $options.filter('[aria-selected=true]');
857
-
858
- // Check if there are any selected options
859
- if ($selected.length > 0) {
860
- // If there are selected options, highlight the first
861
- $selected.first().trigger('mouseenter');
862
- } else {
863
- // If there are no selected options, highlight the first option
864
- // in the dropdown
865
- $options.first().trigger('mouseenter');
866
- }
867
923
  });
868
924
  };
869
925
 
@@ -974,6 +1030,7 @@ S2.define('select2/results',[
974
1030
 
975
1031
  if (container.isOpen()) {
976
1032
  self.setClasses();
1033
+ self.highlightFirstItem();
977
1034
  }
978
1035
  });
979
1036
 
@@ -986,6 +1043,7 @@ S2.define('select2/results',[
986
1043
  });
987
1044
 
988
1045
  container.on('query', function (params) {
1046
+ self.hideMessages();
989
1047
  self.showLoading(params);
990
1048
  });
991
1049
 
@@ -995,6 +1053,7 @@ S2.define('select2/results',[
995
1053
  }
996
1054
 
997
1055
  self.setClasses();
1056
+ self.highlightFirstItem();
998
1057
  });
999
1058
 
1000
1059
  container.on('unselect', function () {
@@ -1003,6 +1062,7 @@ S2.define('select2/results',[
1003
1062
  }
1004
1063
 
1005
1064
  self.setClasses();
1065
+ self.highlightFirstItem();
1006
1066
  });
1007
1067
 
1008
1068
  container.on('open', function () {
@@ -1041,7 +1101,7 @@ S2.define('select2/results',[
1041
1101
  var data = $highlighted.data('data');
1042
1102
 
1043
1103
  if ($highlighted.attr('aria-selected') == 'true') {
1044
- self.trigger('close');
1104
+ self.trigger('close', {});
1045
1105
  } else {
1046
1106
  self.trigger('select', {
1047
1107
  data: data
@@ -1125,11 +1185,7 @@ S2.define('select2/results',[
1125
1185
  this.$results.on('mousewheel', function (e) {
1126
1186
  var top = self.$results.scrollTop();
1127
1187
 
1128
- var bottom = (
1129
- self.$results.get(0).scrollHeight -
1130
- self.$results.scrollTop() +
1131
- e.deltaY
1132
- );
1188
+ var bottom = self.$results.get(0).scrollHeight - top + e.deltaY;
1133
1189
 
1134
1190
  var isAtTop = e.deltaY > 0 && top - e.deltaY <= 0;
1135
1191
  var isAtBottom = e.deltaY < 0 && bottom <= self.$results.height();
@@ -1163,7 +1219,7 @@ S2.define('select2/results',[
1163
1219
  data: data
1164
1220
  });
1165
1221
  } else {
1166
- self.trigger('close');
1222
+ self.trigger('close', {});
1167
1223
  }
1168
1224
 
1169
1225
  return;
@@ -1229,7 +1285,7 @@ S2.define('select2/results',[
1229
1285
  var template = this.options.get('templateResult');
1230
1286
  var escapeMarkup = this.options.get('escapeMarkup');
1231
1287
 
1232
- var content = template(result);
1288
+ var content = template(result, container);
1233
1289
 
1234
1290
  if (content == null) {
1235
1291
  container.style.display = 'none';
@@ -1286,7 +1342,7 @@ S2.define('select2/selection/base',[
1286
1342
  BaseSelection.prototype.render = function () {
1287
1343
  var $selection = $(
1288
1344
  '<span class="select2-selection" role="combobox" ' +
1289
- 'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
1345
+ ' aria-haspopup="true" aria-expanded="false">' +
1290
1346
  '</span>'
1291
1347
  );
1292
1348
 
@@ -1319,7 +1375,7 @@ S2.define('select2/selection/base',[
1319
1375
  });
1320
1376
 
1321
1377
  this.$selection.on('blur', function (evt) {
1322
- self.trigger('blur', evt);
1378
+ self._handleBlur(evt);
1323
1379
  });
1324
1380
 
1325
1381
  this.$selection.on('keydown', function (evt) {
@@ -1366,6 +1422,24 @@ S2.define('select2/selection/base',[
1366
1422
  });
1367
1423
  };
1368
1424
 
1425
+ BaseSelection.prototype._handleBlur = function (evt) {
1426
+ var self = this;
1427
+
1428
+ // This needs to be delayed as the active element is the body when the tab
1429
+ // key is pressed, possibly along with others.
1430
+ window.setTimeout(function () {
1431
+ // Don't trigger `blur` if the focus is still in the selection
1432
+ if (
1433
+ (document.activeElement == self.$selection[0]) ||
1434
+ ($.contains(self.$selection[0], document.activeElement))
1435
+ ) {
1436
+ return;
1437
+ }
1438
+
1439
+ self.trigger('blur', evt);
1440
+ }, 1);
1441
+ };
1442
+
1369
1443
  BaseSelection.prototype._attachCloseHandler = function (container) {
1370
1444
  var self = this;
1371
1445
 
@@ -1466,6 +1540,12 @@ S2.define('select2/selection/single',[
1466
1540
  // User exits the container
1467
1541
  });
1468
1542
 
1543
+ container.on('focus', function (evt) {
1544
+ if (!container.isOpen()) {
1545
+ self.$selection.focus();
1546
+ }
1547
+ });
1548
+
1469
1549
  container.on('selection:update', function (params) {
1470
1550
  self.update(params.data);
1471
1551
  });
@@ -1475,11 +1555,11 @@ S2.define('select2/selection/single',[
1475
1555
  this.$selection.find('.select2-selection__rendered').empty();
1476
1556
  };
1477
1557
 
1478
- SingleSelection.prototype.display = function (data) {
1558
+ SingleSelection.prototype.display = function (data, container) {
1479
1559
  var template = this.options.get('templateSelection');
1480
1560
  var escapeMarkup = this.options.get('escapeMarkup');
1481
1561
 
1482
- return escapeMarkup(template(data));
1562
+ return escapeMarkup(template(data, container));
1483
1563
  };
1484
1564
 
1485
1565
  SingleSelection.prototype.selectionContainer = function () {
@@ -1494,9 +1574,9 @@ S2.define('select2/selection/single',[
1494
1574
 
1495
1575
  var selection = data[0];
1496
1576
 
1497
- var formatted = this.display(selection);
1498
-
1499
1577
  var $rendered = this.$selection.find('.select2-selection__rendered');
1578
+ var formatted = this.display(selection, $rendered);
1579
+
1500
1580
  $rendered.empty().append(formatted);
1501
1581
  $rendered.prop('title', selection.title || selection.text);
1502
1582
  };
@@ -1538,29 +1618,37 @@ S2.define('select2/selection/multiple',[
1538
1618
  });
1539
1619
  });
1540
1620
 
1541
- this.$selection.on('click', '.select2-selection__choice__remove',
1621
+ this.$selection.on(
1622
+ 'click',
1623
+ '.select2-selection__choice__remove',
1542
1624
  function (evt) {
1543
- var $remove = $(this);
1544
- var $selection = $remove.parent();
1625
+ // Ignore the event if it is disabled
1626
+ if (self.options.get('disabled')) {
1627
+ return;
1628
+ }
1545
1629
 
1546
- var data = $selection.data('data');
1630
+ var $remove = $(this);
1631
+ var $selection = $remove.parent();
1547
1632
 
1548
- self.trigger('unselect', {
1549
- originalEvent: evt,
1550
- data: data
1551
- });
1552
- });
1633
+ var data = $selection.data('data');
1634
+
1635
+ self.trigger('unselect', {
1636
+ originalEvent: evt,
1637
+ data: data
1638
+ });
1639
+ }
1640
+ );
1553
1641
  };
1554
1642
 
1555
1643
  MultipleSelection.prototype.clear = function () {
1556
1644
  this.$selection.find('.select2-selection__rendered').empty();
1557
1645
  };
1558
1646
 
1559
- MultipleSelection.prototype.display = function (data) {
1647
+ MultipleSelection.prototype.display = function (data, container) {
1560
1648
  var template = this.options.get('templateSelection');
1561
1649
  var escapeMarkup = this.options.get('escapeMarkup');
1562
1650
 
1563
- return escapeMarkup(template(data));
1651
+ return escapeMarkup(template(data, container));
1564
1652
  };
1565
1653
 
1566
1654
  MultipleSelection.prototype.selectionContainer = function () {
@@ -1587,8 +1675,8 @@ S2.define('select2/selection/multiple',[
1587
1675
  for (var d = 0; d < data.length; d++) {
1588
1676
  var selection = data[d];
1589
1677
 
1590
- var formatted = this.display(selection);
1591
1678
  var $selection = this.selectionContainer();
1679
+ var formatted = this.display(selection, $selection);
1592
1680
 
1593
1681
  $selection.append(formatted);
1594
1682
  $selection.prop('title', selection.title || selection.text);
@@ -1720,7 +1808,7 @@ S2.define('select2/selection/allowClear',[
1720
1808
 
1721
1809
  this.$element.val(this.placeholder.id).trigger('change');
1722
1810
 
1723
- this.trigger('toggle');
1811
+ this.trigger('toggle', {});
1724
1812
  };
1725
1813
 
1726
1814
  AllowClear.prototype._handleKeyboardClear = function (_, evt, container) {
@@ -1768,7 +1856,7 @@ S2.define('select2/selection/search',[
1768
1856
  '<li class="select2-search select2-search--inline">' +
1769
1857
  '<input class="select2-search__field" type="search" tabindex="-1"' +
1770
1858
  ' autocomplete="off" autocorrect="off" autocapitalize="off"' +
1771
- ' spellcheck="false" role="textbox" />' +
1859
+ ' spellcheck="false" role="textbox" aria-autocomplete="list" />' +
1772
1860
  '</li>'
1773
1861
  );
1774
1862
 
@@ -1777,6 +1865,8 @@ S2.define('select2/selection/search',[
1777
1865
 
1778
1866
  var $rendered = decorated.call(this);
1779
1867
 
1868
+ this._transferTabIndex();
1869
+
1780
1870
  return $rendered;
1781
1871
  };
1782
1872
 
@@ -1786,32 +1876,39 @@ S2.define('select2/selection/search',[
1786
1876
  decorated.call(this, container, $container);
1787
1877
 
1788
1878
  container.on('open', function () {
1789
- self.$search.attr('tabindex', 0);
1790
-
1791
- self.$search.focus();
1879
+ self.$search.trigger('focus');
1792
1880
  });
1793
1881
 
1794
1882
  container.on('close', function () {
1795
- self.$search.attr('tabindex', -1);
1796
-
1797
1883
  self.$search.val('');
1798
- self.$search.focus();
1884
+ self.$search.removeAttr('aria-activedescendant');
1885
+ self.$search.trigger('focus');
1799
1886
  });
1800
1887
 
1801
1888
  container.on('enable', function () {
1802
1889
  self.$search.prop('disabled', false);
1890
+
1891
+ self._transferTabIndex();
1803
1892
  });
1804
1893
 
1805
1894
  container.on('disable', function () {
1806
1895
  self.$search.prop('disabled', true);
1807
1896
  });
1808
1897
 
1898
+ container.on('focus', function (evt) {
1899
+ self.$search.trigger('focus');
1900
+ });
1901
+
1902
+ container.on('results:focus', function (params) {
1903
+ self.$search.attr('aria-activedescendant', params.id);
1904
+ });
1905
+
1809
1906
  this.$selection.on('focusin', '.select2-search--inline', function (evt) {
1810
1907
  self.trigger('focus', evt);
1811
1908
  });
1812
1909
 
1813
1910
  this.$selection.on('focusout', '.select2-search--inline', function (evt) {
1814
- self.trigger('blur', evt);
1911
+ self._handleBlur(evt);
1815
1912
  });
1816
1913
 
1817
1914
  this.$selection.on('keydown', '.select2-search--inline', function (evt) {
@@ -1837,18 +1934,73 @@ S2.define('select2/selection/search',[
1837
1934
  }
1838
1935
  });
1839
1936
 
1937
+ // Try to detect the IE version should the `documentMode` property that
1938
+ // is stored on the document. This is only implemented in IE and is
1939
+ // slightly cleaner than doing a user agent check.
1940
+ // This property is not available in Edge, but Edge also doesn't have
1941
+ // this bug.
1942
+ var msie = document.documentMode;
1943
+ var disableInputEvents = msie && msie <= 11;
1944
+
1840
1945
  // Workaround for browsers which do not support the `input` event
1841
1946
  // This will prevent double-triggering of events for browsers which support
1842
1947
  // both the `keyup` and `input` events.
1843
- this.$selection.on('input', '.select2-search--inline', function (evt) {
1844
- // Unbind the duplicated `keyup` event
1845
- self.$selection.off('keyup.search');
1846
- });
1948
+ this.$selection.on(
1949
+ 'input.searchcheck',
1950
+ '.select2-search--inline',
1951
+ function (evt) {
1952
+ // IE will trigger the `input` event when a placeholder is used on a
1953
+ // search box. To get around this issue, we are forced to ignore all
1954
+ // `input` events in IE and keep using `keyup`.
1955
+ if (disableInputEvents) {
1956
+ self.$selection.off('input.search input.searchcheck');
1957
+ return;
1958
+ }
1847
1959
 
1848
- this.$selection.on('keyup.search input', '.select2-search--inline',
1849
- function (evt) {
1850
- self.handleSearch(evt);
1851
- });
1960
+ // Unbind the duplicated `keyup` event
1961
+ self.$selection.off('keyup.search');
1962
+ }
1963
+ );
1964
+
1965
+ this.$selection.on(
1966
+ 'keyup.search input.search',
1967
+ '.select2-search--inline',
1968
+ function (evt) {
1969
+ // IE will trigger the `input` event when a placeholder is used on a
1970
+ // search box. To get around this issue, we are forced to ignore all
1971
+ // `input` events in IE and keep using `keyup`.
1972
+ if (disableInputEvents && evt.type === 'input') {
1973
+ self.$selection.off('input.search input.searchcheck');
1974
+ return;
1975
+ }
1976
+
1977
+ var key = evt.which;
1978
+
1979
+ // We can freely ignore events from modifier keys
1980
+ if (key == KEYS.SHIFT || key == KEYS.CTRL || key == KEYS.ALT) {
1981
+ return;
1982
+ }
1983
+
1984
+ // Tabbing will be handled during the `keydown` phase
1985
+ if (key == KEYS.TAB) {
1986
+ return;
1987
+ }
1988
+
1989
+ self.handleSearch(evt);
1990
+ }
1991
+ );
1992
+ };
1993
+
1994
+ /**
1995
+ * This method will transfer the tabindex attribute from the rendered
1996
+ * selection to the search box. This allows for the search box to be used as
1997
+ * the primary focus instead of the selection container.
1998
+ *
1999
+ * @private
2000
+ */
2001
+ Search.prototype._transferTabIndex = function (decorated) {
2002
+ this.$search.attr('tabindex', this.$selection.attr('tabindex'));
2003
+ this.$selection.attr('tabindex', '-1');
1852
2004
  };
1853
2005
 
1854
2006
  Search.prototype.createPlaceholder = function (decorated, placeholder) {
@@ -1856,6 +2008,8 @@ S2.define('select2/selection/search',[
1856
2008
  };
1857
2009
 
1858
2010
  Search.prototype.update = function (decorated, data) {
2011
+ var searchHadFocus = this.$search[0] == document.activeElement;
2012
+
1859
2013
  this.$search.attr('placeholder', '');
1860
2014
 
1861
2015
  decorated.call(this, data);
@@ -1864,6 +2018,9 @@ S2.define('select2/selection/search',[
1864
2018
  .append(this.$searchContainer);
1865
2019
 
1866
2020
  this.resizeSearch();
2021
+ if (searchHadFocus) {
2022
+ this.$search.focus();
2023
+ }
1867
2024
  };
1868
2025
 
1869
2026
  Search.prototype.handleSearch = function () {
@@ -1885,9 +2042,8 @@ S2.define('select2/selection/search',[
1885
2042
  data: item
1886
2043
  });
1887
2044
 
1888
- this.trigger('open');
1889
-
1890
- this.$search.val(item.text + ' ');
2045
+ this.$search.val(item.text);
2046
+ this.handleSearch();
1891
2047
  };
1892
2048
 
1893
2049
  Search.prototype.resizeSearch = function () {
@@ -3053,7 +3209,7 @@ S2.define('select2/data/select',[
3053
3209
  }
3054
3210
  }
3055
3211
 
3056
- if (data.id) {
3212
+ if (data.id !== undefined) {
3057
3213
  option.value = data.id;
3058
3214
  }
3059
3215
 
@@ -3221,9 +3377,9 @@ S2.define('select2/data/array',[
3221
3377
  var $existingOption = $existing.filter(onlyItem(item));
3222
3378
 
3223
3379
  var existingData = this.item($existingOption);
3224
- var newData = $.extend(true, {}, existingData, item);
3380
+ var newData = $.extend(true, {}, item, existingData);
3225
3381
 
3226
- var $newOption = this.option(existingData);
3382
+ var $newOption = this.option(newData);
3227
3383
 
3228
3384
  $existingOption.replaceWith($newOption);
3229
3385
 
@@ -3259,7 +3415,7 @@ S2.define('select2/data/ajax',[
3259
3415
  this.processResults = this.ajaxOptions.processResults;
3260
3416
  }
3261
3417
 
3262
- ArrayAdapter.__super__.constructor.call(this, $element, options);
3418
+ AjaxAdapter.__super__.constructor.call(this, $element, options);
3263
3419
  }
3264
3420
 
3265
3421
  Utils.Extend(AjaxAdapter, ArrayAdapter);
@@ -3267,9 +3423,9 @@ S2.define('select2/data/ajax',[
3267
3423
  AjaxAdapter.prototype._applyDefaults = function (options) {
3268
3424
  var defaults = {
3269
3425
  data: function (params) {
3270
- return {
3426
+ return $.extend({}, params, {
3271
3427
  q: params.term
3272
- };
3428
+ });
3273
3429
  },
3274
3430
  transport: function (params, success, failure) {
3275
3431
  var $request = $.ajax(params);
@@ -3306,11 +3462,11 @@ S2.define('select2/data/ajax',[
3306
3462
  }, this.ajaxOptions);
3307
3463
 
3308
3464
  if (typeof options.url === 'function') {
3309
- options.url = options.url(params);
3465
+ options.url = options.url.call(this.$element, params);
3310
3466
  }
3311
3467
 
3312
3468
  if (typeof options.data === 'function') {
3313
- options.data = options.data(params);
3469
+ options.data = options.data.call(this.$element, params);
3314
3470
  }
3315
3471
 
3316
3472
  function request () {
@@ -3329,13 +3485,21 @@ S2.define('select2/data/ajax',[
3329
3485
 
3330
3486
  callback(results);
3331
3487
  }, function () {
3332
- // TODO: Handle AJAX errors
3488
+ // Attempt to detect if a request was aborted
3489
+ // Only works if the transport exposes a status property
3490
+ if ($request.status && $request.status === '0') {
3491
+ return;
3492
+ }
3493
+
3494
+ self.trigger('results:message', {
3495
+ message: 'errorLoading'
3496
+ });
3333
3497
  });
3334
3498
 
3335
3499
  self._request = $request;
3336
3500
  }
3337
3501
 
3338
- if (this.ajaxOptions.delay && params.term !== '') {
3502
+ if (this.ajaxOptions.delay && params.term != null) {
3339
3503
  if (this._queryTimeout) {
3340
3504
  window.clearTimeout(this._queryTimeout);
3341
3505
  }
@@ -3361,6 +3525,12 @@ S2.define('select2/data/tags',[
3361
3525
  this.createTag = createTag;
3362
3526
  }
3363
3527
 
3528
+ var insertTag = options.get('insertTag');
3529
+
3530
+ if (insertTag !== undefined) {
3531
+ this.insertTag = insertTag;
3532
+ }
3533
+
3364
3534
  decorated.call(this, $element, options);
3365
3535
 
3366
3536
  if ($.isArray(tags)) {
@@ -3398,7 +3568,10 @@ S2.define('select2/data/tags',[
3398
3568
  }, true)
3399
3569
  );
3400
3570
 
3401
- var checkText = option.text === params.term;
3571
+ var optionText = (option.text || '').toUpperCase();
3572
+ var paramsTerm = (params.term || '').toUpperCase();
3573
+
3574
+ var checkText = optionText === paramsTerm;
3402
3575
 
3403
3576
  if (checkText || checkChildren) {
3404
3577
  if (child) {
@@ -3492,13 +3665,38 @@ S2.define('select2/data/tokenizer',[
3492
3665
  Tokenizer.prototype.query = function (decorated, params, callback) {
3493
3666
  var self = this;
3494
3667
 
3668
+ function createAndSelect (data) {
3669
+ // Normalize the data object so we can use it for checks
3670
+ var item = self._normalizeItem(data);
3671
+
3672
+ // Check if the data object already exists as a tag
3673
+ // Select it if it doesn't
3674
+ var $existingOptions = self.$element.find('option').filter(function () {
3675
+ return $(this).val() === item.id;
3676
+ });
3677
+
3678
+ // If an existing option wasn't found for it, create the option
3679
+ if (!$existingOptions.length) {
3680
+ var $option = self.option(item);
3681
+ $option.attr('data-select2-tag', true);
3682
+
3683
+ self._removeOldTags();
3684
+ self.addOptions([$option]);
3685
+ }
3686
+
3687
+ // Select the item, now that we know there is an option for it
3688
+ select(item);
3689
+ }
3690
+
3495
3691
  function select (data) {
3496
- self.select(data);
3692
+ self.trigger('select', {
3693
+ data: data
3694
+ });
3497
3695
  }
3498
3696
 
3499
3697
  params.term = params.term || '';
3500
3698
 
3501
- var tokenData = this.tokenizer(params, this.options, select);
3699
+ var tokenData = this.tokenizer(params, this.options, createAndSelect);
3502
3700
 
3503
3701
  if (tokenData.term !== params.term) {
3504
3702
  // Replace the search term if we have the search box
@@ -3541,6 +3739,11 @@ S2.define('select2/data/tokenizer',[
3541
3739
 
3542
3740
  var data = createTag(partParams);
3543
3741
 
3742
+ if (data == null) {
3743
+ i++;
3744
+ continue;
3745
+ }
3746
+
3544
3747
  callback(data);
3545
3748
 
3546
3749
  // Reset the term to not include the tokenized portion
@@ -3678,6 +3881,10 @@ S2.define('select2/dropdown',[
3678
3881
  return $dropdown;
3679
3882
  };
3680
3883
 
3884
+ Dropdown.prototype.bind = function () {
3885
+ // Should be implemented in subclasses
3886
+ };
3887
+
3681
3888
  Dropdown.prototype.position = function ($dropdown, $container) {
3682
3889
  // Should be implmented in subclasses
3683
3890
  };
@@ -3754,6 +3961,12 @@ S2.define('select2/dropdown/search',[
3754
3961
  self.$search.val('');
3755
3962
  });
3756
3963
 
3964
+ container.on('focus', function () {
3965
+ if (!container.isOpen()) {
3966
+ self.$search.focus();
3967
+ }
3968
+ });
3969
+
3757
3970
  container.on('results:all', function (params) {
3758
3971
  if (params.query.term == null || params.query.term === '') {
3759
3972
  var showSearch = self.showSearch(params);
@@ -3904,7 +4117,9 @@ S2.define('select2/dropdown/infiniteScroll',[
3904
4117
 
3905
4118
  InfiniteScroll.prototype.createLoadingMore = function () {
3906
4119
  var $option = $(
3907
- '<li class="option load-more" role="treeitem"></li>'
4120
+ '<li ' +
4121
+ 'class="select2-results__option select2-results__option--load-more"' +
4122
+ 'role="treeitem" aria-disabled="true"></li>'
3908
4123
  );
3909
4124
 
3910
4125
  var message = this.options.get('translations').get('loadingMore');
@@ -3922,7 +4137,7 @@ S2.define('select2/dropdown/attachBody',[
3922
4137
  '../utils'
3923
4138
  ], function ($, Utils) {
3924
4139
  function AttachBody (decorated, $element, options) {
3925
- this.$dropdownParent = options.get('dropdownParent') || document.body;
4140
+ this.$dropdownParent = options.get('dropdownParent') || $(document.body);
3926
4141
 
3927
4142
  decorated.call(this, $element, options);
3928
4143
  }
@@ -3963,6 +4178,12 @@ S2.define('select2/dropdown/attachBody',[
3963
4178
  });
3964
4179
  };
3965
4180
 
4181
+ AttachBody.prototype.destroy = function (decorated) {
4182
+ decorated.call(this);
4183
+
4184
+ this.$dropdownContainer.remove();
4185
+ };
4186
+
3966
4187
  AttachBody.prototype.position = function (decorated, $dropdown, $container) {
3967
4188
  // Clone all of the container classes
3968
4189
  $dropdown.attr('class', $container.attr('class'));
@@ -3993,7 +4214,8 @@ S2.define('select2/dropdown/attachBody',[
3993
4214
  this.$dropdownContainer.detach();
3994
4215
  };
3995
4216
 
3996
- AttachBody.prototype._attachPositioningHandler = function (container) {
4217
+ AttachBody.prototype._attachPositioningHandler =
4218
+ function (decorated, container) {
3997
4219
  var self = this;
3998
4220
 
3999
4221
  var scrollEvent = 'scroll.select2.' + container.id;
@@ -4020,7 +4242,8 @@ S2.define('select2/dropdown/attachBody',[
4020
4242
  });
4021
4243
  };
4022
4244
 
4023
- AttachBody.prototype._detachPositioningHandler = function (container) {
4245
+ AttachBody.prototype._detachPositioningHandler =
4246
+ function (decorated, container) {
4024
4247
  var scrollEvent = 'scroll.select2.' + container.id;
4025
4248
  var resizeEvent = 'resize.select2.' + container.id;
4026
4249
  var orientationEvent = 'orientationchange.select2.' + container.id;
@@ -4039,7 +4262,6 @@ S2.define('select2/dropdown/attachBody',[
4039
4262
 
4040
4263
  var newDirection = null;
4041
4264
 
4042
- var position = this.$container.position();
4043
4265
  var offset = this.$container.offset();
4044
4266
 
4045
4267
  offset.bottom = offset.top + this.$container.outerHeight(false);
@@ -4068,6 +4290,20 @@ S2.define('select2/dropdown/attachBody',[
4068
4290
  top: container.bottom
4069
4291
  };
4070
4292
 
4293
+ // Determine what the parent element is to use for calciulating the offset
4294
+ var $offsetParent = this.$dropdownParent;
4295
+
4296
+ // For statically positoned elements, we need to get the element
4297
+ // that is determining the offset
4298
+ if ($offsetParent.css('position') === 'static') {
4299
+ $offsetParent = $offsetParent.offsetParent();
4300
+ }
4301
+
4302
+ var parentOffset = $offsetParent.offset();
4303
+
4304
+ css.top -= parentOffset.top;
4305
+ css.left -= parentOffset.left;
4306
+
4071
4307
  if (!isCurrentlyAbove && !isCurrentlyBelow) {
4072
4308
  newDirection = 'below';
4073
4309
  }
@@ -4080,7 +4316,7 @@ S2.define('select2/dropdown/attachBody',[
4080
4316
 
4081
4317
  if (newDirection == 'above' ||
4082
4318
  (isCurrentlyAbove && newDirection !== 'below')) {
4083
- css.top = container.top - dropdown.height;
4319
+ css.top = container.top - parentOffset.top - dropdown.height;
4084
4320
  }
4085
4321
 
4086
4322
  if (newDirection != null) {
@@ -4096,14 +4332,13 @@ S2.define('select2/dropdown/attachBody',[
4096
4332
  };
4097
4333
 
4098
4334
  AttachBody.prototype._resizeDropdown = function () {
4099
- this.$dropdownContainer.width();
4100
-
4101
4335
  var css = {
4102
4336
  width: this.$container.outerWidth(false) + 'px'
4103
4337
  };
4104
4338
 
4105
4339
  if (this.options.get('dropdownAutoWidth')) {
4106
4340
  css.minWidth = css.width;
4341
+ css.position = 'relative';
4107
4342
  css.width = 'auto';
4108
4343
  }
4109
4344
 
@@ -4170,20 +4405,41 @@ S2.define('select2/dropdown/selectOnClose',[
4170
4405
 
4171
4406
  decorated.call(this, container, $container);
4172
4407
 
4173
- container.on('close', function () {
4174
- self._handleSelectOnClose();
4408
+ container.on('close', function (params) {
4409
+ self._handleSelectOnClose(params);
4175
4410
  });
4176
4411
  };
4177
4412
 
4178
- SelectOnClose.prototype._handleSelectOnClose = function () {
4413
+ SelectOnClose.prototype._handleSelectOnClose = function (_, params) {
4414
+ if (params && params.originalSelect2Event != null) {
4415
+ var event = params.originalSelect2Event;
4416
+
4417
+ // Don't select an item if the close event was triggered from a select or
4418
+ // unselect event
4419
+ if (event._type === 'select' || event._type === 'unselect') {
4420
+ return;
4421
+ }
4422
+ }
4423
+
4179
4424
  var $highlightedResults = this.getHighlightedResults();
4180
4425
 
4426
+ // Only select highlighted results
4181
4427
  if ($highlightedResults.length < 1) {
4182
4428
  return;
4183
4429
  }
4184
4430
 
4431
+ var data = $highlightedResults.data('data');
4432
+
4433
+ // Don't re-select already selected resulte
4434
+ if (
4435
+ (data.element != null && data.element.selected) ||
4436
+ (data.element == null && data.selected)
4437
+ ) {
4438
+ return;
4439
+ }
4440
+
4185
4441
  this.trigger('select', {
4186
- data: $highlightedResults.data('data')
4442
+ data: data
4187
4443
  });
4188
4444
  };
4189
4445
 
@@ -4217,7 +4473,10 @@ S2.define('select2/dropdown/closeOnSelect',[
4217
4473
  return;
4218
4474
  }
4219
4475
 
4220
- this.trigger('close');
4476
+ this.trigger('close', {
4477
+ originalEvent: originalEvent,
4478
+ originalSelect2Event: evt
4479
+ });
4221
4480
  };
4222
4481
 
4223
4482
  return CloseOnSelect;
@@ -4325,7 +4584,7 @@ S2.define('select2/defaults',[
4325
4584
  }
4326
4585
 
4327
4586
  Defaults.prototype.apply = function (options) {
4328
- options = $.extend({}, this.defaults, options);
4587
+ options = $.extend(true, {}, this.defaults, options);
4329
4588
 
4330
4589
  if (options.dataAdapter == null) {
4331
4590
  if (options.ajax != null) {
@@ -4868,8 +5127,8 @@ S2.define('select2/core',[
4868
5127
 
4869
5128
  // Hide the original select
4870
5129
  $element.addClass('select2-hidden-accessible');
4871
- $element.attr('aria-hidden', 'true');
4872
-
5130
+ $element.attr('aria-hidden', 'true');
5131
+
4873
5132
  // Synchronize any monitored attributes
4874
5133
  this._syncAttributes();
4875
5134
 
@@ -4889,6 +5148,7 @@ S2.define('select2/core',[
4889
5148
  id = Utils.generateChars(4);
4890
5149
  }
4891
5150
 
5151
+ id = id.replace(/(:|\.|\[|\]|,)/g, '');
4892
5152
  id = 'select2-' + id;
4893
5153
 
4894
5154
  return id;
@@ -4970,10 +5230,15 @@ S2.define('select2/core',[
4970
5230
  });
4971
5231
  });
4972
5232
 
4973
- this._sync = Utils.bind(this._syncAttributes, this);
5233
+ this.$element.on('focus.select2', function (evt) {
5234
+ self.trigger('focus', evt);
5235
+ });
5236
+
5237
+ this._syncA = Utils.bind(this._syncAttributes, this);
5238
+ this._syncS = Utils.bind(this._syncSubtree, this);
4974
5239
 
4975
5240
  if (this.$element[0].attachEvent) {
4976
- this.$element[0].attachEvent('onpropertychange', this._sync);
5241
+ this.$element[0].attachEvent('onpropertychange', this._syncA);
4977
5242
  }
4978
5243
 
4979
5244
  var observer = window.MutationObserver ||
@@ -4983,14 +5248,30 @@ S2.define('select2/core',[
4983
5248
 
4984
5249
  if (observer != null) {
4985
5250
  this._observer = new observer(function (mutations) {
4986
- $.each(mutations, self._sync);
5251
+ $.each(mutations, self._syncA);
5252
+ $.each(mutations, self._syncS);
4987
5253
  });
4988
5254
  this._observer.observe(this.$element[0], {
4989
5255
  attributes: true,
5256
+ childList: true,
4990
5257
  subtree: false
4991
5258
  });
4992
5259
  } else if (this.$element[0].addEventListener) {
4993
- this.$element[0].addEventListener('DOMAttrModified', self._sync, false);
5260
+ this.$element[0].addEventListener(
5261
+ 'DOMAttrModified',
5262
+ self._syncA,
5263
+ false
5264
+ );
5265
+ this.$element[0].addEventListener(
5266
+ 'DOMNodeInserted',
5267
+ self._syncS,
5268
+ false
5269
+ );
5270
+ this.$element[0].addEventListener(
5271
+ 'DOMNodeRemoved',
5272
+ self._syncS,
5273
+ false
5274
+ );
4994
5275
  }
4995
5276
  };
4996
5277
 
@@ -5004,12 +5285,16 @@ S2.define('select2/core',[
5004
5285
 
5005
5286
  Select2.prototype._registerSelectionEvents = function () {
5006
5287
  var self = this;
5007
- var nonRelayEvents = ['toggle'];
5288
+ var nonRelayEvents = ['toggle', 'focus'];
5008
5289
 
5009
5290
  this.selection.on('toggle', function () {
5010
5291
  self.toggleDropdown();
5011
5292
  });
5012
5293
 
5294
+ this.selection.on('focus', function (params) {
5295
+ self.focus(params);
5296
+ });
5297
+
5013
5298
  this.selection.on('*', function (name, params) {
5014
5299
  if ($.inArray(name, nonRelayEvents) !== -1) {
5015
5300
  return;
@@ -5054,17 +5339,13 @@ S2.define('select2/core',[
5054
5339
  self.$container.addClass('select2-container--disabled');
5055
5340
  });
5056
5341
 
5057
- this.on('focus', function () {
5058
- self.$container.addClass('select2-container--focus');
5059
- });
5060
-
5061
5342
  this.on('blur', function () {
5062
5343
  self.$container.removeClass('select2-container--focus');
5063
5344
  });
5064
5345
 
5065
5346
  this.on('query', function (params) {
5066
5347
  if (!self.isOpen()) {
5067
- self.trigger('open');
5348
+ self.trigger('open', {});
5068
5349
  }
5069
5350
 
5070
5351
  this.dataAdapter.query(params, function (data) {
@@ -5088,30 +5369,31 @@ S2.define('select2/core',[
5088
5369
  var key = evt.which;
5089
5370
 
5090
5371
  if (self.isOpen()) {
5091
- if (key === KEYS.ENTER) {
5092
- self.trigger('results:select');
5372
+ if (key === KEYS.ESC || key === KEYS.TAB ||
5373
+ (key === KEYS.UP && evt.altKey)) {
5374
+ self.close();
5375
+
5376
+ evt.preventDefault();
5377
+ } else if (key === KEYS.ENTER) {
5378
+ self.trigger('results:select', {});
5093
5379
 
5094
5380
  evt.preventDefault();
5095
5381
  } else if ((key === KEYS.SPACE && evt.ctrlKey)) {
5096
- self.trigger('results:toggle');
5382
+ self.trigger('results:toggle', {});
5097
5383
 
5098
5384
  evt.preventDefault();
5099
5385
  } else if (key === KEYS.UP) {
5100
- self.trigger('results:previous');
5386
+ self.trigger('results:previous', {});
5101
5387
 
5102
5388
  evt.preventDefault();
5103
5389
  } else if (key === KEYS.DOWN) {
5104
- self.trigger('results:next');
5105
-
5106
- evt.preventDefault();
5107
- } else if (key === KEYS.ESC || key === KEYS.TAB) {
5108
- self.close();
5390
+ self.trigger('results:next', {});
5109
5391
 
5110
5392
  evt.preventDefault();
5111
5393
  }
5112
5394
  } else {
5113
5395
  if (key === KEYS.ENTER || key === KEYS.SPACE ||
5114
- ((key === KEYS.DOWN || key === KEYS.UP) && evt.altKey)) {
5396
+ (key === KEYS.DOWN && evt.altKey)) {
5115
5397
  self.open();
5116
5398
 
5117
5399
  evt.preventDefault();
@@ -5128,9 +5410,49 @@ S2.define('select2/core',[
5128
5410
  this.close();
5129
5411
  }
5130
5412
 
5131
- this.trigger('disable');
5413
+ this.trigger('disable', {});
5132
5414
  } else {
5133
- this.trigger('enable');
5415
+ this.trigger('enable', {});
5416
+ }
5417
+ };
5418
+
5419
+ Select2.prototype._syncSubtree = function (evt, mutations) {
5420
+ var changed = false;
5421
+ var self = this;
5422
+
5423
+ // Ignore any mutation events raised for elements that aren't options or
5424
+ // optgroups. This handles the case when the select element is destroyed
5425
+ if (
5426
+ evt && evt.target && (
5427
+ evt.target.nodeName !== 'OPTION' && evt.target.nodeName !== 'OPTGROUP'
5428
+ )
5429
+ ) {
5430
+ return;
5431
+ }
5432
+
5433
+ if (!mutations) {
5434
+ // If mutation events aren't supported, then we can only assume that the
5435
+ // change affected the selections
5436
+ changed = true;
5437
+ } else if (mutations.addedNodes && mutations.addedNodes.length > 0) {
5438
+ for (var n = 0; n < mutations.addedNodes.length; n++) {
5439
+ var node = mutations.addedNodes[n];
5440
+
5441
+ if (node.selected) {
5442
+ changed = true;
5443
+ }
5444
+ }
5445
+ } else if (mutations.removedNodes && mutations.removedNodes.length > 0) {
5446
+ changed = true;
5447
+ }
5448
+
5449
+ // Only re-pull the data if we think there is a change
5450
+ if (changed) {
5451
+ this.dataAdapter.current(function (currentData) {
5452
+ self.trigger('selection:update', {
5453
+ data: currentData
5454
+ });
5455
+ });
5134
5456
  }
5135
5457
  };
5136
5458
 
@@ -5147,6 +5469,10 @@ S2.define('select2/core',[
5147
5469
  'unselect': 'unselecting'
5148
5470
  };
5149
5471
 
5472
+ if (args === undefined) {
5473
+ args = {};
5474
+ }
5475
+
5150
5476
  if (name in preTriggerMap) {
5151
5477
  var preTriggerName = preTriggerMap[name];
5152
5478
  var preTriggerArgs = {
@@ -5185,8 +5511,6 @@ S2.define('select2/core',[
5185
5511
  }
5186
5512
 
5187
5513
  this.trigger('query', {});
5188
-
5189
- this.trigger('open');
5190
5514
  };
5191
5515
 
5192
5516
  Select2.prototype.close = function () {
@@ -5194,13 +5518,27 @@ S2.define('select2/core',[
5194
5518
  return;
5195
5519
  }
5196
5520
 
5197
- this.trigger('close');
5521
+ this.trigger('close', {});
5198
5522
  };
5199
5523
 
5200
5524
  Select2.prototype.isOpen = function () {
5201
5525
  return this.$container.hasClass('select2-container--open');
5202
5526
  };
5203
5527
 
5528
+ Select2.prototype.hasFocus = function () {
5529
+ return this.$container.hasClass('select2-container--focus');
5530
+ };
5531
+
5532
+ Select2.prototype.focus = function (data) {
5533
+ // No need to re-trigger focus events if we are already focused
5534
+ if (this.hasFocus()) {
5535
+ return;
5536
+ }
5537
+
5538
+ this.$container.addClass('select2-container--focus');
5539
+ this.trigger('focus', {});
5540
+ };
5541
+
5204
5542
  Select2.prototype.enable = function (args) {
5205
5543
  if (this.options.get('debug') && window.console && console.warn) {
5206
5544
  console.warn(
@@ -5264,7 +5602,7 @@ S2.define('select2/core',[
5264
5602
  this.$container.remove();
5265
5603
 
5266
5604
  if (this.$element[0].detachEvent) {
5267
- this.$element[0].detachEvent('onpropertychange', this._sync);
5605
+ this.$element[0].detachEvent('onpropertychange', this._syncA);
5268
5606
  }
5269
5607
 
5270
5608
  if (this._observer != null) {
@@ -5272,16 +5610,21 @@ S2.define('select2/core',[
5272
5610
  this._observer = null;
5273
5611
  } else if (this.$element[0].removeEventListener) {
5274
5612
  this.$element[0]
5275
- .removeEventListener('DOMAttrModified', this._sync, false);
5613
+ .removeEventListener('DOMAttrModified', this._syncA, false);
5614
+ this.$element[0]
5615
+ .removeEventListener('DOMNodeInserted', this._syncS, false);
5616
+ this.$element[0]
5617
+ .removeEventListener('DOMNodeRemoved', this._syncS, false);
5276
5618
  }
5277
5619
 
5278
- this._sync = null;
5620
+ this._syncA = null;
5621
+ this._syncS = null;
5279
5622
 
5280
5623
  this.$element.off('.select2');
5281
5624
  this.$element.attr('tabindex', this.$element.data('old-tabindex'));
5282
5625
 
5283
5626
  this.$element.removeClass('select2-hidden-accessible');
5284
- this.$element.attr('aria-hidden', 'false');
5627
+ this.$element.attr('aria-hidden', 'false');
5285
5628
  this.$element.removeData('select2');
5286
5629
 
5287
5630
  this.dataAdapter.destroy();
@@ -5317,16 +5660,20 @@ S2.define('select2/core',[
5317
5660
  return Select2;
5318
5661
  });
5319
5662
 
5663
+ S2.define('jquery-mousewheel',[
5664
+ 'jquery'
5665
+ ], function ($) {
5666
+ // Used to shim jQuery.mousewheel for non-full builds.
5667
+ return $;
5668
+ });
5669
+
5320
5670
  S2.define('jquery.select2',[
5321
5671
  'jquery',
5322
- 'require',
5672
+ 'jquery-mousewheel',
5323
5673
 
5324
5674
  './select2/core',
5325
5675
  './select2/defaults'
5326
- ], function ($, require, Select2, Defaults) {
5327
- // Force jQuery.mousewheel to be loaded if it hasn't already
5328
- require('jquery.mousewheel');
5329
-
5676
+ ], function ($, _, Select2, Defaults) {
5330
5677
  if ($.fn.select2 == null) {
5331
5678
  // All methods that should return the element
5332
5679
  var thisMethods = ['open', 'close', 'destroy'];
@@ -5336,25 +5683,28 @@ S2.define('jquery.select2',[
5336
5683
 
5337
5684
  if (typeof options === 'object') {
5338
5685
  this.each(function () {
5339
- var instanceOptions = $.extend({}, options, true);
5686
+ var instanceOptions = $.extend(true, {}, options);
5340
5687
 
5341
5688
  var instance = new Select2($(this), instanceOptions);
5342
5689
  });
5343
5690
 
5344
5691
  return this;
5345
5692
  } else if (typeof options === 'string') {
5346
- var instance = this.data('select2');
5693
+ var ret;
5694
+ var args = Array.prototype.slice.call(arguments, 1);
5347
5695
 
5348
- if (instance == null && window.console && console.error) {
5349
- console.error(
5350
- 'The select2(\'' + options + '\') method was called on an ' +
5351
- 'element that is not using Select2.'
5352
- );
5353
- }
5696
+ this.each(function () {
5697
+ var instance = $(this).data('select2');
5354
5698
 
5355
- var args = Array.prototype.slice.call(arguments, 1);
5699
+ if (instance == null && window.console && console.error) {
5700
+ console.error(
5701
+ 'The select2(\'' + options + '\') method was called on an ' +
5702
+ 'element that is not using Select2.'
5703
+ );
5704
+ }
5356
5705
 
5357
- var ret = instance[options](args);
5706
+ ret = instance[options].apply(instance, args);
5707
+ });
5358
5708
 
5359
5709
  // Check if we should be returning `this`
5360
5710
  if ($.inArray(options, thisMethods) > -1) {
@@ -5375,13 +5725,6 @@ S2.define('jquery.select2',[
5375
5725
  return Select2;
5376
5726
  });
5377
5727
 
5378
- S2.define('jquery.mousewheel',[
5379
- 'jquery'
5380
- ], function ($) {
5381
- // Used to shim jQuery.mousewheel for non-full builds.
5382
- return $;
5383
- });
5384
-
5385
5728
  // Return the AMD loader configuration so it can be used outside of this file
5386
5729
  return {
5387
5730
  define: S2.define,