select2-rails 4.0.1 → 4.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +12 -2
  3. data/lib/select2-rails/source_file.rb +10 -7
  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 +283 -107
  8. data/vendor/assets/javascripts/select2_locale_ar.js +2 -2
  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 +1 -1
  25. data/vendor/assets/javascripts/select2_locale_hi.js +1 -1
  26. data/vendor/assets/javascripts/select2_locale_hr.js +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  39. data/vendor/assets/javascripts/select2_locale_nb.js +2 -2
  40. data/vendor/assets/javascripts/select2_locale_nl.js +1 -1
  41. data/vendor/assets/javascripts/select2_locale_pl.js +1 -1
  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 +1 -1
  49. data/vendor/assets/javascripts/select2_locale_sr.js +1 -1
  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 +38 -26
  58. data/vendor/assets/stylesheets/select2.css +2 -0
  59. metadata +11 -7
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Select2 4.0.1
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.3.1 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,60 +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) {
78
- name = name.split('/');
79
- lastIndex = name.length - 1;
80
-
81
- // Node .js allowance:
82
- if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
83
- name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
84
- }
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
+ }
85
96
 
86
- //Lop off the last part of baseParts, so that . matches the
87
- //"directory" and not name of the baseName's module. For instance,
88
- //baseName of "one/two/three", maps to "one/two/three.js", but we
89
- //want the directory, "one/two" for this normalization.
90
- name = baseParts.slice(0, baseParts.length - 1).concat(name);
91
-
92
- //start trimDots
93
- for (i = 0; i < name.length; i += 1) {
94
- part = name[i];
95
- if (part === ".") {
96
- name.splice(i, 1);
97
- i -= 1;
98
- } else if (part === "..") {
99
- if (i === 1 && (name[2] === '..' || name[0] === '..')) {
100
- //End of the line. Keep at least one non-dot
101
- //path segment at the front so it can be mapped
102
- //correctly to disk. Otherwise, there is likely
103
- //no path mapping for a path starting with '..'.
104
- //This can still fail, but catches the most reasonable
105
- //uses of ..
106
- break;
107
- } else if (i > 0) {
108
- name.splice(i - 1, 2);
109
- i -= 2;
110
- }
97
+ // Starts with a '.' so need the baseName
98
+ if (name[0].charAt(0) === '.' && baseParts) {
99
+ //Convert baseName to array, and lop off the last part,
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
103
+ //this normalization.
104
+ normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
105
+ name = normalizedBaseParts.concat(name);
106
+ }
107
+
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;
111
125
  }
112
126
  }
113
- //end trimDots
114
-
115
- name = name.join("/");
116
- } else if (name.indexOf('./') === 0) {
117
- // No baseName, so this is ID is resolved relative
118
- // to baseUrl, pull off the leading dot.
119
- name = name.substring(2);
120
127
  }
128
+ //end trimDots
129
+
130
+ name = name.join('/');
121
131
  }
122
132
 
123
133
  //Apply map config if available.
@@ -230,32 +240,39 @@ var requirejs, require, define;
230
240
  return [prefix, name];
231
241
  }
232
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
+
233
249
  /**
234
250
  * Makes a name map, normalizing the name, and using a plugin
235
251
  * for normalization if necessary. Grabs a ref to plugin
236
252
  * too, as an optimization.
237
253
  */
238
- makeMap = function (name, relName) {
254
+ makeMap = function (name, relParts) {
239
255
  var plugin,
240
256
  parts = splitPrefix(name),
241
- prefix = parts[0];
257
+ prefix = parts[0],
258
+ relResourceName = relParts[1];
242
259
 
243
260
  name = parts[1];
244
261
 
245
262
  if (prefix) {
246
- prefix = normalize(prefix, relName);
263
+ prefix = normalize(prefix, relResourceName);
247
264
  plugin = callDep(prefix);
248
265
  }
249
266
 
250
267
  //Normalize according
251
268
  if (prefix) {
252
269
  if (plugin && plugin.normalize) {
253
- name = plugin.normalize(name, makeNormalize(relName));
270
+ name = plugin.normalize(name, makeNormalize(relResourceName));
254
271
  } else {
255
- name = normalize(name, relName);
272
+ name = normalize(name, relResourceName);
256
273
  }
257
274
  } else {
258
- name = normalize(name, relName);
275
+ name = normalize(name, relResourceName);
259
276
  parts = splitPrefix(name);
260
277
  prefix = parts[0];
261
278
  name = parts[1];
@@ -302,13 +319,14 @@ var requirejs, require, define;
302
319
  };
303
320
 
304
321
  main = function (name, deps, callback, relName) {
305
- var cjsModule, depName, ret, map, i,
322
+ var cjsModule, depName, ret, map, i, relParts,
306
323
  args = [],
307
324
  callbackType = typeof callback,
308
325
  usingExports;
309
326
 
310
327
  //Use name if no relName
311
328
  relName = relName || name;
329
+ relParts = makeRelParts(relName);
312
330
 
313
331
  //Call the callback to define the module, if necessary.
314
332
  if (callbackType === 'undefined' || callbackType === 'function') {
@@ -317,7 +335,7 @@ var requirejs, require, define;
317
335
  //Default to [require, exports, module] if no deps
318
336
  deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
319
337
  for (i = 0; i < deps.length; i += 1) {
320
- map = makeMap(deps[i], relName);
338
+ map = makeMap(deps[i], relParts);
321
339
  depName = map.f;
322
340
 
323
341
  //Fast path CommonJS standard dependencies.
@@ -373,7 +391,7 @@ var requirejs, require, define;
373
391
  //deps arg is the module name, and second arg (if passed)
374
392
  //is just the relName.
375
393
  //Normalize module name, if it contains . or ..
376
- return callDep(makeMap(deps, callback).f);
394
+ return callDep(makeMap(deps, makeRelParts(callback)).f);
377
395
  } else if (!deps.splice) {
378
396
  //deps is a config object, not an array.
379
397
  config = deps;
@@ -606,9 +624,23 @@ S2.define('select2/utils',[
606
624
 
607
625
  Observable.prototype.trigger = function (event) {
608
626
  var slice = Array.prototype.slice;
627
+ var params = slice.call(arguments, 1);
609
628
 
610
629
  this.listeners = this.listeners || {};
611
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
+
612
644
  if (event in this.listeners) {
613
645
  this.invoke(this.listeners[event], slice.call(arguments, 1));
614
646
  }
@@ -842,6 +874,25 @@ S2.define('select2/results',[
842
874
  return sorter(data);
843
875
  };
844
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
+
845
896
  Results.prototype.setClasses = function () {
846
897
  var self = this;
847
898
 
@@ -869,17 +920,6 @@ S2.define('select2/results',[
869
920
  }
870
921
  });
871
922
 
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
923
  });
884
924
  };
885
925
 
@@ -990,6 +1030,7 @@ S2.define('select2/results',[
990
1030
 
991
1031
  if (container.isOpen()) {
992
1032
  self.setClasses();
1033
+ self.highlightFirstItem();
993
1034
  }
994
1035
  });
995
1036
 
@@ -1012,6 +1053,7 @@ S2.define('select2/results',[
1012
1053
  }
1013
1054
 
1014
1055
  self.setClasses();
1056
+ self.highlightFirstItem();
1015
1057
  });
1016
1058
 
1017
1059
  container.on('unselect', function () {
@@ -1020,6 +1062,7 @@ S2.define('select2/results',[
1020
1062
  }
1021
1063
 
1022
1064
  self.setClasses();
1065
+ self.highlightFirstItem();
1023
1066
  });
1024
1067
 
1025
1068
  container.on('open', function () {
@@ -1142,11 +1185,7 @@ S2.define('select2/results',[
1142
1185
  this.$results.on('mousewheel', function (e) {
1143
1186
  var top = self.$results.scrollTop();
1144
1187
 
1145
- var bottom = (
1146
- self.$results.get(0).scrollHeight -
1147
- self.$results.scrollTop() +
1148
- e.deltaY
1149
- );
1188
+ var bottom = self.$results.get(0).scrollHeight - top + e.deltaY;
1150
1189
 
1151
1190
  var isAtTop = e.deltaY > 0 && top - e.deltaY <= 0;
1152
1191
  var isAtBottom = e.deltaY < 0 && bottom <= self.$results.height();
@@ -1501,6 +1540,12 @@ S2.define('select2/selection/single',[
1501
1540
  // User exits the container
1502
1541
  });
1503
1542
 
1543
+ container.on('focus', function (evt) {
1544
+ if (!container.isOpen()) {
1545
+ self.$selection.focus();
1546
+ }
1547
+ });
1548
+
1504
1549
  container.on('selection:update', function (params) {
1505
1550
  self.update(params.data);
1506
1551
  });
@@ -3164,7 +3209,7 @@ S2.define('select2/data/select',[
3164
3209
  }
3165
3210
  }
3166
3211
 
3167
- if (data.id) {
3212
+ if (data.id !== undefined) {
3168
3213
  option.value = data.id;
3169
3214
  }
3170
3215
 
@@ -3332,7 +3377,7 @@ S2.define('select2/data/array',[
3332
3377
  var $existingOption = $existing.filter(onlyItem(item));
3333
3378
 
3334
3379
  var existingData = this.item($existingOption);
3335
- var newData = $.extend(true, {}, existingData, item);
3380
+ var newData = $.extend(true, {}, item, existingData);
3336
3381
 
3337
3382
  var $newOption = this.option(newData);
3338
3383
 
@@ -3440,13 +3485,21 @@ S2.define('select2/data/ajax',[
3440
3485
 
3441
3486
  callback(results);
3442
3487
  }, function () {
3443
- // 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
+ });
3444
3497
  });
3445
3498
 
3446
3499
  self._request = $request;
3447
3500
  }
3448
3501
 
3449
- if (this.ajaxOptions.delay && params.term !== '') {
3502
+ if (this.ajaxOptions.delay && params.term != null) {
3450
3503
  if (this._queryTimeout) {
3451
3504
  window.clearTimeout(this._queryTimeout);
3452
3505
  }
@@ -3472,6 +3525,12 @@ S2.define('select2/data/tags',[
3472
3525
  this.createTag = createTag;
3473
3526
  }
3474
3527
 
3528
+ var insertTag = options.get('insertTag');
3529
+
3530
+ if (insertTag !== undefined) {
3531
+ this.insertTag = insertTag;
3532
+ }
3533
+
3475
3534
  decorated.call(this, $element, options);
3476
3535
 
3477
3536
  if ($.isArray(tags)) {
@@ -3509,7 +3568,10 @@ S2.define('select2/data/tags',[
3509
3568
  }, true)
3510
3569
  );
3511
3570
 
3512
- var checkText = option.text === params.term;
3571
+ var optionText = (option.text || '').toUpperCase();
3572
+ var paramsTerm = (params.term || '').toUpperCase();
3573
+
3574
+ var checkText = optionText === paramsTerm;
3513
3575
 
3514
3576
  if (checkText || checkChildren) {
3515
3577
  if (child) {
@@ -3603,6 +3665,29 @@ S2.define('select2/data/tokenizer',[
3603
3665
  Tokenizer.prototype.query = function (decorated, params, callback) {
3604
3666
  var self = this;
3605
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
+
3606
3691
  function select (data) {
3607
3692
  self.trigger('select', {
3608
3693
  data: data
@@ -3611,7 +3696,7 @@ S2.define('select2/data/tokenizer',[
3611
3696
 
3612
3697
  params.term = params.term || '';
3613
3698
 
3614
- var tokenData = this.tokenizer(params, this.options, select);
3699
+ var tokenData = this.tokenizer(params, this.options, createAndSelect);
3615
3700
 
3616
3701
  if (tokenData.term !== params.term) {
3617
3702
  // Replace the search term if we have the search box
@@ -3876,6 +3961,12 @@ S2.define('select2/dropdown/search',[
3876
3961
  self.$search.val('');
3877
3962
  });
3878
3963
 
3964
+ container.on('focus', function () {
3965
+ if (!container.isOpen()) {
3966
+ self.$search.focus();
3967
+ }
3968
+ });
3969
+
3879
3970
  container.on('results:all', function (params) {
3880
3971
  if (params.query.term == null || params.query.term === '') {
3881
3972
  var showSearch = self.showSearch(params);
@@ -4171,7 +4262,6 @@ S2.define('select2/dropdown/attachBody',[
4171
4262
 
4172
4263
  var newDirection = null;
4173
4264
 
4174
- var position = this.$container.position();
4175
4265
  var offset = this.$container.offset();
4176
4266
 
4177
4267
  offset.bottom = offset.top + this.$container.outerHeight(false);
@@ -4200,14 +4290,20 @@ S2.define('select2/dropdown/attachBody',[
4200
4290
  top: container.bottom
4201
4291
  };
4202
4292
 
4203
- // Fix positioning with static parents
4204
- if (this.$dropdownParent[0].style.position !== 'static') {
4205
- var parentOffset = this.$dropdownParent.offset();
4293
+ // Determine what the parent element is to use for calciulating the offset
4294
+ var $offsetParent = this.$dropdownParent;
4206
4295
 
4207
- css.top -= parentOffset.top;
4208
- css.left -= parentOffset.left;
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();
4209
4300
  }
4210
4301
 
4302
+ var parentOffset = $offsetParent.offset();
4303
+
4304
+ css.top -= parentOffset.top;
4305
+ css.left -= parentOffset.left;
4306
+
4211
4307
  if (!isCurrentlyAbove && !isCurrentlyBelow) {
4212
4308
  newDirection = 'below';
4213
4309
  }
@@ -4220,7 +4316,7 @@ S2.define('select2/dropdown/attachBody',[
4220
4316
 
4221
4317
  if (newDirection == 'above' ||
4222
4318
  (isCurrentlyAbove && newDirection !== 'below')) {
4223
- css.top = container.top - dropdown.height;
4319
+ css.top = container.top - parentOffset.top - dropdown.height;
4224
4320
  }
4225
4321
 
4226
4322
  if (newDirection != null) {
@@ -4242,6 +4338,7 @@ S2.define('select2/dropdown/attachBody',[
4242
4338
 
4243
4339
  if (this.options.get('dropdownAutoWidth')) {
4244
4340
  css.minWidth = css.width;
4341
+ css.position = 'relative';
4245
4342
  css.width = 'auto';
4246
4343
  }
4247
4344
 
@@ -4308,12 +4405,22 @@ S2.define('select2/dropdown/selectOnClose',[
4308
4405
 
4309
4406
  decorated.call(this, container, $container);
4310
4407
 
4311
- container.on('close', function () {
4312
- self._handleSelectOnClose();
4408
+ container.on('close', function (params) {
4409
+ self._handleSelectOnClose(params);
4313
4410
  });
4314
4411
  };
4315
4412
 
4316
- 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
+
4317
4424
  var $highlightedResults = this.getHighlightedResults();
4318
4425
 
4319
4426
  // Only select highlighted results
@@ -4366,7 +4473,10 @@ S2.define('select2/dropdown/closeOnSelect',[
4366
4473
  return;
4367
4474
  }
4368
4475
 
4369
- this.trigger('close', {});
4476
+ this.trigger('close', {
4477
+ originalEvent: originalEvent,
4478
+ originalSelect2Event: evt
4479
+ });
4370
4480
  };
4371
4481
 
4372
4482
  return CloseOnSelect;
@@ -4474,7 +4584,7 @@ S2.define('select2/defaults',[
4474
4584
  }
4475
4585
 
4476
4586
  Defaults.prototype.apply = function (options) {
4477
- options = $.extend({}, this.defaults, options);
4587
+ options = $.extend(true, {}, this.defaults, options);
4478
4588
 
4479
4589
  if (options.dataAdapter == null) {
4480
4590
  if (options.ajax != null) {
@@ -5038,6 +5148,7 @@ S2.define('select2/core',[
5038
5148
  id = Utils.generateChars(4);
5039
5149
  }
5040
5150
 
5151
+ id = id.replace(/(:|\.|\[|\]|,)/g, '');
5041
5152
  id = 'select2-' + id;
5042
5153
 
5043
5154
  return id;
@@ -5119,10 +5230,15 @@ S2.define('select2/core',[
5119
5230
  });
5120
5231
  });
5121
5232
 
5122
- 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);
5123
5239
 
5124
5240
  if (this.$element[0].attachEvent) {
5125
- this.$element[0].attachEvent('onpropertychange', this._sync);
5241
+ this.$element[0].attachEvent('onpropertychange', this._syncA);
5126
5242
  }
5127
5243
 
5128
5244
  var observer = window.MutationObserver ||
@@ -5132,14 +5248,30 @@ S2.define('select2/core',[
5132
5248
 
5133
5249
  if (observer != null) {
5134
5250
  this._observer = new observer(function (mutations) {
5135
- $.each(mutations, self._sync);
5251
+ $.each(mutations, self._syncA);
5252
+ $.each(mutations, self._syncS);
5136
5253
  });
5137
5254
  this._observer.observe(this.$element[0], {
5138
5255
  attributes: true,
5256
+ childList: true,
5139
5257
  subtree: false
5140
5258
  });
5141
5259
  } else if (this.$element[0].addEventListener) {
5142
- 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
+ );
5143
5275
  }
5144
5276
  };
5145
5277
 
@@ -5284,6 +5416,46 @@ S2.define('select2/core',[
5284
5416
  }
5285
5417
  };
5286
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
+ });
5456
+ }
5457
+ };
5458
+
5287
5459
  /**
5288
5460
  * Override the trigger method to automatically trigger pre-events when
5289
5461
  * there are events that can be prevented.
@@ -5430,7 +5602,7 @@ S2.define('select2/core',[
5430
5602
  this.$container.remove();
5431
5603
 
5432
5604
  if (this.$element[0].detachEvent) {
5433
- this.$element[0].detachEvent('onpropertychange', this._sync);
5605
+ this.$element[0].detachEvent('onpropertychange', this._syncA);
5434
5606
  }
5435
5607
 
5436
5608
  if (this._observer != null) {
@@ -5438,10 +5610,15 @@ S2.define('select2/core',[
5438
5610
  this._observer = null;
5439
5611
  } else if (this.$element[0].removeEventListener) {
5440
5612
  this.$element[0]
5441
- .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);
5442
5618
  }
5443
5619
 
5444
- this._sync = null;
5620
+ this._syncA = null;
5621
+ this._syncS = null;
5445
5622
 
5446
5623
  this.$element.off('.select2');
5447
5624
  this.$element.attr('tabindex', this.$element.data('old-tabindex'));
@@ -5514,6 +5691,7 @@ S2.define('jquery.select2',[
5514
5691
  return this;
5515
5692
  } else if (typeof options === 'string') {
5516
5693
  var ret;
5694
+ var args = Array.prototype.slice.call(arguments, 1);
5517
5695
 
5518
5696
  this.each(function () {
5519
5697
  var instance = $(this).data('select2');
@@ -5525,8 +5703,6 @@ S2.define('jquery.select2',[
5525
5703
  );
5526
5704
  }
5527
5705
 
5528
- var args = Array.prototype.slice.call(arguments, 1);
5529
-
5530
5706
  ret = instance[options].apply(instance, args);
5531
5707
  });
5532
5708