select2-rails 3.3.2 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  Copyright 2012 Igor Vaynberg
3
3
 
4
- Version: 3.3.2 Timestamp: Mon Mar 25 12:14:18 PDT 2013
4
+ Version: 3.4.0 Timestamp: Tue May 14 08:27:33 PDT 2013
5
5
 
6
6
  This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU
7
7
  General Public License version 2 (the "GPL License"). You may choose either license to govern your
@@ -19,23 +19,23 @@ CONDITIONS OF ANY KIND, either express or implied. See the Apache License and th
19
19
  the specific language governing permissions and limitations under the Apache License and the GPL License.
20
20
  */
21
21
  (function ($) {
22
- if(typeof $.fn.each2 == "undefined"){
23
- $.fn.extend({
24
- /*
25
- * 4-10 times faster .each replacement
26
- * use it carefully, as it overrides jQuery context of element on each iteration
27
- */
28
- each2 : function (c) {
29
- var j = $([0]), i = -1, l = this.length;
30
- while (
31
- ++i < l
32
- && (j.context = j[0] = this[i])
33
- && c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object
34
- );
35
- return this;
36
- }
37
- });
38
- }
22
+ if(typeof $.fn.each2 == "undefined"){
23
+ $.fn.extend({
24
+ /*
25
+ * 4-10 times faster .each replacement
26
+ * use it carefully, as it overrides jQuery context of element on each iteration
27
+ */
28
+ each2 : function (c) {
29
+ var j = $([0]), i = -1, l = this.length;
30
+ while (
31
+ ++i < l
32
+ && (j.context = j[0] = this[i])
33
+ && c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object
34
+ );
35
+ return this;
36
+ }
37
+ });
38
+ }
39
39
  })(jQuery);
40
40
 
41
41
  (function ($, undefined) {
@@ -47,7 +47,7 @@ the specific language governing permissions and limitations under the Apache Lic
47
47
  }
48
48
 
49
49
  var KEY, AbstractSelect2, SingleSelect2, MultiSelect2, nextUid, sizer,
50
- lastMousePosition, $document;
50
+ lastMousePosition, $document, scrollBarDimensions,
51
51
 
52
52
  KEY = {
53
53
  TAB: 9,
@@ -95,7 +95,8 @@ the specific language governing permissions and limitations under the Apache Lic
95
95
  k = k.which ? k.which : k;
96
96
  return k >= 112 && k <= 123;
97
97
  }
98
- };
98
+ },
99
+ MEASURE_SCROLLBAR_TEMPLATE = "<div class='select2-measure-scrollbar'></div>";
99
100
 
100
101
  $document = $(document);
101
102
 
@@ -109,6 +110,19 @@ the specific language governing permissions and limitations under the Apache Lic
109
110
  return -1;
110
111
  }
111
112
 
113
+ function measureScrollbar () {
114
+ var $template = $( MEASURE_SCROLLBAR_TEMPLATE );
115
+ $template.appendTo('body');
116
+
117
+ var dim = {
118
+ width: $template.width() - $template[0].clientWidth,
119
+ height: $template.height() - $template[0].clientHeight
120
+ };
121
+ $template.remove();
122
+
123
+ return dim;
124
+ }
125
+
112
126
  /**
113
127
  * Compares equality of a and b
114
128
  * @param a
@@ -143,12 +157,12 @@ the specific language governing permissions and limitations under the Apache Lic
143
157
 
144
158
  function installKeyUpChangeEvent(element) {
145
159
  var key="keyup-change-value";
146
- element.bind("keydown", function () {
160
+ element.on("keydown", function () {
147
161
  if ($.data(element, key) === undefined) {
148
162
  $.data(element, key, element.val());
149
163
  }
150
164
  });
151
- element.bind("keyup", function () {
165
+ element.on("keyup", function () {
152
166
  var val= $.data(element, key);
153
167
  if (val !== undefined && element.val() !== val) {
154
168
  $.removeData(element, key);
@@ -157,7 +171,7 @@ the specific language governing permissions and limitations under the Apache Lic
157
171
  });
158
172
  }
159
173
 
160
- $document.bind("mousemove", function (e) {
174
+ $document.on("mousemove", function (e) {
161
175
  lastMousePosition = {x: e.pageX, y: e.pageY};
162
176
  });
163
177
 
@@ -168,7 +182,7 @@ the specific language governing permissions and limitations under the Apache Lic
168
182
  * the elements under the pointer are scrolled.
169
183
  */
170
184
  function installFilteredMouseMove(element) {
171
- element.bind("mousemove", function (e) {
185
+ element.on("mousemove", function (e) {
172
186
  var lastpos = lastMousePosition;
173
187
  if (lastpos === undefined || lastpos.x !== e.pageX || lastpos.y !== e.pageY) {
174
188
  $(e.target).trigger("mousemove-filtered", e);
@@ -213,7 +227,7 @@ the specific language governing permissions and limitations under the Apache Lic
213
227
 
214
228
  function installDebouncedScroll(threshold, element) {
215
229
  var notify = debounce(threshold, function (e) { element.trigger("scroll-debounced", e);});
216
- element.bind("scroll", function (e) {
230
+ element.on("scroll", function (e) {
217
231
  if (indexOf(e.target, element.get()) >= 0) notify(e);
218
232
  });
219
233
  }
@@ -248,6 +262,23 @@ the specific language governing permissions and limitations under the Apache Lic
248
262
  }, 0);
249
263
  }
250
264
 
265
+ function getCursorInfo(el) {
266
+ el = $(el)[0];
267
+ var offset = 0;
268
+ var length = 0;
269
+ if ('selectionStart' in el) {
270
+ offset = el.selectionStart;
271
+ length = el.selectionEnd - offset;
272
+ } else if ('selection' in document) {
273
+ el.focus();
274
+ var sel = document.selection.createRange();
275
+ length = document.selection.createRange().text.length;
276
+ sel.moveStart('character', -el.value.length);
277
+ offset = sel.text.length - length;
278
+ }
279
+ return { offset: offset, length: length };
280
+ }
281
+
251
282
  function killEvent(event) {
252
283
  event.preventDefault();
253
284
  event.stopPropagation();
@@ -259,22 +290,22 @@ the specific language governing permissions and limitations under the Apache Lic
259
290
 
260
291
  function measureTextWidth(e) {
261
292
  if (!sizer){
262
- var style = e[0].currentStyle || window.getComputedStyle(e[0], null);
263
- sizer = $(document.createElement("div")).css({
264
- position: "absolute",
265
- left: "-10000px",
266
- top: "-10000px",
267
- display: "none",
268
- fontSize: style.fontSize,
269
- fontFamily: style.fontFamily,
270
- fontStyle: style.fontStyle,
271
- fontWeight: style.fontWeight,
272
- letterSpacing: style.letterSpacing,
273
- textTransform: style.textTransform,
274
- whiteSpace: "nowrap"
275
- });
293
+ var style = e[0].currentStyle || window.getComputedStyle(e[0], null);
294
+ sizer = $(document.createElement("div")).css({
295
+ position: "absolute",
296
+ left: "-10000px",
297
+ top: "-10000px",
298
+ display: "none",
299
+ fontSize: style.fontSize,
300
+ fontFamily: style.fontFamily,
301
+ fontStyle: style.fontStyle,
302
+ fontWeight: style.fontWeight,
303
+ letterSpacing: style.letterSpacing,
304
+ textTransform: style.textTransform,
305
+ whiteSpace: "nowrap"
306
+ });
276
307
  sizer.attr("class","select2-sizer");
277
- $("body").append(sizer);
308
+ $("body").append(sizer);
278
309
  }
279
310
  sizer.text(e.val());
280
311
  return sizer.width();
@@ -328,11 +359,11 @@ the specific language governing permissions and limitations under the Apache Lic
328
359
  * Produces an ajax-based query function
329
360
  *
330
361
  * @param options object containing configuration paramters
362
+ * @param options.params parameter map for the transport ajax call, can contain such options as cache, jsonpCallback, etc. see $.ajax
331
363
  * @param options.transport function that will be used to execute the ajax request. must be compatible with parameters supported by $.ajax
332
364
  * @param options.url url for the data
333
365
  * @param options.data a function(searchTerm, pageNumber, context) that should return an object containing query string parameters for the above url.
334
366
  * @param options.dataType request data type: ajax, jsonp, other datatatypes supported by jQuery's $.ajax function or the transport function if specified
335
- * @param options.traditional a boolean flag that should be true if you wish to use the traditional style of param serialization for the ajax request
336
367
  * @param options.quietMillis (optional) milliseconds to wait before making the ajaxRequest, helps debounce the ajax function if invoked too often
337
368
  * @param options.results a function(remoteData, pageNumber) that converts data returned form the remote request to the format expected by Select2.
338
369
  * The expected format is an object containing the following keys:
@@ -355,9 +386,15 @@ the specific language governing permissions and limitations under the Apache Lic
355
386
  var requestNumber = requestSequence, // this request's sequence number
356
387
  data = options.data, // ajax data function
357
388
  url = ajaxUrl, // ajax url string or function
358
- transport = options.transport || $.ajax,
359
- type = options.type || 'GET', // set type of request (GET or POST)
360
- params = {};
389
+ transport = options.transport || $.fn.select2.ajaxDefaults.transport,
390
+ // deprecated - to be removed in 4.0 - use params instead
391
+ deprecated = {
392
+ type: options.type || 'GET', // set type of request (GET or POST)
393
+ cache: options.cache || false,
394
+ jsonpCallback: options.jsonpCallback||undefined,
395
+ dataType: options.dataType||"json"
396
+ },
397
+ params = $.extend({}, $.fn.select2.ajaxDefaults.params, deprecated);
361
398
 
362
399
  data = data ? data.call(self, query.term, query.page, query.context) : null;
363
400
  url = (typeof url === 'function') ? url.call(self, query.term, query.page, query.context) : url;
@@ -376,8 +413,6 @@ the specific language governing permissions and limitations under the Apache Lic
376
413
  url: url,
377
414
  dataType: options.dataType,
378
415
  data: data,
379
- type: type,
380
- cache: false,
381
416
  success: function (data) {
382
417
  if (requestNumber < requestSequence) {
383
418
  return;
@@ -412,12 +447,12 @@ the specific language governing permissions and limitations under the Apache Lic
412
447
  tmp,
413
448
  text = function (item) { return ""+item.text; }; // function used to retrieve the text portion of a data item that is matched against the search
414
449
 
415
- if ($.isArray(data)) {
450
+ if ($.isArray(data)) {
416
451
  tmp = data;
417
452
  data = { results: tmp };
418
453
  }
419
454
 
420
- if ($.isFunction(data) === false) {
455
+ if ($.isFunction(data) === false) {
421
456
  tmp = data;
422
457
  data = function() { return tmp; };
423
458
  }
@@ -427,7 +462,7 @@ the specific language governing permissions and limitations under the Apache Lic
427
462
  text = dataItem.text;
428
463
  // if text is not a function we assume it to be a key name
429
464
  if (!$.isFunction(text)) {
430
- dataText = data.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available
465
+ dataText = dataItem.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available
431
466
  text = function (item) { return item[dataText]; };
432
467
  }
433
468
  }
@@ -590,7 +625,7 @@ the specific language governing permissions and limitations under the Apache Lic
590
625
 
591
626
  // abstract
592
627
  init: function (opts) {
593
- var results, search, resultsSelector = ".select2-results", mask;
628
+ var results, search, resultsSelector = ".select2-results", disabled, readonly;
594
629
 
595
630
  // prepare options
596
631
  this.opts = opts = this.prepareOpts(opts);
@@ -603,7 +638,6 @@ the specific language governing permissions and limitations under the Apache Lic
603
638
  this.destroy();
604
639
  }
605
640
 
606
- this.enabled=true;
607
641
  this.container = this.createContainer();
608
642
 
609
643
  this.containerId="s2id_"+(opts.element.attr("id") || "autogen"+nextUid());
@@ -618,14 +652,12 @@ the specific language governing permissions and limitations under the Apache Lic
618
652
  this.container.css(evaluate(opts.containerCss));
619
653
  this.container.addClass(evaluate(opts.containerCssClass));
620
654
 
621
- this.elementTabIndex = this.opts.element.attr("tabIndex");
655
+ this.elementTabIndex = this.opts.element.attr("tabindex");
622
656
 
623
657
  // swap container for the element
624
658
  this.opts.element
625
659
  .data("select2", this)
626
- .addClass("select2-offscreen")
627
- .bind("focus.select2", function() { $(this).select2("focus"); })
628
- .attr("tabIndex", "-1")
660
+ .attr("tabindex", "-1")
629
661
  .before(this.container);
630
662
  this.container.data("select2", this);
631
663
 
@@ -636,8 +668,6 @@ the specific language governing permissions and limitations under the Apache Lic
636
668
  this.results = results = this.container.find(resultsSelector);
637
669
  this.search = search = this.container.find("input.select2-input");
638
670
 
639
- search.attr("tabIndex", this.elementTabIndex);
640
-
641
671
  this.resultsPage = 0;
642
672
  this.context = null;
643
673
 
@@ -645,10 +675,14 @@ the specific language governing permissions and limitations under the Apache Lic
645
675
  this.initContainer();
646
676
 
647
677
  installFilteredMouseMove(this.results);
648
- this.dropdown.delegate(resultsSelector, "mousemove-filtered touchstart touchmove touchend", this.bind(this.highlightUnderEvent));
678
+ this.dropdown.on("mousemove-filtered touchstart touchmove touchend", resultsSelector, this.bind(this.highlightUnderEvent));
649
679
 
650
680
  installDebouncedScroll(80, this.results);
651
- this.dropdown.delegate(resultsSelector, "scroll-debounced", this.bind(this.loadMoreIfNeeded));
681
+ this.dropdown.on("scroll-debounced", resultsSelector, this.bind(this.loadMoreIfNeeded));
682
+
683
+ // do not propagate change event from the search field out of the component
684
+ $(this.container).on("change", ".select2-input", function(e) {e.stopPropagation();});
685
+ $(this.dropdown).on("change", ".select2-input", function(e) {e.stopPropagation();});
652
686
 
653
687
  // if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel
654
688
  if ($.fn.mousewheel) {
@@ -665,11 +699,11 @@ the specific language governing permissions and limitations under the Apache Lic
665
699
  }
666
700
 
667
701
  installKeyUpChangeEvent(search);
668
- search.bind("keyup-change input paste", this.bind(this.updateResults));
669
- search.bind("focus", function () { search.addClass("select2-focused"); });
670
- search.bind("blur", function () { search.removeClass("select2-focused");});
702
+ search.on("keyup-change input paste", this.bind(this.updateResults));
703
+ search.on("focus", function () { search.addClass("select2-focused"); });
704
+ search.on("blur", function () { search.removeClass("select2-focused");});
671
705
 
672
- this.dropdown.delegate(resultsSelector, "mouseup", this.bind(function (e) {
706
+ this.dropdown.on("mouseup", resultsSelector, this.bind(function (e) {
673
707
  if ($(e.target).closest(".select2-result-selectable").length > 0) {
674
708
  this.highlightUnderEvent(e);
675
709
  this.selectHighlighted(e);
@@ -679,7 +713,7 @@ the specific language governing permissions and limitations under the Apache Lic
679
713
  // trap all mouse events from leaving the dropdown. sometimes there may be a modal that is listening
680
714
  // for mouse events outside of itself so it can close itself. since the dropdown is now outside the select2's
681
715
  // dom it will trigger the popup close, which is not what we want
682
- this.dropdown.bind("click mouseup mousedown", function (e) { e.stopPropagation(); });
716
+ this.dropdown.on("click mouseup mousedown", function (e) { e.stopPropagation(); });
683
717
 
684
718
  if ($.isFunction(this.opts.initSelection)) {
685
719
  // initialize selection based on the current value of the source element
@@ -690,7 +724,24 @@ the specific language governing permissions and limitations under the Apache Lic
690
724
  this.monitorSource();
691
725
  }
692
726
 
693
- if (opts.element.is(":disabled") || opts.element.is("[readonly='readonly']")) this.disable();
727
+ if (opts.maximumInputLength !== null) {
728
+ this.search.attr("maxlength", opts.maximumInputLength);
729
+ }
730
+
731
+ var disabled = opts.element.prop("disabled");
732
+ if (disabled === undefined) disabled = false;
733
+ this.enable(!disabled);
734
+
735
+ var readonly = opts.element.prop("readonly");
736
+ if (readonly === undefined) readonly = false;
737
+ this.readonly(readonly);
738
+
739
+ // Calculate size of scrollbar
740
+ scrollBarDimensions = scrollBarDimensions || measureScrollbar();
741
+
742
+ this.autofocus = opts.element.prop("autofocus")
743
+ opts.element.prop("autofocus", false);
744
+ if (this.autofocus) this.focus();
694
745
  },
695
746
 
696
747
  // abstract
@@ -706,15 +757,37 @@ the specific language governing permissions and limitations under the Apache Lic
706
757
  select2.opts.element
707
758
  .removeClass("select2-offscreen")
708
759
  .removeData("select2")
709
- .unbind(".select2")
710
- .attr({"tabIndex": this.elementTabIndex})
760
+ .off(".select2")
761
+ .attr({"tabindex": this.elementTabIndex})
762
+ .prop("autofocus", this.autofocus||false)
711
763
  .show();
712
764
  }
713
765
  },
714
766
 
767
+ // abstract
768
+ optionToData: function(element) {
769
+ if (element.is("option")) {
770
+ return {
771
+ id:element.prop("value"),
772
+ text:element.text(),
773
+ element: element.get(),
774
+ css: element.attr("class"),
775
+ disabled: element.prop("disabled"),
776
+ locked: equal(element.attr("locked"), "locked")
777
+ };
778
+ } else if (element.is("optgroup")) {
779
+ return {
780
+ text:element.attr("label"),
781
+ children:[],
782
+ element: element.get(),
783
+ css: element.attr("class")
784
+ };
785
+ }
786
+ },
787
+
715
788
  // abstract
716
789
  prepareOpts: function (opts) {
717
- var element, select, idKey, ajaxUrl;
790
+ var element, select, idKey, ajaxUrl, self = this;
718
791
 
719
792
  element = opts.element;
720
793
 
@@ -733,7 +806,7 @@ the specific language governing permissions and limitations under the Apache Lic
733
806
 
734
807
  opts = $.extend({}, {
735
808
  populateResults: function(container, results, query) {
736
- var populate, data, result, children, id=this.opts.id, self=this;
809
+ var populate, data, result, children, id=this.opts.id;
737
810
 
738
811
  populate=function(results, container, depth) {
739
812
 
@@ -807,10 +880,10 @@ the specific language governing permissions and limitations under the Apache Lic
807
880
  var group;
808
881
  if (element.is("option")) {
809
882
  if (query.matcher(term, element.text(), element)) {
810
- collection.push({id:element.attr("value"), text:element.text(), element: element.get(), css: element.attr("class"), disabled: equal(element.attr("disabled"), "disabled") });
883
+ collection.push(self.optionToData(element));
811
884
  }
812
885
  } else if (element.is("optgroup")) {
813
- group={text:element.attr("label"), children:[], element: element.get(), css: element.attr("class")};
886
+ group=self.optionToData(element);
814
887
  element.children().each2(function(i, elm) { process(elm, group.children); });
815
888
  if (group.children.length>0) {
816
889
  collection.push(group);
@@ -881,7 +954,7 @@ the specific language governing permissions and limitations under the Apache Lic
881
954
  monitorSource: function () {
882
955
  var el = this.opts.element, sync;
883
956
 
884
- el.bind("change.select2", this.bind(function (e) {
957
+ el.on("change.select2", this.bind(function (e) {
885
958
  if (this.opts.element.data("select2-change-triggered") !== true) {
886
959
  this.initSelection();
887
960
  }
@@ -893,19 +966,13 @@ the specific language governing permissions and limitations under the Apache Lic
893
966
 
894
967
  // sync enabled state
895
968
 
896
- enabled = this.opts.element.attr("disabled") !== "disabled";
897
- readonly = this.opts.element.attr("readonly") === "readonly";
898
-
899
- enabled = enabled && !readonly;
900
-
901
- if (this.enabled !== enabled) {
902
- if (enabled) {
903
- this.enable();
904
- } else {
905
- this.disable();
906
- }
907
- }
969
+ var disabled = el.prop("disabled");
970
+ if (disabled === undefined) disabled = false;
971
+ this.enable(!disabled);
908
972
 
973
+ var readonly = el.prop("readonly");
974
+ if (readonly === undefined) readonly = false;
975
+ this.readonly(readonly);
909
976
 
910
977
  syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass);
911
978
  this.container.addClass(evaluate(this.opts.containerCssClass));
@@ -916,17 +983,31 @@ the specific language governing permissions and limitations under the Apache Lic
916
983
  });
917
984
 
918
985
  // mozilla and IE
919
- el.bind("propertychange.select2 DOMAttrModified.select2", sync);
986
+ el.on("propertychange.select2 DOMAttrModified.select2", sync);
987
+
988
+
989
+ // hold onto a reference of the callback to work around a chromium bug
990
+ if (this.mutationCallback === undefined) {
991
+ this.mutationCallback = function (mutations) {
992
+ mutations.forEach(sync);
993
+ }
994
+ }
995
+
920
996
  // safari and chrome
921
997
  if (typeof WebKitMutationObserver !== "undefined") {
922
998
  if (this.propertyObserver) { delete this.propertyObserver; this.propertyObserver = null; }
923
- this.propertyObserver = new WebKitMutationObserver(function (mutations) {
924
- mutations.forEach(sync);
925
- });
999
+ this.propertyObserver = new WebKitMutationObserver(this.mutationCallback);
926
1000
  this.propertyObserver.observe(el.get(0), { attributes:true, subtree:false });
927
1001
  }
928
1002
  },
929
1003
 
1004
+ // abstract
1005
+ triggerSelect: function(data) {
1006
+ var evt = $.Event("select2-selecting", { val: this.id(data), object: data });
1007
+ this.opts.element.trigger(evt);
1008
+ return !evt.isDefaultPrevented();
1009
+ },
1010
+
930
1011
  /**
931
1012
  * Triggers the change event on the source element
932
1013
  */
@@ -950,24 +1031,46 @@ the specific language governing permissions and limitations under the Apache Lic
950
1031
  this.opts.element.blur();
951
1032
  },
952
1033
 
1034
+ //abstract
1035
+ isInterfaceEnabled: function()
1036
+ {
1037
+ return this.enabledInterface === true;
1038
+ },
1039
+
953
1040
  // abstract
954
- enable: function() {
955
- if (this.enabled) return;
1041
+ enableInterface: function() {
1042
+ var enabled = this._enabled && !this._readonly,
1043
+ disabled = !enabled;
1044
+
1045
+ if (enabled === this.enabledInterface) return false;
956
1046
 
957
- this.enabled=true;
958
- this.container.removeClass("select2-container-disabled");
959
- this.opts.element.removeAttr("disabled");
1047
+ this.container.toggleClass("select2-container-disabled", disabled);
1048
+ this.close();
1049
+ this.enabledInterface = enabled;
1050
+
1051
+ return true;
960
1052
  },
961
1053
 
962
1054
  // abstract
963
- disable: function() {
964
- if (!this.enabled) return;
1055
+ enable: function(enabled) {
1056
+ if (enabled === undefined) enabled = true;
1057
+ if (this._enabled === enabled) return false;
1058
+ this._enabled = enabled;
965
1059
 
966
- this.close();
1060
+ this.opts.element.prop("disabled", !enabled);
1061
+ this.enableInterface();
1062
+ return true;
1063
+ },
967
1064
 
968
- this.enabled=false;
969
- this.container.addClass("select2-container-disabled");
970
- this.opts.element.attr("disabled", "disabled");
1065
+ // abstract
1066
+ readonly: function(enabled) {
1067
+ if (enabled === undefined) enabled = false;
1068
+ if (this._readonly === enabled) return false;
1069
+ this._readonly = enabled;
1070
+
1071
+ this.opts.element.prop("readonly", enabled);
1072
+ this.enableInterface();
1073
+ return true;
971
1074
  },
972
1075
 
973
1076
  // abstract
@@ -977,22 +1080,37 @@ the specific language governing permissions and limitations under the Apache Lic
977
1080
 
978
1081
  // abstract
979
1082
  positionDropdown: function() {
980
- var offset = this.container.offset(),
1083
+ var $dropdown = this.dropdown,
1084
+ offset = this.container.offset(),
981
1085
  height = this.container.outerHeight(false),
982
1086
  width = this.container.outerWidth(false),
983
- dropHeight = this.dropdown.outerHeight(false),
984
- viewPortRight = $(window).scrollLeft() + $(window).width(),
1087
+ dropHeight = $dropdown.outerHeight(false),
1088
+ viewPortRight = $(window).scrollLeft() + $(window).width(),
985
1089
  viewportBottom = $(window).scrollTop() + $(window).height(),
986
1090
  dropTop = offset.top + height,
987
1091
  dropLeft = offset.left,
988
1092
  enoughRoomBelow = dropTop + dropHeight <= viewportBottom,
989
1093
  enoughRoomAbove = (offset.top - dropHeight) >= this.body().scrollTop(),
990
- dropWidth = this.dropdown.outerWidth(false),
991
- enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight,
992
- aboveNow = this.dropdown.hasClass("select2-drop-above"),
1094
+ dropWidth = $dropdown.outerWidth(false),
1095
+ enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight,
1096
+ aboveNow = $dropdown.hasClass("select2-drop-above"),
993
1097
  bodyOffset,
994
1098
  above,
995
- css;
1099
+ css,
1100
+ resultsListNode;
1101
+
1102
+ if (this.opts.dropdownAutoWidth) {
1103
+ resultsListNode = $('.select2-results', $dropdown)[0];
1104
+ $dropdown.addClass('select2-drop-auto-width');
1105
+ $dropdown.css('width', '');
1106
+ // Add scrollbar width to dropdown if vertical scrollbar is present
1107
+ dropWidth = $dropdown.outerWidth(false) + (resultsListNode.scrollHeight === resultsListNode.clientHeight ? 0 : scrollBarDimensions.width);
1108
+ dropWidth > width ? width = dropWidth : dropWidth = width;
1109
+ enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight;
1110
+ }
1111
+ else {
1112
+ this.container.removeClass('select2-drop-auto-width');
1113
+ }
996
1114
 
997
1115
  //console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow);
998
1116
  //console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body().scrollTop(), "enough?", enoughRoomAbove);
@@ -1022,11 +1140,11 @@ the specific language governing permissions and limitations under the Apache Lic
1022
1140
  if (above) {
1023
1141
  dropTop = offset.top - dropHeight;
1024
1142
  this.container.addClass("select2-drop-above");
1025
- this.dropdown.addClass("select2-drop-above");
1143
+ $dropdown.addClass("select2-drop-above");
1026
1144
  }
1027
1145
  else {
1028
1146
  this.container.removeClass("select2-drop-above");
1029
- this.dropdown.removeClass("select2-drop-above");
1147
+ $dropdown.removeClass("select2-drop-above");
1030
1148
  }
1031
1149
 
1032
1150
  css = $.extend({
@@ -1035,7 +1153,7 @@ the specific language governing permissions and limitations under the Apache Lic
1035
1153
  width: width
1036
1154
  }, evaluate(this.opts.dropdownCss));
1037
1155
 
1038
- this.dropdown.css(css);
1156
+ $dropdown.css(css);
1039
1157
  },
1040
1158
 
1041
1159
  // abstract
@@ -1044,7 +1162,9 @@ the specific language governing permissions and limitations under the Apache Lic
1044
1162
 
1045
1163
  if (this.opened()) return false;
1046
1164
 
1047
- event = $.Event("opening");
1165
+ if (this._enabled === false || this._readonly === true) return false;
1166
+
1167
+ event = $.Event("select2-opening");
1048
1168
  this.opts.element.trigger(event);
1049
1169
  return !event.isDefaultPrevented();
1050
1170
  },
@@ -1067,7 +1187,7 @@ the specific language governing permissions and limitations under the Apache Lic
1067
1187
 
1068
1188
  if (!this.shouldOpen()) return false;
1069
1189
 
1070
- window.setTimeout(this.bind(this.opening), 1);
1190
+ this.opening();
1071
1191
 
1072
1192
  return true;
1073
1193
  },
@@ -1083,17 +1203,14 @@ the specific language governing permissions and limitations under the Apache Lic
1083
1203
  orient = "orientationchange."+cid,
1084
1204
  mask;
1085
1205
 
1086
- this.clearDropdownAlignmentPreference();
1087
-
1088
1206
  this.container.addClass("select2-dropdown-open").addClass("select2-container-active");
1089
1207
 
1208
+ this.clearDropdownAlignmentPreference();
1090
1209
 
1091
1210
  if(this.dropdown[0] !== this.body().children().last()[0]) {
1092
1211
  this.dropdown.detach().appendTo(this.body());
1093
1212
  }
1094
1213
 
1095
- this.updateResults(true);
1096
-
1097
1214
  // create the dropdown mask if doesnt already exist
1098
1215
  mask = $("#select2-drop-mask");
1099
1216
  if (mask.length == 0) {
@@ -1101,7 +1218,7 @@ the specific language governing permissions and limitations under the Apache Lic
1101
1218
  mask.attr("id","select2-drop-mask").attr("class","select2-drop-mask");
1102
1219
  mask.hide();
1103
1220
  mask.appendTo(this.body());
1104
- mask.bind("mousedown touchstart", function (e) {
1221
+ mask.on("mousedown touchstart", function (e) {
1105
1222
  var dropdown = $("#select2-drop"), self;
1106
1223
  if (dropdown.length > 0) {
1107
1224
  self=dropdown.data("select2");
@@ -1109,6 +1226,8 @@ the specific language governing permissions and limitations under the Apache Lic
1109
1226
  self.selectHighlighted({noFocus: true});
1110
1227
  }
1111
1228
  self.close();
1229
+ e.preventDefault();
1230
+ e.stopPropagation();
1112
1231
  }
1113
1232
  });
1114
1233
  }
@@ -1135,14 +1254,12 @@ the specific language governing permissions and limitations under the Apache Lic
1135
1254
  // the position of the dropdown to be updated as well so it does not come unglued from the container
1136
1255
  var that = this;
1137
1256
  this.container.parents().add(window).each(function () {
1138
- $(this).bind(resize+" "+scroll+" "+orient, function (e) {
1257
+ $(this).on(resize+" "+scroll+" "+orient, function (e) {
1139
1258
  $("#select2-drop-mask").css(_makeMaskCss());
1140
1259
  that.positionDropdown();
1141
1260
  });
1142
1261
  });
1143
1262
 
1144
- this.focusSearch();
1145
-
1146
1263
  function _makeMaskCss() {
1147
1264
  return {
1148
1265
  width : Math.max(document.documentElement.scrollWidth, $(window).width()),
@@ -1161,7 +1278,7 @@ the specific language governing permissions and limitations under the Apache Lic
1161
1278
  orient = "orientationchange."+cid;
1162
1279
 
1163
1280
  // unbind event listeners
1164
- this.container.parents().add(window).each(function () { $(this).unbind(scroll).unbind(resize).unbind(orient); });
1281
+ this.container.parents().add(window).each(function () { $(this).off(scroll).off(resize).off(orient); });
1165
1282
 
1166
1283
  this.clearDropdownAlignmentPreference();
1167
1284
 
@@ -1170,9 +1287,11 @@ the specific language governing permissions and limitations under the Apache Lic
1170
1287
  this.dropdown.hide();
1171
1288
  this.container.removeClass("select2-dropdown-open");
1172
1289
  this.results.empty();
1290
+
1291
+
1173
1292
  this.clearSearch();
1174
1293
  this.search.removeClass("select2-active");
1175
- this.opts.element.trigger($.Event("close"));
1294
+ this.opts.element.trigger($.Event("select2-close"));
1176
1295
  },
1177
1296
 
1178
1297
  // abstract
@@ -1203,7 +1322,7 @@ the specific language governing permissions and limitations under the Apache Lic
1203
1322
  return;
1204
1323
  }
1205
1324
 
1206
- children = this.findHighlightableChoices();
1325
+ children = this.findHighlightableChoices().find('.select2-result-label');
1207
1326
 
1208
1327
  child = $(children[index]);
1209
1328
 
@@ -1231,7 +1350,6 @@ the specific language governing permissions and limitations under the Apache Lic
1231
1350
 
1232
1351
  // abstract
1233
1352
  findHighlightableChoices: function() {
1234
- var h=this.results.find(".select2-result-selectable:not(.select2-selected):not(.select2-disabled)");
1235
1353
  return this.results.find(".select2-result-selectable:not(.select2-selected):not(.select2-disabled)");
1236
1354
  },
1237
1355
 
@@ -1272,7 +1390,7 @@ the specific language governing permissions and limitations under the Apache Lic
1272
1390
 
1273
1391
  data = choice.data("select2-data");
1274
1392
  if (data) {
1275
- this.opts.element.trigger({ type: "highlight", val: this.id(data), choice: data });
1393
+ this.opts.element.trigger({ type: "select2-highlight", val: this.id(data), choice: data });
1276
1394
  }
1277
1395
  },
1278
1396
 
@@ -1285,7 +1403,7 @@ the specific language governing permissions and limitations under the Apache Lic
1285
1403
  highlightUnderEvent: function (event) {
1286
1404
  var el = $(event.target).closest(".select2-result-selectable");
1287
1405
  if (el.length > 0 && !el.is(".select2-highlighted")) {
1288
- var choices = this.findHighlightableChoices();
1406
+ var choices = this.findHighlightableChoices();
1289
1407
  this.highlight(choices.index(el));
1290
1408
  } else if (el.length == 0) {
1291
1409
  // if we are over an unselectable item remove al highlights
@@ -1383,8 +1501,8 @@ the specific language governing permissions and limitations under the Apache Lic
1383
1501
  if (maxSelSize >=1) {
1384
1502
  data = this.data();
1385
1503
  if ($.isArray(data) && data.length >= maxSelSize && checkFormatter(opts.formatSelectionTooBig, "formatSelectionTooBig")) {
1386
- render("<li class='select2-selection-limit'>" + opts.formatSelectionTooBig(maxSelSize) + "</li>");
1387
- return;
1504
+ render("<li class='select2-selection-limit'>" + opts.formatSelectionTooBig(maxSelSize) + "</li>");
1505
+ return;
1388
1506
  }
1389
1507
  }
1390
1508
 
@@ -1394,6 +1512,7 @@ the specific language governing permissions and limitations under the Apache Lic
1394
1512
  } else {
1395
1513
  render("");
1396
1514
  }
1515
+ if (initial) this.showSearch(true);
1397
1516
  return;
1398
1517
  }
1399
1518
 
@@ -1467,7 +1586,7 @@ the specific language governing permissions and limitations under the Apache Lic
1467
1586
 
1468
1587
  postRender();
1469
1588
 
1470
- this.opts.element.trigger({ type: "loaded", data:data });
1589
+ this.opts.element.trigger({ type: "select2-loaded", data:data });
1471
1590
  })});
1472
1591
  },
1473
1592
 
@@ -1537,18 +1656,18 @@ the specific language governing permissions and limitations under the Apache Lic
1537
1656
  attrs = style.split(';');
1538
1657
  for (i = 0, l = attrs.length; i < l; i = i + 1) {
1539
1658
  matches = attrs[i].replace(/\s/g, '')
1540
- .match(/width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/);
1659
+ .match(/width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i);
1541
1660
  if (matches !== null && matches.length >= 1)
1542
1661
  return matches[1];
1543
1662
  }
1544
1663
  }
1545
1664
 
1546
- if (this.opts.width === "resolve") {
1547
- // next check if css('width') can resolve a width that is percent based, this is sometimes possible
1548
- // when attached to input type=hidden or elements hidden via css
1549
- style = this.opts.element.css('width');
1550
- if (style.indexOf("%") > 0) return style;
1665
+ // next check if css('width') can resolve a width that is percent based, this is sometimes possible
1666
+ // when attached to input type=hidden or elements hidden via css
1667
+ style = this.opts.element.css('width');
1668
+ if (style && style.length > 0) return style;
1551
1669
 
1670
+ if (this.opts.width === "resolve") {
1552
1671
  // finally, fallback on the calculated width of the element
1553
1672
  return (this.opts.element.outerWidth(false) === 0 ? 'auto' : this.opts.element.outerWidth(false) + 'px');
1554
1673
  }
@@ -1572,18 +1691,18 @@ the specific language governing permissions and limitations under the Apache Lic
1572
1691
 
1573
1692
  // single
1574
1693
 
1575
- createContainer: function () {
1694
+ createContainer: function () {
1576
1695
  var container = $(document.createElement("div")).attr({
1577
1696
  "class": "select2-container"
1578
1697
  }).html([
1579
1698
  "<a href='javascript:void(0)' onclick='return false;' class='select2-choice' tabindex='-1'>",
1580
- " <span></span><abbr class='select2-search-choice-close' style='display:none;'></abbr>",
1699
+ " <span>&nbsp;</span><abbr class='select2-search-choice-close'></abbr>",
1581
1700
  " <div><b></b></div>" ,
1582
1701
  "</a>",
1583
1702
  "<input class='select2-focusser select2-offscreen' type='text'/>",
1584
- "<div class='select2-drop' style='display:none'>" ,
1703
+ "<div class='select2-drop select2-display-none'>" ,
1585
1704
  " <div class='select2-search'>" ,
1586
- " <input type='text' autocomplete='off' class='select2-input'/>" ,
1705
+ " <input type='text' autocomplete='off' autocorrect='off' autocapitilize='off' spellcheck='false' class='select2-input'/>" ,
1587
1706
  " </div>" ,
1588
1707
  " <ul class='select2-results'>" ,
1589
1708
  " </ul>" ,
@@ -1592,29 +1711,35 @@ the specific language governing permissions and limitations under the Apache Lic
1592
1711
  },
1593
1712
 
1594
1713
  // single
1595
- disable: function() {
1596
- if (!this.enabled) return;
1597
-
1598
- this.parent.disable.apply(this, arguments);
1599
-
1600
- this.focusser.attr("disabled", "disabled");
1601
- },
1602
-
1603
- // single
1604
- enable: function() {
1605
- if (this.enabled) return;
1606
-
1607
- this.parent.enable.apply(this, arguments);
1608
-
1609
- this.focusser.removeAttr("disabled");
1714
+ enableInterface: function() {
1715
+ if (this.parent.enableInterface.apply(this, arguments)) {
1716
+ this.focusser.prop("disabled", !this.isInterfaceEnabled());
1717
+ }
1610
1718
  },
1611
1719
 
1612
1720
  // single
1613
1721
  opening: function () {
1722
+ var el, range;
1614
1723
  this.parent.opening.apply(this, arguments);
1615
- this.focusser.attr("disabled", "disabled");
1724
+ if (this.showSearchInput !== false) {
1725
+ // IE appends focusser.val() at the end of field :/ so we manually insert it at the beginning using a range
1726
+ // all other browsers handle this just fine
1616
1727
 
1617
- this.opts.element.trigger($.Event("open"));
1728
+ this.search.val(this.focusser.val());
1729
+ }
1730
+ this.search.focus();
1731
+ // in IE we have to move the cursor to the end after focussing, otherwise it will be at the beginning and
1732
+ // new text will appear *before* focusser.val()
1733
+ el = this.search.get(0);
1734
+ if (el.createTextRange) {
1735
+ range = el.createTextRange();
1736
+ range.collapse(false);
1737
+ range.select();
1738
+ }
1739
+
1740
+ this.focusser.prop("disabled", true).val("");
1741
+ this.updateResults(true);
1742
+ this.opts.element.trigger($.Event("select2-open"));
1618
1743
  },
1619
1744
 
1620
1745
  // single
@@ -1622,7 +1747,7 @@ the specific language governing permissions and limitations under the Apache Lic
1622
1747
  if (!this.opened()) return;
1623
1748
  this.parent.close.apply(this, arguments);
1624
1749
  this.focusser.removeAttr("disabled");
1625
- focus(this.focusser);
1750
+ this.focusser.focus();
1626
1751
  },
1627
1752
 
1628
1753
  // single
@@ -1652,10 +1777,9 @@ the specific language governing permissions and limitations under the Apache Lic
1652
1777
 
1653
1778
  var selection,
1654
1779
  container = this.container,
1655
- dropdown = this.dropdown,
1656
- clickingInside = false;
1780
+ dropdown = this.dropdown;
1657
1781
 
1658
- this.showSearch(this.opts.minimumResultsForSearch >= 0);
1782
+ this.showSearch(false);
1659
1783
 
1660
1784
  this.selection = selection = container.find(".select2-choice");
1661
1785
 
@@ -1663,11 +1787,14 @@ the specific language governing permissions and limitations under the Apache Lic
1663
1787
 
1664
1788
  // rewrite labels from original element to focusser
1665
1789
  this.focusser.attr("id", "s2id_autogen"+nextUid());
1790
+
1666
1791
  $("label[for='" + this.opts.element.attr("id") + "']")
1667
1792
  .attr('for', this.focusser.attr('id'));
1668
1793
 
1669
- this.search.bind("keydown", this.bind(function (e) {
1670
- if (!this.enabled) return;
1794
+ this.focusser.attr("tabindex", this.elementTabIndex);
1795
+
1796
+ this.search.on("keydown", this.bind(function (e) {
1797
+ if (!this.isInterfaceEnabled()) return;
1671
1798
 
1672
1799
  if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) {
1673
1800
  // prevent the page from scrolling
@@ -1681,11 +1808,13 @@ the specific language governing permissions and limitations under the Apache Lic
1681
1808
  this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
1682
1809
  killEvent(e);
1683
1810
  return;
1684
- case KEY.TAB:
1685
1811
  case KEY.ENTER:
1686
1812
  this.selectHighlighted();
1687
1813
  killEvent(e);
1688
1814
  return;
1815
+ case KEY.TAB:
1816
+ this.selectHighlighted({noFocus: true});
1817
+ return;
1689
1818
  case KEY.ESC:
1690
1819
  this.cancel(e);
1691
1820
  killEvent(e);
@@ -1693,7 +1822,7 @@ the specific language governing permissions and limitations under the Apache Lic
1693
1822
  }
1694
1823
  }));
1695
1824
 
1696
- this.search.bind("blur", this.bind(function(e) {
1825
+ this.search.on("blur", this.bind(function(e) {
1697
1826
  // a workaround for chrome to keep the search field focussed when the scroll bar is used to scroll the dropdown.
1698
1827
  // without this the search field loses focus which is annoying
1699
1828
  if (document.activeElement === this.body().get(0)) {
@@ -1703,8 +1832,8 @@ the specific language governing permissions and limitations under the Apache Lic
1703
1832
  }
1704
1833
  }));
1705
1834
 
1706
- this.focusser.bind("keydown", this.bind(function (e) {
1707
- if (!this.enabled) return;
1835
+ this.focusser.on("keydown", this.bind(function (e) {
1836
+ if (!this.isInterfaceEnabled()) return;
1708
1837
 
1709
1838
  if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) {
1710
1839
  return;
@@ -1733,56 +1862,61 @@ the specific language governing permissions and limitations under the Apache Lic
1733
1862
 
1734
1863
 
1735
1864
  installKeyUpChangeEvent(this.focusser);
1736
- this.focusser.bind("keyup-change input", this.bind(function(e) {
1865
+ this.focusser.on("keyup-change input", this.bind(function(e) {
1866
+ e.stopPropagation();
1737
1867
  if (this.opened()) return;
1738
1868
  this.open();
1739
- if (this.showSearchInput !== false) {
1740
- this.search.val(this.focusser.val());
1741
- }
1742
- this.focusser.val("");
1743
- killEvent(e);
1744
1869
  }));
1745
1870
 
1746
- selection.delegate("abbr", "mousedown", this.bind(function (e) {
1747
- if (!this.enabled) return;
1871
+ selection.on("mousedown", "abbr", this.bind(function (e) {
1872
+ if (!this.isInterfaceEnabled()) return;
1748
1873
  this.clear();
1749
1874
  killEventImmediately(e);
1750
1875
  this.close();
1751
1876
  this.selection.focus();
1752
1877
  }));
1753
1878
 
1754
- selection.bind("mousedown", this.bind(function (e) {
1755
- clickingInside = true;
1879
+ selection.on("mousedown", this.bind(function (e) {
1880
+
1881
+ if (!this.container.hasClass("select2-container-active")) {
1882
+ this.opts.element.trigger($.Event("select2-focus"));
1883
+ }
1756
1884
 
1757
1885
  if (this.opened()) {
1758
1886
  this.close();
1759
- } else if (this.enabled) {
1887
+ } else if (this.isInterfaceEnabled()) {
1760
1888
  this.open();
1761
1889
  }
1762
1890
 
1763
1891
  killEvent(e);
1764
-
1765
- clickingInside = false;
1766
1892
  }));
1767
1893
 
1768
- dropdown.bind("mousedown", this.bind(function() { this.search.focus(); }));
1894
+ dropdown.on("mousedown", this.bind(function() { this.search.focus(); }));
1769
1895
 
1770
- selection.bind("focus", this.bind(function(e) {
1896
+ selection.on("focus", this.bind(function(e) {
1771
1897
  killEvent(e);
1772
1898
  }));
1773
1899
 
1774
- this.focusser.bind("focus", this.bind(function(){
1900
+ this.focusser.on("focus", this.bind(function(){
1901
+ if (!this.container.hasClass("select2-container-active")) {
1902
+ this.opts.element.trigger($.Event("select2-focus"));
1903
+ }
1775
1904
  this.container.addClass("select2-container-active");
1776
- })).bind("blur", this.bind(function() {
1905
+ })).on("blur", this.bind(function() {
1777
1906
  if (!this.opened()) {
1778
1907
  this.container.removeClass("select2-container-active");
1908
+ this.opts.element.trigger($.Event("select2-blur"));
1779
1909
  }
1780
1910
  }));
1781
- this.search.bind("focus", this.bind(function(){
1911
+ this.search.on("focus", this.bind(function(){
1912
+ if (!this.container.hasClass("select2-container-active")) {
1913
+ this.opts.element.trigger($.Event("select2-focus"));
1914
+ }
1782
1915
  this.container.addClass("select2-container-active");
1783
- }))
1916
+ }));
1784
1917
 
1785
1918
  this.initContainerWidth();
1919
+ this.opts.element.addClass("select2-offscreen");
1786
1920
  this.setPlaceholder();
1787
1921
 
1788
1922
  },
@@ -1797,7 +1931,7 @@ the specific language governing permissions and limitations under the Apache Lic
1797
1931
  this.setPlaceholder();
1798
1932
 
1799
1933
  if (triggerChange !== false){
1800
- this.opts.element.trigger({ type: "removed", val: this.id(data), choice: data });
1934
+ this.opts.element.trigger({ type: "select2-removed", val: this.id(data), choice: data });
1801
1935
  this.triggerChange({removed:data});
1802
1936
  }
1803
1937
  }
@@ -1810,6 +1944,7 @@ the specific language governing permissions and limitations under the Apache Lic
1810
1944
  initSelection: function () {
1811
1945
  var selected;
1812
1946
  if (this.opts.element.val() === "" && this.opts.element.text() === "") {
1947
+ this.updateSelection([]);
1813
1948
  this.close();
1814
1949
  this.setPlaceholder();
1815
1950
  } else {
@@ -1826,15 +1961,15 @@ the specific language governing permissions and limitations under the Apache Lic
1826
1961
 
1827
1962
  // single
1828
1963
  prepareOpts: function () {
1829
- var opts = this.parent.prepareOpts.apply(this, arguments);
1964
+ var opts = this.parent.prepareOpts.apply(this, arguments),
1965
+ self=this;
1830
1966
 
1831
1967
  if (opts.element.get(0).tagName.toLowerCase() === "select") {
1832
1968
  // install the selection initializer
1833
1969
  opts.initSelection = function (element, callback) {
1834
1970
  var selected = element.find(":selected");
1835
1971
  // a single select box always has a value, no need to null check 'selected'
1836
- if ($.isFunction(callback))
1837
- callback({id: selected.attr("value"), text: selected.text(), element:selected});
1972
+ callback(self.optionToData(selected));
1838
1973
  };
1839
1974
  } else if ("data" in opts) {
1840
1975
  // install default initSelection when applied to hidden input and data is local
@@ -1885,7 +2020,7 @@ the specific language governing permissions and limitations under the Apache Lic
1885
2020
 
1886
2021
  this.selection.addClass("select2-default");
1887
2022
 
1888
- this.selection.find("abbr").hide();
2023
+ this.container.removeClass("select2-allowclear");
1889
2024
  }
1890
2025
  },
1891
2026
 
@@ -1907,12 +2042,13 @@ the specific language governing permissions and limitations under the Apache Lic
1907
2042
  this.highlight(selected);
1908
2043
  }
1909
2044
 
1910
- // hide the search box if this is the first we got the results and there are a few of them
2045
+ // show the search box if this is the first we got the results and there are enough of them for search
1911
2046
 
1912
- if (initial === true) {
2047
+ if (initial === true && this.showSearchInput === false) {
1913
2048
  var min=this.opts.minimumResultsForSearch;
1914
- showSearchInput = min < 0 ? false : countResults(data.results) >= min;
1915
- this.showSearch(showSearchInput);
2049
+ if (min>=0) {
2050
+ this.showSearch(countResults(data.results)>=min);
2051
+ }
1916
2052
  }
1917
2053
 
1918
2054
  },
@@ -1921,26 +2057,31 @@ the specific language governing permissions and limitations under the Apache Lic
1921
2057
  showSearch: function(showSearchInput) {
1922
2058
  this.showSearchInput = showSearchInput;
1923
2059
 
1924
- this.dropdown.find(".select2-search")[showSearchInput ? "removeClass" : "addClass"]("select2-search-hidden");
2060
+ this.dropdown.find(".select2-search").toggleClass("select2-search-hidden", !showSearchInput);
2061
+ this.dropdown.find(".select2-search").toggleClass("select2-offscreen", !showSearchInput);
1925
2062
  //add "select2-with-searchbox" to the container if search box is shown
1926
- $(this.dropdown, this.container)[showSearchInput ? "addClass" : "removeClass"]("select2-with-searchbox");
2063
+ $(this.dropdown, this.container).toggleClass("select2-with-searchbox", showSearchInput);
1927
2064
  },
1928
2065
 
1929
2066
  // single
1930
2067
  onSelect: function (data, options) {
1931
- var old = this.opts.element.val();
2068
+
2069
+ if (!this.triggerSelect(data)) { return; }
2070
+
2071
+ var old = this.opts.element.val(),
2072
+ oldData = this.data();
1932
2073
 
1933
2074
  this.opts.element.val(this.id(data));
1934
2075
  this.updateSelection(data);
1935
2076
 
1936
- this.opts.element.trigger({ type: "selected", val: this.id(data), choice: data });
2077
+ this.opts.element.trigger({ type: "select2-selected", val: this.id(data), choice: data });
1937
2078
 
1938
2079
  this.close();
1939
2080
 
1940
2081
  if (!options || !options.noFocus)
1941
2082
  this.selection.focus();
1942
2083
 
1943
- if (!equal(old, this.id(data))) { this.triggerChange(); }
2084
+ if (!equal(old, this.id(data))) { this.triggerChange({added:data,removed:oldData}); }
1944
2085
  },
1945
2086
 
1946
2087
  // single
@@ -1959,13 +2100,17 @@ the specific language governing permissions and limitations under the Apache Lic
1959
2100
  this.selection.removeClass("select2-default");
1960
2101
 
1961
2102
  if (this.opts.allowClear && this.getPlaceholder() !== undefined) {
1962
- this.selection.find("abbr").show();
2103
+ this.container.addClass("select2-allowclear");
1963
2104
  }
1964
2105
  },
1965
2106
 
1966
2107
  // single
1967
2108
  val: function () {
1968
- var val, triggerChange = false, data = null, self = this;
2109
+ var val,
2110
+ triggerChange = false,
2111
+ data = null,
2112
+ self = this,
2113
+ oldData = this.data();
1969
2114
 
1970
2115
  if (arguments.length === 0) {
1971
2116
  return this.opts.element.val();
@@ -1981,13 +2126,13 @@ the specific language governing permissions and limitations under the Apache Lic
1981
2126
  this.select
1982
2127
  .val(val)
1983
2128
  .find(":selected").each2(function (i, elm) {
1984
- data = {id: elm.attr("value"), text: elm.text(), element: elm.get(0)};
2129
+ data = self.optionToData(elm);
1985
2130
  return false;
1986
2131
  });
1987
2132
  this.updateSelection(data);
1988
2133
  this.setPlaceholder();
1989
2134
  if (triggerChange) {
1990
- this.triggerChange();
2135
+ this.triggerChange({added: data, removed:oldData});
1991
2136
  }
1992
2137
  } else {
1993
2138
  if (this.opts.initSelection === undefined) {
@@ -1996,9 +2141,6 @@ the specific language governing permissions and limitations under the Apache Lic
1996
2141
  // val is an id. !val is true for [undefined,null,'',0] - 0 is legal
1997
2142
  if (!val && val !== 0) {
1998
2143
  this.clear(triggerChange);
1999
- if (triggerChange) {
2000
- this.triggerChange();
2001
- }
2002
2144
  return;
2003
2145
  }
2004
2146
  this.opts.element.val(val);
@@ -2007,7 +2149,7 @@ the specific language governing permissions and limitations under the Apache Lic
2007
2149
  self.updateSelection(data);
2008
2150
  self.setPlaceholder();
2009
2151
  if (triggerChange) {
2010
- self.triggerChange();
2152
+ self.triggerChange({added: data, removed:oldData});
2011
2153
  }
2012
2154
  });
2013
2155
  }
@@ -2020,7 +2162,7 @@ the specific language governing permissions and limitations under the Apache Lic
2020
2162
  },
2021
2163
 
2022
2164
  // single
2023
- data: function(value) {
2165
+ data: function(value, triggerChange) {
2024
2166
  var data;
2025
2167
 
2026
2168
  if (arguments.length === 0) {
@@ -2029,10 +2171,14 @@ the specific language governing permissions and limitations under the Apache Lic
2029
2171
  return data;
2030
2172
  } else {
2031
2173
  if (!value || value === "") {
2032
- this.clear();
2174
+ this.clear(triggerChange);
2033
2175
  } else {
2176
+ data = this.data();
2034
2177
  this.opts.element.val(!value ? "" : this.id(value));
2035
2178
  this.updateSelection(value);
2179
+ if (triggerChange) {
2180
+ this.triggerChange({added: value, removed:data});
2181
+ }
2036
2182
  }
2037
2183
  }
2038
2184
  }
@@ -2048,19 +2194,20 @@ the specific language governing permissions and limitations under the Apache Lic
2048
2194
  " <ul class='select2-choices'>",
2049
2195
  //"<li class='select2-search-choice'><span>California</span><a href="javascript:void(0)" class="select2-search-choice-close"></a></li>" ,
2050
2196
  " <li class='select2-search-field'>" ,
2051
- " <input type='text' autocomplete='off' class='select2-input'>" ,
2197
+ " <input type='text' autocomplete='off' autocorrect='off' autocapitilize='off' spellcheck='false' class='select2-input'>" ,
2052
2198
  " </li>" ,
2053
2199
  "</ul>" ,
2054
- "<div class='select2-drop select2-drop-multi' style='display:none;'>" ,
2200
+ "<div class='select2-drop select2-drop-multi select2-display-none'>" ,
2055
2201
  " <ul class='select2-results'>" ,
2056
2202
  " </ul>" ,
2057
2203
  "</div>"].join(""));
2058
- return container;
2204
+ return container;
2059
2205
  },
2060
2206
 
2061
2207
  // multi
2062
2208
  prepareOpts: function () {
2063
- var opts = this.parent.prepareOpts.apply(this, arguments);
2209
+ var opts = this.parent.prepareOpts.apply(this, arguments),
2210
+ self=this;
2064
2211
 
2065
2212
  // TODO validate placeholder is a string if specified
2066
2213
 
@@ -2071,7 +2218,7 @@ the specific language governing permissions and limitations under the Apache Lic
2071
2218
  var data = [];
2072
2219
 
2073
2220
  element.find(":selected").each2(function (i, elm) {
2074
- data.push({id: elm.attr("value"), text: elm.text(), element: elm[0]});
2221
+ data.push(self.optionToData(elm));
2075
2222
  });
2076
2223
  callback(data);
2077
2224
  };
@@ -2092,7 +2239,21 @@ the specific language governing permissions and limitations under the Apache Lic
2092
2239
  return is_match;
2093
2240
  },
2094
2241
  callback: !$.isFunction(callback) ? $.noop : function() {
2095
- callback(matches);
2242
+ // reorder matches based on the order they appear in the ids array because right now
2243
+ // they are in the order in which they appear in data array
2244
+ var ordered = [];
2245
+ for (var i = 0; i < ids.length; i++) {
2246
+ var id = ids[i];
2247
+ for (var j = 0; j < matches.length; j++) {
2248
+ var match = matches[j];
2249
+ if (equal(id, opts.id(match))) {
2250
+ ordered.push(match);
2251
+ matches.splice(j, 1);
2252
+ break;
2253
+ }
2254
+ }
2255
+ }
2256
+ callback(ordered);
2096
2257
  }
2097
2258
  });
2098
2259
  };
@@ -2101,6 +2262,24 @@ the specific language governing permissions and limitations under the Apache Lic
2101
2262
  return opts;
2102
2263
  },
2103
2264
 
2265
+ selectChoice: function (choice) {
2266
+
2267
+ var selected = this.container.find(".select2-search-choice-focus");
2268
+ if (selected.length && choice && choice[0] == selected[0]) {
2269
+
2270
+ } else {
2271
+ if (selected.length) {
2272
+ this.opts.element.trigger("choice-deselected", selected);
2273
+ }
2274
+ selected.removeClass("select2-search-choice-focus");
2275
+ if (choice && choice.length) {
2276
+ this.close();
2277
+ choice.addClass("select2-search-choice-focus");
2278
+ this.opts.element.trigger("choice-selected", choice);
2279
+ }
2280
+ }
2281
+ },
2282
+
2104
2283
  // multi
2105
2284
  initContainer: function () {
2106
2285
 
@@ -2109,39 +2288,77 @@ the specific language governing permissions and limitations under the Apache Lic
2109
2288
  this.searchContainer = this.container.find(".select2-search-field");
2110
2289
  this.selection = selection = this.container.find(selector);
2111
2290
 
2291
+ var _this = this;
2292
+ this.selection.on("mousedown", ".select2-search-choice", function (e) {
2293
+ //killEvent(e);
2294
+ _this.search[0].focus();
2295
+ _this.selectChoice($(this));
2296
+ })
2297
+ //.sortable({
2298
+ // items: " > li",
2299
+ // tolerance: "pointer",
2300
+ // revert: 100
2301
+ //});
2302
+
2112
2303
  // rewrite labels from original element to focusser
2113
2304
  this.search.attr("id", "s2id_autogen"+nextUid());
2114
2305
  $("label[for='" + this.opts.element.attr("id") + "']")
2115
2306
  .attr('for', this.search.attr('id'));
2116
2307
 
2117
- this.search.bind("input paste", this.bind(function() {
2118
- if (!this.enabled) return;
2308
+ this.search.on("input paste", this.bind(function() {
2309
+ if (!this.isInterfaceEnabled()) return;
2119
2310
  if (!this.opened()) {
2120
2311
  this.open();
2121
2312
  }
2122
2313
  }));
2123
2314
 
2124
- this.search.bind("keydown", this.bind(function (e) {
2125
- if (!this.enabled) return;
2315
+ this.search.attr("tabindex", this.elementTabIndex);
2126
2316
 
2127
- if (e.which === KEY.BACKSPACE && this.search.val() === "") {
2128
- this.close();
2317
+ this.keydowns = 0;
2318
+ this.search.on("keydown", this.bind(function (e) {
2319
+ if (!this.isInterfaceEnabled()) return;
2320
+
2321
+ ++this.keydowns;
2322
+ var selected = selection.find(".select2-search-choice-focus");
2323
+ var prev = selected.prev(".select2-search-choice:not(.select2-locked)");
2324
+ var next = selected.next(".select2-search-choice:not(.select2-locked)");
2325
+ var pos = getCursorInfo(this.search);
2129
2326
 
2130
- var choices,
2131
- selected = selection.find(".select2-search-choice-focus");
2132
- if (selected.length > 0) {
2327
+ if (selected.length &&
2328
+ (e.which == KEY.LEFT || e.which == KEY.RIGHT || e.which == KEY.BACKSPACE || e.which == KEY.DELETE || e.which == KEY.ENTER)) {
2329
+ var selectedChoice = selected;
2330
+ if (e.which == KEY.LEFT && prev.length) {
2331
+ selectedChoice = prev;
2332
+ }
2333
+ else if (e.which == KEY.RIGHT) {
2334
+ selectedChoice = next.length ? next : null;
2335
+ }
2336
+ else if (e.which === KEY.BACKSPACE) {
2133
2337
  this.unselect(selected.first());
2134
2338
  this.search.width(10);
2135
- killEvent(e);
2136
- return;
2339
+ selectedChoice = prev.length ? prev : next;
2340
+ } else if (e.which == KEY.DELETE) {
2341
+ this.unselect(selected.first());
2342
+ this.search.width(10);
2343
+ selectedChoice = next.length ? next : null;
2344
+ } else if (e.which == KEY.ENTER) {
2345
+ selectedChoice = null;
2137
2346
  }
2138
2347
 
2139
- choices = selection.find(".select2-search-choice:not(.select2-locked)");
2140
- if (choices.length > 0) {
2141
- choices.last().addClass("select2-search-choice-focus");
2348
+ this.selectChoice(selectedChoice);
2349
+ killEvent(e);
2350
+ if (!selectedChoice || !selectedChoice.length) {
2351
+ this.open();
2142
2352
  }
2353
+ return;
2354
+ } else if (((e.which === KEY.BACKSPACE && this.keydowns == 1)
2355
+ || e.which == KEY.LEFT) && (pos.offset == 0 && !pos.length)) {
2356
+
2357
+ this.selectChoice(selection.find(".select2-search-choice:not(.select2-locked)").last());
2358
+ killEvent(e);
2359
+ return;
2143
2360
  } else {
2144
- selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
2361
+ this.selectChoice(null);
2145
2362
  }
2146
2363
 
2147
2364
  if (this.opened()) {
@@ -2152,10 +2369,12 @@ the specific language governing permissions and limitations under the Apache Lic
2152
2369
  killEvent(e);
2153
2370
  return;
2154
2371
  case KEY.ENTER:
2155
- case KEY.TAB:
2156
2372
  this.selectHighlighted();
2157
2373
  killEvent(e);
2158
2374
  return;
2375
+ case KEY.TAB:
2376
+ this.selectHighlighted({noFocus:true});
2377
+ return;
2159
2378
  case KEY.ESC:
2160
2379
  this.cancel(e);
2161
2380
  killEvent(e);
@@ -2190,56 +2409,59 @@ the specific language governing permissions and limitations under the Apache Lic
2190
2409
 
2191
2410
  }));
2192
2411
 
2193
- this.search.bind("keyup", this.bind(this.resizeSearch));
2412
+ this.search.on("keyup", this.bind(function (e) {
2413
+ this.keydowns = 0;
2414
+ this.resizeSearch();
2415
+ })
2416
+ );
2194
2417
 
2195
- this.search.bind("blur", this.bind(function(e) {
2418
+ this.search.on("blur", this.bind(function(e) {
2196
2419
  this.container.removeClass("select2-container-active");
2197
2420
  this.search.removeClass("select2-focused");
2421
+ this.selectChoice(null);
2198
2422
  if (!this.opened()) this.clearSearch();
2199
2423
  e.stopImmediatePropagation();
2424
+ this.opts.element.trigger($.Event("select2-blur"));
2200
2425
  }));
2201
2426
 
2202
- this.container.delegate(selector, "mousedown", this.bind(function (e) {
2203
- if (!this.enabled) return;
2427
+ this.container.on("mousedown", selector, this.bind(function (e) {
2428
+ if (!this.isInterfaceEnabled()) return;
2204
2429
  if ($(e.target).closest(".select2-search-choice").length > 0) {
2205
2430
  // clicked inside a select2 search choice, do not open
2206
2431
  return;
2207
2432
  }
2433
+ this.selectChoice(null);
2208
2434
  this.clearPlaceholder();
2435
+ if (!this.container.hasClass("select2-container-active")) {
2436
+ this.opts.element.trigger($.Event("select2-focus"));
2437
+ }
2209
2438
  this.open();
2210
2439
  this.focusSearch();
2211
2440
  e.preventDefault();
2212
2441
  }));
2213
2442
 
2214
- this.container.delegate(selector, "focus", this.bind(function () {
2215
- if (!this.enabled) return;
2443
+ this.container.on("focus", selector, this.bind(function () {
2444
+ if (!this.isInterfaceEnabled()) return;
2445
+ if (!this.container.hasClass("select2-container-active")) {
2446
+ this.opts.element.trigger($.Event("select2-focus"));
2447
+ }
2216
2448
  this.container.addClass("select2-container-active");
2217
2449
  this.dropdown.addClass("select2-drop-active");
2218
2450
  this.clearPlaceholder();
2219
2451
  }));
2220
2452
 
2221
2453
  this.initContainerWidth();
2454
+ this.opts.element.addClass("select2-offscreen");
2222
2455
 
2223
2456
  // set the placeholder if necessary
2224
2457
  this.clearSearch();
2225
2458
  },
2226
2459
 
2227
2460
  // multi
2228
- enable: function() {
2229
- if (this.enabled) return;
2230
-
2231
- this.parent.enable.apply(this, arguments);
2232
-
2233
- this.search.removeAttr("disabled");
2234
- },
2235
-
2236
- // multi
2237
- disable: function() {
2238
- if (!this.enabled) return;
2239
-
2240
- this.parent.disable.apply(this, arguments);
2241
-
2242
- this.search.attr("disabled", true);
2461
+ enableInterface: function() {
2462
+ if (this.parent.enableInterface.apply(this, arguments)) {
2463
+ this.search.prop("disabled", !this.isInterfaceEnabled());
2464
+ }
2243
2465
  },
2244
2466
 
2245
2467
  // multi
@@ -2266,13 +2488,14 @@ the specific language governing permissions and limitations under the Apache Lic
2266
2488
 
2267
2489
  // multi
2268
2490
  clearSearch: function () {
2269
- var placeholder = this.getPlaceholder();
2491
+ var placeholder = this.getPlaceholder(),
2492
+ maxWidth = this.getMaxSearchWidth();
2270
2493
 
2271
2494
  if (placeholder !== undefined && this.getVal().length === 0 && this.search.hasClass("select2-focused") === false) {
2272
2495
  this.search.val(placeholder).addClass("select2-default");
2273
2496
  // stretch the search box to full width of the container so as much of the placeholder is visible as possible
2274
2497
  // we could call this.resizeSearch(), but we do not because that requires a sizer and we do not want to create one so early because of a firefox bug, see #944
2275
- this.search.width(this.getMaxSearchWidth());
2498
+ this.search.width(maxWidth > 0 ? maxWidth : this.container.css("width"));
2276
2499
  } else {
2277
2500
  this.search.val("").width(10);
2278
2501
  }
@@ -2294,7 +2517,9 @@ the specific language governing permissions and limitations under the Apache Lic
2294
2517
 
2295
2518
  this.focusSearch();
2296
2519
 
2297
- this.opts.element.trigger($.Event("open"));
2520
+ this.updateResults(true);
2521
+ this.search.focus();
2522
+ this.opts.element.trigger($.Event("select2-open"));
2298
2523
  },
2299
2524
 
2300
2525
  // multi
@@ -2350,6 +2575,9 @@ the specific language governing permissions and limitations under the Apache Lic
2350
2575
 
2351
2576
  // multi
2352
2577
  onSelect: function (data, options) {
2578
+
2579
+ if (!this.triggerSelect(data)) { return; }
2580
+
2353
2581
  this.addSelectedChoice(data);
2354
2582
 
2355
2583
  this.opts.element.trigger({ type: "selected", val: this.id(data), choice: data });
@@ -2408,14 +2636,14 @@ the specific language governing permissions and limitations under the Apache Lic
2408
2636
 
2409
2637
  formatted=this.opts.formatSelection(data, choice.find("div"));
2410
2638
  if (formatted != undefined) {
2411
- choice.find("div").replaceWith("<div>"+this.opts.escapeMarkup(formatted)+"</div>");
2639
+ choice.find("div").replaceWith("<div title='"+this.opts.escapeMarkup(formatted)+"'>"+this.opts.escapeMarkup(formatted)+"</div>");
2412
2640
  }
2413
2641
 
2414
2642
  if(enableChoice){
2415
2643
  choice.find(".select2-search-choice-close")
2416
- .bind("mousedown", killEvent)
2417
- .bind("click dblclick", this.bind(function (e) {
2418
- if (!this.enabled) return;
2644
+ .on("mousedown", killEvent)
2645
+ .on("click dblclick", this.bind(function (e) {
2646
+ if (!this.isInterfaceEnabled()) return;
2419
2647
 
2420
2648
  $(e.target).closest(".select2-search-choice").fadeOut('fast', this.bind(function(){
2421
2649
  this.unselect($(e.target));
@@ -2424,8 +2652,8 @@ the specific language governing permissions and limitations under the Apache Lic
2424
2652
  this.focusSearch();
2425
2653
  })).dequeue();
2426
2654
  killEvent(e);
2427
- })).bind("focus", this.bind(function () {
2428
- if (!this.enabled) return;
2655
+ })).on("focus", this.bind(function () {
2656
+ if (!this.isInterfaceEnabled()) return;
2429
2657
  this.container.addClass("select2-container-active");
2430
2658
  this.dropdown.addClass("select2-drop-active");
2431
2659
  }));
@@ -2472,7 +2700,7 @@ the specific language governing permissions and limitations under the Apache Lic
2472
2700
  },
2473
2701
 
2474
2702
  // multi
2475
- postprocessResults: function () {
2703
+ postprocessResults: function (data, initial, noHighlightUpdate) {
2476
2704
  var val = this.getVal(),
2477
2705
  choices = this.results.find(".select2-result"),
2478
2706
  compound = this.results.find(".select2-result-with-children"),
@@ -2495,10 +2723,15 @@ the specific language governing permissions and limitations under the Apache Lic
2495
2723
  }
2496
2724
  });
2497
2725
 
2498
- if (this.highlight() == -1){
2726
+ if (this.highlight() == -1 && noHighlightUpdate !== false){
2499
2727
  self.highlight(0);
2500
2728
  }
2501
2729
 
2730
+ //If all results are chosen render formatNoMAtches
2731
+ if(!this.opts.createSearchChoice && !choices.filter('.select2-result:not(.select2-selected)').length > 0){
2732
+ this.results.append("<li class='select2-no-results'>" + self.opts.formatNoMatches(self.search.val()) + "</li>");
2733
+ }
2734
+
2502
2735
  },
2503
2736
 
2504
2737
  // multi
@@ -2509,7 +2742,7 @@ the specific language governing permissions and limitations under the Apache Lic
2509
2742
  // multi
2510
2743
  resizeSearch: function () {
2511
2744
  var minimumWidth, left, maxWidth, containerLeft, searchWidth,
2512
- sideBorderPadding = getSideBorderPadding(this.search);
2745
+ sideBorderPadding = getSideBorderPadding(this.search);
2513
2746
 
2514
2747
  minimumWidth = measureTextWidth(this.search) + 10;
2515
2748
 
@@ -2563,18 +2796,36 @@ the specific language governing permissions and limitations under the Apache Lic
2563
2796
  },
2564
2797
 
2565
2798
  // multi
2566
- val: function () {
2567
- var val, triggerChange = false, data = [], self=this;
2799
+ buildChangeDetails: function (old, current) {
2800
+ var current = current.slice(0),
2801
+ old = old.slice(0);
2802
+
2803
+ // remove intersection from each array
2804
+ for (var i = 0; i < current.length; i++) {
2805
+ for (var j = 0; j < old.length; j++) {
2806
+ if (equal(this.opts.id(current[i]), this.opts.id(old[j]))) {
2807
+ current.splice(i, 1);
2808
+ i--;
2809
+ old.splice(j, 1);
2810
+ j--;
2811
+ }
2812
+ }
2813
+ }
2814
+
2815
+ return {added: current, removed: old};
2816
+ },
2817
+
2818
+
2819
+ // multi
2820
+ val: function (val, triggerChange) {
2821
+ var oldData, self=this, changeDetails;
2568
2822
 
2569
2823
  if (arguments.length === 0) {
2570
2824
  return this.getVal();
2571
2825
  }
2572
2826
 
2573
- val = arguments[0];
2574
-
2575
- if (arguments.length > 1) {
2576
- triggerChange = arguments[1];
2577
- }
2827
+ oldData=this.data();
2828
+ if (!oldData.length) oldData=[];
2578
2829
 
2579
2830
  // val is an id. !val is true for [undefined,null,'',0] - 0 is legal
2580
2831
  if (!val && val !== 0) {
@@ -2582,7 +2833,7 @@ the specific language governing permissions and limitations under the Apache Lic
2582
2833
  this.updateSelection([]);
2583
2834
  this.clearSearch();
2584
2835
  if (triggerChange) {
2585
- this.triggerChange();
2836
+ this.triggerChange({added: this.data(), removed: oldData});
2586
2837
  }
2587
2838
  return;
2588
2839
  }
@@ -2593,7 +2844,7 @@ the specific language governing permissions and limitations under the Apache Lic
2593
2844
  if (this.select) {
2594
2845
  this.opts.initSelection(this.select, this.bind(this.updateSelection));
2595
2846
  if (triggerChange) {
2596
- this.triggerChange();
2847
+ this.triggerChange(this.buildChangeDetails(oldData, this.data()));
2597
2848
  }
2598
2849
  } else {
2599
2850
  if (this.opts.initSelection === undefined) {
@@ -2606,7 +2857,7 @@ the specific language governing permissions and limitations under the Apache Lic
2606
2857
  self.updateSelection(data);
2607
2858
  self.clearSearch();
2608
2859
  if (triggerChange) {
2609
- self.triggerChange();
2860
+ self.triggerChange(this.buildChangeDetails(oldData, this.data()));
2610
2861
  }
2611
2862
  });
2612
2863
  }
@@ -2647,19 +2898,23 @@ the specific language governing permissions and limitations under the Apache Lic
2647
2898
  },
2648
2899
 
2649
2900
  // multi
2650
- data: function(values) {
2651
- var self=this, ids;
2901
+ data: function(values, triggerChange) {
2902
+ var self=this, ids, old;
2652
2903
  if (arguments.length === 0) {
2653
2904
  return this.selection
2654
2905
  .find(".select2-search-choice")
2655
2906
  .map(function() { return $(this).data("select2-data"); })
2656
2907
  .get();
2657
2908
  } else {
2909
+ old = this.data();
2658
2910
  if (!values) { values = []; }
2659
2911
  ids = $.map(values, function(e) { return self.opts.id(e); });
2660
2912
  this.setVal(ids);
2661
2913
  this.updateSelection(values);
2662
2914
  this.clearSearch();
2915
+ if (triggerChange) {
2916
+ this.triggerChange(this.buildChangeDetails(old, this.data()));
2917
+ }
2663
2918
  }
2664
2919
  }
2665
2920
  });
@@ -2669,7 +2924,9 @@ the specific language governing permissions and limitations under the Apache Lic
2669
2924
  var args = Array.prototype.slice.call(arguments, 0),
2670
2925
  opts,
2671
2926
  select2,
2672
- value, multiple, allowedMethods = ["val", "destroy", "opened", "open", "close", "focus", "isFocused", "container", "onSortStart", "onSortEnd", "enable", "disable", "positionDropdown", "data"];
2927
+ value, multiple,
2928
+ allowedMethods = ["val", "destroy", "opened", "open", "close", "focus", "isFocused", "container", "onSortStart", "onSortEnd", "enable", "readonly", "positionDropdown", "data"],
2929
+ valueMethods = ["val", "opened", "isFocused", "container", "data"];
2673
2930
 
2674
2931
  this.each(function () {
2675
2932
  if (args.length === 0 || typeof(args[0]) === "object") {
@@ -2677,7 +2934,7 @@ the specific language governing permissions and limitations under the Apache Lic
2677
2934
  opts.element = $(this);
2678
2935
 
2679
2936
  if (opts.element.get(0).tagName.toLowerCase() === "select") {
2680
- multiple = opts.element.attr("multiple");
2937
+ multiple = opts.element.prop("multiple");
2681
2938
  } else {
2682
2939
  multiple = opts.multiple || false;
2683
2940
  if ("tags" in opts) {opts.multiple = multiple = true;}
@@ -2699,7 +2956,9 @@ the specific language governing permissions and limitations under the Apache Lic
2699
2956
  } else {
2700
2957
  value = select2[args[0]].apply(select2, args.slice(1));
2701
2958
  }
2702
- if (value !== undefined) {return false;}
2959
+ if (indexOf(args[0], valueMethods) >= 0) {
2960
+ return false;
2961
+ }
2703
2962
  } else {
2704
2963
  throw "Invalid arguments to select2 plugin: " + args;
2705
2964
  }
@@ -2753,12 +3012,12 @@ the specific language governing permissions and limitations under the Apache Lic
2753
3012
  '<': '&lt;',
2754
3013
  '>': '&gt;',
2755
3014
  '"': '&quot;',
2756
- "'": '&apos;',
3015
+ "'": '&#39;',
2757
3016
  "/": '&#47;'
2758
3017
  };
2759
3018
 
2760
3019
  return String(markup).replace(/[&<>"'\/\\]/g, function (match) {
2761
- return replace_map[match[0]];
3020
+ return replace_map[match];
2762
3021
  });
2763
3022
  },
2764
3023
  blurOnChange: false,
@@ -2767,6 +3026,15 @@ the specific language governing permissions and limitations under the Apache Lic
2767
3026
  adaptDropdownCssClass: function(c) { return null; }
2768
3027
  };
2769
3028
 
3029
+ $.fn.select2.ajaxDefaults = {
3030
+ transport: $.ajax,
3031
+ params: {
3032
+ type: "GET",
3033
+ cache: false,
3034
+ dataType: "json"
3035
+ }
3036
+ };
3037
+
2770
3038
  // exports
2771
3039
  window.Select2 = {
2772
3040
  query: {