select2-rails 3.0.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -26,7 +26,7 @@ Add to your `app/assets/stylesheets/application.css`:
26
26
  *= require select2
27
27
 
28
28
  ## Version
29
- From `v2.1.0` on, `select2-rails`'s version will match the version of `Select2` it uses. Currently, `select2-rails` uses `Select2 v2.1`.
29
+ From `v2.1.0` on, `select2-rails`'s version will match the version of `Select2` it uses. Currently, `select2-rails` uses `Select2 v3.1`.
30
30
 
31
31
  The last number of the version is the patch version specific to the gem. For example, for a version of the form `2.x.y`, `2.x` is the release of `Select2` we should be compatible with, and y is the patch version specific to the gem (ie. to resolve any gem-specific issues that crop up).
32
32
 
@@ -1,5 +1,5 @@
1
1
  module Select2
2
2
  module Rails
3
- VERSION = "3.0.0"
3
+ VERSION = "3.1.0"
4
4
  end
5
5
  end
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  Copyright 2012 Igor Vaynberg
3
3
 
4
- Version: 3.0 Timestamp: Tue Jul 31 21:09:16 PDT 2012
4
+ Version: 3.1 Timestamp: Tue Aug 14 09:05:17 PDT 2012
5
5
 
6
6
  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in
7
7
  compliance with the License. You may obtain a copy of the License in the LICENSE file, or at:
@@ -13,23 +13,23 @@
13
13
  See the License for the specific language governing permissions and limitations under the License.
14
14
  */
15
15
  (function ($) {
16
- if(typeof $.fn.each2 == "undefined"){
17
- $.fn.extend({
18
- /*
19
- * 4-10 times faster .each replacement
20
- * use it carefully, as it overrides jQuery context of element on each iteration
21
- */
22
- each2 : function (c) {
23
- var j = $([0]), i = -1, l = this.length;
24
- while (
25
- ++i < l
26
- && (j.context = j[0] = this[i])
27
- && c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object
28
- );
29
- return this;
30
- }
31
- });
32
- }
16
+ if(typeof $.fn.each2 == "undefined"){
17
+ $.fn.extend({
18
+ /*
19
+ * 4-10 times faster .each replacement
20
+ * use it carefully, as it overrides jQuery context of element on each iteration
21
+ */
22
+ each2 : function (c) {
23
+ var j = $([0]), i = -1, l = this.length;
24
+ while (
25
+ ++i < l
26
+ && (j.context = j[0] = this[i])
27
+ && c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object
28
+ );
29
+ return this;
30
+ }
31
+ });
32
+ }
33
33
  })(jQuery);
34
34
 
35
35
  (function ($, undefined) {
@@ -71,8 +71,8 @@
71
71
  }
72
72
  return false;
73
73
  },
74
- isControl: function (k) {
75
- k = k.which ? k.which : k;
74
+ isControl: function (e) {
75
+ var k = e.which;
76
76
  switch (k) {
77
77
  case KEY.SHIFT:
78
78
  case KEY.CTRL:
@@ -80,7 +80,7 @@
80
80
  return true;
81
81
  }
82
82
 
83
- if (k.metaKey) return true;
83
+ if (e.metaKey) return true;
84
84
 
85
85
  return false;
86
86
  },
@@ -94,7 +94,7 @@
94
94
 
95
95
  function escapeMarkup(markup) {
96
96
  if (markup && typeof(markup) === "string") {
97
- return markup.replace("&", "&amp;");
97
+ return markup.replace(/&/g, "&amp;");
98
98
  } else {
99
99
  return markup;
100
100
  }
@@ -181,7 +181,7 @@
181
181
  * the elements under the pointer are scrolled.
182
182
  */
183
183
  function installFilteredMouseMove(element) {
184
- element.bind("mousemove", function (e) {
184
+ element.bind("mousemove", function (e) {
185
185
  var lastpos = $.data(document, "select2-lastpos");
186
186
  if (lastpos === undefined || lastpos.x !== e.pageX || lastpos.y !== e.pageY) {
187
187
  $(e.target).trigger("mousemove-filtered", e);
@@ -233,21 +233,21 @@
233
233
 
234
234
  function measureTextWidth(e) {
235
235
  if (!sizer){
236
- var style = e[0].currentStyle || window.getComputedStyle(e[0], null);
237
- sizer = $("<div></div>").css({
238
- position: "absolute",
239
- left: "-10000px",
240
- top: "-10000px",
241
- display: "none",
242
- fontSize: style.fontSize,
243
- fontFamily: style.fontFamily,
244
- fontStyle: style.fontStyle,
245
- fontWeight: style.fontWeight,
246
- letterSpacing: style.letterSpacing,
247
- textTransform: style.textTransform,
248
- whiteSpace: "nowrap"
249
- });
250
- $("body").append(sizer);
236
+ var style = e[0].currentStyle || window.getComputedStyle(e[0], null);
237
+ sizer = $("<div></div>").css({
238
+ position: "absolute",
239
+ left: "-10000px",
240
+ top: "-10000px",
241
+ display: "none",
242
+ fontSize: style.fontSize,
243
+ fontFamily: style.fontFamily,
244
+ fontStyle: style.fontStyle,
245
+ fontWeight: style.fontWeight,
246
+ letterSpacing: style.letterSpacing,
247
+ textTransform: style.textTransform,
248
+ whiteSpace: "nowrap"
249
+ });
250
+ $("body").append(sizer);
251
251
  }
252
252
  sizer.text(e.val());
253
253
  return sizer.width();
@@ -277,6 +277,7 @@
277
277
  * @param options.url url for the data
278
278
  * @param options.data a function(searchTerm, pageNumber, context) that should return an object containing query string parameters for the above url.
279
279
  * @param options.dataType request data type: ajax, jsonp, other datatatypes supported by jQuery's $.ajax function or the transport function if specified
280
+ * @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
280
281
  * @param options.quietMillis (optional) milliseconds to wait before making the ajaxRequest, helps debounce the ajax function if invoked too often
281
282
  * @param options.results a function(remoteData, pageNumber) that converts data returned form the remote request to the format expected by Select2.
282
283
  * The expected format is an object containing the following keys:
@@ -297,6 +298,7 @@
297
298
  var requestNumber = requestSequence, // this request's sequence number
298
299
  data = options.data, // ajax data function
299
300
  transport = options.transport || $.ajax,
301
+ traditional = options.traditional || false,
300
302
  type = options.type || 'GET'; // set type of request (GET or POST)
301
303
 
302
304
  data = data.call(this, query.term, query.page, query.context);
@@ -308,6 +310,7 @@
308
310
  dataType: options.dataType,
309
311
  data: data,
310
312
  type: type,
313
+ traditional: traditional,
311
314
  success: function (data) {
312
315
  if (requestNumber < requestSequence) {
313
316
  return;
@@ -396,10 +399,79 @@
396
399
  */
397
400
  function checkFormatter(formatter, formatterName) {
398
401
  if ($.isFunction(formatter)) return true;
399
- if (!formatter) return fasle;
402
+ if (!formatter) return false;
400
403
  throw new Error("formatterName must be a function or a falsy value");
401
404
  }
402
405
 
406
+ function evaluate(val) {
407
+ return $.isFunction(val) ? val() : val;
408
+ }
409
+
410
+ function countResults(results) {
411
+ var count = 0;
412
+ $.each(results, function(i, item) {
413
+ if (item.children) {
414
+ count += countResults(item.children);
415
+ } else {
416
+ count++;
417
+ }
418
+ });
419
+ return count;
420
+ }
421
+
422
+ /**
423
+ * Default tokenizer. This function uses breaks the input on substring match of any string from the
424
+ * opts.tokenSeparators array and uses opts.createSearchChoice to create the choice object. Both of those
425
+ * two options have to be defined in order for the tokenizer to work.
426
+ *
427
+ * @param input text user has typed so far or pasted into the search field
428
+ * @param selection currently selected choices
429
+ * @param selectCallback function(choice) callback tho add the choice to selection
430
+ * @param opts select2's opts
431
+ * @return undefined/null to leave the current input unchanged, or a string to change the input to the returned value
432
+ */
433
+ function defaultTokenizer(input, selection, selectCallback, opts) {
434
+ var original = input, // store the original so we can compare and know if we need to tell the search to update its text
435
+ dupe = false, // check for whether a token we extracted represents a duplicate selected choice
436
+ token, // token
437
+ index, // position at which the separator was found
438
+ i, l, // looping variables
439
+ separator; // the matched separator
440
+
441
+ if (!opts.createSearchChoice || !opts.tokenSeparators || opts.tokenSeparators.length < 1) return undefined;
442
+
443
+ while (true) {
444
+ index = -1;
445
+
446
+ for (i = 0, l = opts.tokenSeparators.length; i < l; i++) {
447
+ separator = opts.tokenSeparators[i];
448
+ index = input.indexOf(separator);
449
+ if (index >= 0) break;
450
+ }
451
+
452
+ if (index < 0) break; // did not find any token separator in the input string, bail
453
+
454
+ token = input.substring(0, index);
455
+ input = input.substring(index + separator.length);
456
+
457
+ if (token.length > 0) {
458
+ token = opts.createSearchChoice(token, selection);
459
+ if (token !== undefined && token !== null && opts.id(token) !== undefined && opts.id(token) !== null) {
460
+ dupe = false;
461
+ for (i = 0, l = selection.length; i < l; i++) {
462
+ if (equal(opts.id(token), opts.id(selection[i]))) {
463
+ dupe = true; break;
464
+ }
465
+ }
466
+
467
+ if (!dupe) selectCallback(token);
468
+ }
469
+ }
470
+ }
471
+
472
+ if (original.localeCompare(input) != 0) return input;
473
+ }
474
+
403
475
  /**
404
476
  * blurs any Select2 container that has focus when an element outside them was clicked or received focus
405
477
  *
@@ -429,10 +501,6 @@
429
501
  });
430
502
  });
431
503
 
432
- function evaluate(val) {
433
- return $.isFunction(val) ? val() : val;
434
- }
435
-
436
504
  /**
437
505
  * Creates a new class
438
506
  *
@@ -476,6 +544,9 @@
476
544
  this.enabled=true;
477
545
  this.container = this.createContainer();
478
546
 
547
+ this.containerId="s2id"+nextUid();
548
+ this.container.attr("id", this.containerId);
549
+
479
550
  // cache the body so future lookups are cheap
480
551
  this.body = thunk(function() { return opts.element.closest("body"); });
481
552
 
@@ -490,11 +561,10 @@
490
561
  this.opts.element
491
562
  .data("select2", this)
492
563
  .hide()
493
- .after(this.container);
564
+ .before(this.container);
494
565
  this.container.data("select2", this);
495
566
 
496
567
  this.dropdown = this.container.find(".select2-drop");
497
- this.dropdown.css(evaluate(opts.dropdownCss));
498
568
  this.dropdown.addClass(evaluate(opts.dropdownCssClass));
499
569
  this.dropdown.data("select2", this);
500
570
 
@@ -559,7 +629,7 @@
559
629
  this.monitorSource();
560
630
  }
561
631
 
562
- if (opts.element.is(":disabled")) this.disable();
632
+ if (opts.element.is(":disabled") || opts.element.is("[readonly='readonly']")) this.disable();
563
633
  },
564
634
 
565
635
  // abstract
@@ -585,9 +655,6 @@
585
655
  this.select = select = opts.element;
586
656
  }
587
657
 
588
- //Custom tags separator.
589
- opts.separator = opts.separator || ",";
590
-
591
658
  if (select) {
592
659
  // these options are not allowed when attached to a select because they are picked up off the element itself
593
660
  $.each(["id", "multiple", "ajax", "query", "createSearchChoice", "initSelection", "data", "tags"], function () {
@@ -599,7 +666,7 @@
599
666
 
600
667
  opts = $.extend({}, {
601
668
  populateResults: function(container, results, query) {
602
- var populate, data, result, children, id=this.opts.id;
669
+ var populate, data, result, children, id=this.opts.id, self=this;
603
670
 
604
671
  populate=function(results, container, depth) {
605
672
 
@@ -615,6 +682,7 @@
615
682
  node.addClass("select2-result");
616
683
  node.addClass(selectable ? "select2-result-selectable" : "select2-result-unselectable");
617
684
  if (compound) { node.addClass("select2-result-with-children"); }
685
+ node.addClass(self.opts.formatResultCssClass(result));
618
686
 
619
687
  label=$("<div></div>");
620
688
  label.addClass("select2-result-label");
@@ -658,10 +726,10 @@
658
726
  var group;
659
727
  if (element.is("option")) {
660
728
  if (query.matcher(term, element.text(), element)) {
661
- collection.push({id:element.attr("value"), text:element.text(), element: element.get()});
729
+ collection.push({id:element.attr("value"), text:element.text(), element: element.get(), css: element.attr("class")});
662
730
  }
663
731
  } else if (element.is("optgroup")) {
664
- group={text:element.attr("label"), children:[], element: element.get()};
732
+ group={text:element.attr("label"), children:[], element: element.get(), css: element.attr("class")};
665
733
  element.children().each2(function(i, elm) { process(elm, group.children); });
666
734
  if (group.children.length>0) {
667
735
  collection.push(group);
@@ -685,6 +753,7 @@
685
753
  });
686
754
  // this is needed because inside val() we construct choices from options and there id is hardcoded
687
755
  opts.id=function(e) { return e.id; };
756
+ opts.formatResultCssClass = function(data) { return data.css; }
688
757
  } else {
689
758
  if (!("query" in opts)) {
690
759
  if ("ajax" in opts) {
@@ -858,10 +927,32 @@
858
927
  */
859
928
  // abstract
860
929
  opening: function() {
930
+ var cid = this.containerId, selector = "#"+ cid,
931
+ scroll = "scroll." + cid, resize = "resize." + cid;
932
+
933
+ this.container.parents().each(function() {
934
+ $(this).bind(scroll, function() {
935
+ var s2 = $(selector);
936
+ if (s2.length == 0) {
937
+ $(this).unbind(scroll);
938
+ }
939
+ s2.select2("close");
940
+ });
941
+ });
942
+
943
+ $(window).bind(resize, function() {
944
+ var s2 = $(selector);
945
+ if (s2.length == 0) {
946
+ $(window).unbind(resize);
947
+ }
948
+ s2.select2("close");
949
+ });
950
+
861
951
  this.clearDropdownAlignmentPreference();
862
952
 
863
953
  if (this.search.val() === " ") { this.search.val(""); }
864
954
 
955
+ this.dropdown.css(evaluate(this.opts.dropdownCss));
865
956
  this.dropdown.addClass("select2-drop-active");
866
957
  this.container.addClass("select2-dropdown-open").addClass("select2-container-active");
867
958
 
@@ -883,6 +974,13 @@
883
974
  close: function () {
884
975
  if (!this.opened()) return;
885
976
 
977
+ var self = this;
978
+
979
+ this.container.parents().each(function() {
980
+ $(this).unbind("scroll." + self.containerId);
981
+ });
982
+ $(window).unbind("resize." + this.containerId);
983
+
886
984
  this.clearDropdownAlignmentPreference();
887
985
 
888
986
  this.dropdown.hide();
@@ -984,7 +1082,7 @@
984
1082
  highlightUnderEvent: function (event) {
985
1083
  var el = $(event.target).closest(".select2-result-selectable");
986
1084
  if (el.length > 0 && !el.is(".select2-highlighted")) {
987
- var choices = this.results.find('.select2-result-selectable');
1085
+ var choices = this.results.find('.select2-result-selectable');
988
1086
  this.highlight(choices.index(el));
989
1087
  } else if (el.length == 0) {
990
1088
  // if we are over an unselectable item remove al highlights
@@ -1018,7 +1116,7 @@
1018
1116
  self.opts.populateResults.call(this, results, data.results, {term: term, page: page, context:context});
1019
1117
 
1020
1118
  if (data.more===true) {
1021
- more.detach().appendTo(results.children(":last")).text(self.opts.formatLoadMore(page+1));
1119
+ more.detach().appendTo(results).text(self.opts.formatLoadMore(page+1));
1022
1120
  window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10);
1023
1121
  } else {
1024
1122
  more.remove();
@@ -1029,12 +1127,19 @@
1029
1127
  }
1030
1128
  },
1031
1129
 
1130
+ /**
1131
+ * Default tokenizer function which does nothing
1132
+ */
1133
+ tokenize: function() {
1134
+
1135
+ },
1136
+
1032
1137
  /**
1033
1138
  * @param initial whether or not this is the call to this method right after the dropdown has been opened
1034
1139
  */
1035
1140
  // abstract
1036
1141
  updateResults: function (initial) {
1037
- var search = this.search, results = this.results, opts = this.opts, data, self=this;
1142
+ var search = this.search, results = this.results, opts = this.opts, data, self=this, input;
1038
1143
 
1039
1144
  // if the search is currently hidden we do not alter the results
1040
1145
  if (initial !== true && (this.showSearchInput === false || !this.opened())) {
@@ -1057,8 +1162,8 @@
1057
1162
  if (opts.maximumSelectionSize >=1) {
1058
1163
  data = this.data();
1059
1164
  if ($.isArray(data) && data.length >= opts.maximumSelectionSize && checkFormatter(opts.formatSelectionTooBig, "formatSelectionTooBig")) {
1060
- render("<li class='select2-selection-limit'>" + opts.formatSelectionTooBig(opts.maximumSelectionSize) + "</li>");
1061
- return;
1165
+ render("<li class='select2-selection-limit'>" + opts.formatSelectionTooBig(opts.maximumSelectionSize) + "</li>");
1166
+ return;
1062
1167
  }
1063
1168
  }
1064
1169
 
@@ -1066,6 +1171,15 @@
1066
1171
  render("<li class='select2-no-results'>" + opts.formatInputTooShort(search.val(), opts.minimumInputLength) + "</li>");
1067
1172
  return;
1068
1173
  }
1174
+ else {
1175
+ render("<li class='select2-searching'>" + opts.formatSearching() + "</li>");
1176
+ }
1177
+
1178
+ // give the tokenizer a chance to pre-process the input
1179
+ input = this.tokenize();
1180
+ if (input != undefined && input != null) {
1181
+ search.val(input);
1182
+ }
1069
1183
 
1070
1184
  this.resultsPage = 1;
1071
1185
  opts.query({
@@ -1101,7 +1215,7 @@
1101
1215
  self.opts.populateResults.call(this, results, data.results, {term: search.val(), page: this.resultsPage, context:null});
1102
1216
 
1103
1217
  if (data.more === true && checkFormatter(opts.formatLoadMore, "formatLoadMore")) {
1104
- results.children().filter(":last").append("<li class='select2-more-results'>" + escapeMarkup(opts.formatLoadMore(this.resultsPage)) + "</li>");
1218
+ results.append("<li class='select2-more-results'>" + escapeMarkup(opts.formatLoadMore(this.resultsPage)) + "</li>");
1105
1219
  window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10);
1106
1220
  }
1107
1221
 
@@ -1215,7 +1329,7 @@
1215
1329
 
1216
1330
  // single
1217
1331
 
1218
- createContainer: function () {
1332
+ createContainer: function () {
1219
1333
  var container = $("<div></div>", {
1220
1334
  "class": "select2-container"
1221
1335
  }).html([
@@ -1306,6 +1420,10 @@
1306
1420
  return;
1307
1421
  }
1308
1422
 
1423
+ if (this.opts.openOnEnter === false && e.which === KEY.ENTER) {
1424
+ return;
1425
+ }
1426
+
1309
1427
  this.open();
1310
1428
 
1311
1429
  if (e.which === KEY.ENTER) {
@@ -1332,7 +1450,6 @@
1332
1450
  } else if (this.enabled) {
1333
1451
  this.open();
1334
1452
  }
1335
- killEvent(e);
1336
1453
 
1337
1454
  clickingInside = false;
1338
1455
  }));
@@ -1346,7 +1463,9 @@
1346
1463
  }));
1347
1464
 
1348
1465
  selection.bind("blur", this.bind(function() {
1349
- this.container.removeClass("select2-container-active");
1466
+ if (!this.opened()) {
1467
+ this.container.removeClass("select2-container-active");
1468
+ }
1350
1469
  window.setTimeout(this.bind(function() { this.search.attr("tabIndex", this.opts.element.attr("tabIndex")); }), 10);
1351
1470
  }));
1352
1471
 
@@ -1359,7 +1478,19 @@
1359
1478
  return;
1360
1479
  }
1361
1480
 
1362
- if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) {
1481
+ if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e)
1482
+ || e.which === KEY.ESC) {
1483
+ return;
1484
+ }
1485
+
1486
+ if (this.opts.openOnEnter === false && e.which === KEY.ENTER) {
1487
+ return;
1488
+ }
1489
+
1490
+ if (e.which == KEY.DELETE) {
1491
+ if (this.opts.allowClear) {
1492
+ this.clear();
1493
+ }
1363
1494
  return;
1364
1495
  }
1365
1496
 
@@ -1384,6 +1515,8 @@
1384
1515
  keyWritten = keyWritten.toUpperCase();
1385
1516
  }
1386
1517
 
1518
+ // focus the field before calling val so the cursor ends up after the value instead of before
1519
+ this.search.focus();
1387
1520
  this.search.val(keyWritten);
1388
1521
 
1389
1522
  // prevent event propagation so it doesnt replay on the now focussed search field and result in double key entry
@@ -1489,9 +1622,7 @@
1489
1622
  // hide the search box if this is the first we got the results and there are a few of them
1490
1623
 
1491
1624
  if (initial === true) {
1492
- // TODO below we use data.results.length, but what we really need is something recursive to calc the length
1493
- // TODO in case there are optgroups
1494
- showSearchInput = this.showSearchInput = data.results.length >= this.opts.minimumResultsForSearch;
1625
+ showSearchInput = this.showSearchInput = countResults(data.results) >= this.opts.minimumResultsForSearch;
1495
1626
  this.dropdown.find(".select2-search")[showSearchInput ? "removeClass" : "addClass"]("select2-search-hidden");
1496
1627
 
1497
1628
  //add "select2-with-searchbox" to the container if search box is shown
@@ -1602,14 +1733,14 @@
1602
1733
  " <ul class='select2-choices'>",
1603
1734
  //"<li class='select2-search-choice'><span>California</span><a href="javascript:void(0)" class="select2-search-choice-close"></a></li>" ,
1604
1735
  " <li class='select2-search-field'>" ,
1605
- " <input type='text' autocomplete='off' style='width: 25px;' class='select2-input'>" ,
1736
+ " <input type='text' autocomplete='off' class='select2-input'>" ,
1606
1737
  " </li>" ,
1607
1738
  "</ul>" ,
1608
1739
  "<div class='select2-drop select2-drop-multi' style='display:none;'>" ,
1609
1740
  " <ul class='select2-results'>" ,
1610
1741
  " </ul>" ,
1611
1742
  "</div>"].join(""));
1612
- return container;
1743
+ return container;
1613
1744
  },
1614
1745
 
1615
1746
  // multi
@@ -1685,7 +1816,12 @@
1685
1816
  }
1686
1817
  }
1687
1818
 
1688
- if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.BACKSPACE || e.which === KEY.ESC) {
1819
+ if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e)
1820
+ || e.which === KEY.BACKSPACE || e.which === KEY.ESC) {
1821
+ return;
1822
+ }
1823
+
1824
+ if (this.opts.openOnEnter === false && e.which === KEY.ENTER) {
1689
1825
  return;
1690
1826
  }
1691
1827
 
@@ -1792,7 +1928,7 @@
1792
1928
  this.parent.opening.apply(this, arguments);
1793
1929
 
1794
1930
  this.clearPlaceholder();
1795
- this.resizeSearch();
1931
+ this.resizeSearch();
1796
1932
  this.focusSearch();
1797
1933
  },
1798
1934
 
@@ -1833,6 +1969,18 @@
1833
1969
  self.postprocessResults();
1834
1970
  },
1835
1971
 
1972
+ tokenize: function() {
1973
+ var input = this.search.val();
1974
+ input = this.opts.tokenizer(input, this.data(), this.bind(this.onSelect), this.opts);
1975
+ if (input != null && input != undefined) {
1976
+ this.search.val(input);
1977
+ if (input.length > 0) {
1978
+ this.open();
1979
+ }
1980
+ }
1981
+
1982
+ },
1983
+
1836
1984
  // multi
1837
1985
  onSelect: function (data) {
1838
1986
  this.addSelectedChoice(data);
@@ -1842,10 +1990,9 @@
1842
1990
  this.close();
1843
1991
  this.search.width(10);
1844
1992
  } else {
1845
- this.search.width(10);
1846
- this.resizeSearch();
1847
-
1848
1993
  if (this.countSelectableResults()>0) {
1994
+ this.search.width(10);
1995
+ this.resizeSearch();
1849
1996
  this.positionDropdown();
1850
1997
  } else {
1851
1998
  // if nothing left to select close
@@ -1880,6 +2027,7 @@
1880
2027
  formatted=this.opts.formatSelection(data, choice);
1881
2028
  choice.find("div").replaceWith("<div>"+escapeMarkup(formatted)+"</div>");
1882
2029
  choice.find(".select2-search-choice-close")
2030
+ .bind("mousedown", killEvent)
1883
2031
  .bind("click dblclick", this.bind(function (e) {
1884
2032
  if (!this.enabled) return;
1885
2033
 
@@ -1965,7 +2113,7 @@
1965
2113
  resizeSearch: function () {
1966
2114
 
1967
2115
  var minimumWidth, left, maxWidth, containerLeft, searchWidth,
1968
- sideBorderPadding = getSideBorderPadding(this.search);
2116
+ sideBorderPadding = getSideBorderPadding(this.search);
1969
2117
 
1970
2118
  minimumWidth = measureTextWidth(this.search) + 10;
1971
2119
 
@@ -1975,7 +2123,6 @@
1975
2123
  containerLeft = this.selection.offset().left;
1976
2124
 
1977
2125
  searchWidth = maxWidth - (left - containerLeft) - sideBorderPadding;
1978
-
1979
2126
  if (searchWidth < minimumWidth) {
1980
2127
  searchWidth = maxWidth - sideBorderPadding;
1981
2128
  }
@@ -2109,7 +2256,7 @@
2109
2256
  var args = Array.prototype.slice.call(arguments, 0),
2110
2257
  opts,
2111
2258
  select2,
2112
- value, multiple, allowedMethods = ["val", "destroy", "open", "close", "focus", "isFocused", "container", "onSortStart", "onSortEnd", "enable", "disable", "positionDropdown", "data"];
2259
+ value, multiple, allowedMethods = ["val", "destroy", "opened", "open", "close", "focus", "isFocused", "container", "onSortStart", "onSortEnd", "enable", "disable", "positionDropdown", "data"];
2113
2260
 
2114
2261
  this.each(function () {
2115
2262
  if (args.length === 0 || typeof(args[0]) === "object") {
@@ -2151,6 +2298,7 @@
2151
2298
  $.fn.select2.defaults = {
2152
2299
  width: "copy",
2153
2300
  closeOnSelect: true,
2301
+ openOnEnter: true,
2154
2302
  containerCss: {},
2155
2303
  dropdownCss: {},
2156
2304
  containerCssClass: "",
@@ -2163,17 +2311,22 @@
2163
2311
  formatSelection: function (data, container) {
2164
2312
  return data.text;
2165
2313
  },
2314
+ formatResultCssClass: function(data) {return undefined;},
2166
2315
  formatNoMatches: function () { return "No matches found"; },
2167
2316
  formatInputTooShort: function (input, min) { return "Please enter " + (min - input.length) + " more characters"; },
2168
2317
  formatSelectionTooBig: function (limit) { return "You can only select " + limit + " items"; },
2169
2318
  formatLoadMore: function (pageNumber) { return "Loading more results..."; },
2319
+ formatSearching: function () { return "Searching..."; },
2170
2320
  minimumResultsForSearch: 0,
2171
2321
  minimumInputLength: 0,
2172
2322
  maximumSelectionSize: 0,
2173
2323
  id: function (e) { return e.id; },
2174
2324
  matcher: function(term, text) {
2175
2325
  return text.toUpperCase().indexOf(term.toUpperCase()) >= 0;
2176
- }
2326
+ },
2327
+ separator: ",",
2328
+ tokenSeparators: [],
2329
+ tokenizer: defaultTokenizer
2177
2330
  };
2178
2331
 
2179
2332
  // exports
@@ -1,5 +1,5 @@
1
1
  /*
2
- *Version: 3.0 Timestamp: Tue Jul 31 21:09:16 PDT 2012
2
+ *Version: 3.1 Timestamp: Tue Aug 14 09:05:17 PDT 2012
3
3
 
4
4
  .select2-container
5
5
  position: relative
@@ -7,6 +7,7 @@
7
7
  /* inline-block for ie7
8
8
  zoom: 1
9
9
  *display: inline
10
+ vertical-align: top
10
11
  /*
11
12
  * Force border-box so that % widths fit the parent
12
13
  * container without overlap because of margin/padding.
@@ -328,7 +329,7 @@
328
329
  font-style: normal
329
330
  .select2-highlighted em
330
331
  background: transparent
331
- .select2-no-results, .select2-selection-limit
332
+ .select2-no-results, .select2-searching, .select2-selection-limit
332
333
  background: #f4f4f4
333
334
  display: list-item
334
335
  .select2-disabled
@@ -497,4 +498,4 @@
497
498
 
498
499
  .select2-offscreen
499
500
  position: absolute
500
- left: -10000px
501
+ left: -10000px
metadata CHANGED
@@ -1,84 +1,89 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: select2-rails
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 3
7
- - 0
8
- - 0
9
- version: 3.0.0
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.1.0
5
+ prerelease:
10
6
  platform: ruby
11
- authors:
7
+ authors:
12
8
  - Rogerio Medeiros
13
9
  autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
-
17
- date: 2012-08-06 00:00:00 -03:00
18
- default_executable:
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
21
- type: :runtime
22
- version_requirements: &id001 !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ~>
25
- - !ruby/object:Gem::Version
26
- segments:
27
- - 0
28
- - 14
29
- version: "0.14"
12
+ date: 2012-08-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
30
15
  name: thor
31
- requirement: *id001
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '0.14'
22
+ type: :runtime
32
23
  prerelease: false
33
- - !ruby/object:Gem::Dependency
34
- type: :development
35
- version_requirements: &id002 !ruby/object:Gem::Requirement
36
- requirements:
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
37
27
  - - ~>
38
- - !ruby/object:Gem::Version
39
- segments:
40
- - 1
41
- - 0
42
- version: "1.0"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.14'
30
+ - !ruby/object:Gem::Dependency
43
31
  name: bundler
44
- requirement: *id002
45
- prerelease: false
46
- - !ruby/object:Gem::Dependency
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '1.0'
47
38
  type: :development
48
- version_requirements: &id003 !ruby/object:Gem::Requirement
49
- requirements:
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
50
43
  - - ~>
51
- - !ruby/object:Gem::Version
52
- segments:
53
- - 3
54
- - 0
55
- version: "3.0"
44
+ - !ruby/object:Gem::Version
45
+ version: '1.0'
46
+ - !ruby/object:Gem::Dependency
56
47
  name: rails
57
- requirement: *id003
58
- prerelease: false
59
- - !ruby/object:Gem::Dependency
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '3.0'
60
54
  type: :development
61
- version_requirements: &id004 !ruby/object:Gem::Requirement
62
- requirements:
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
63
59
  - - ~>
64
- - !ruby/object:Gem::Version
65
- segments:
66
- - 3
67
- - 1
68
- version: "3.1"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ - !ruby/object:Gem::Dependency
69
63
  name: sass
70
- requirement: *id004
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '3.1'
70
+ type: :development
71
71
  prerelease: false
72
- description: Select2 is a jQuery based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results. This gem integrates Select2 with Rails asset pipeline for easy of use.
73
- email:
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '3.1'
78
+ description: Select2 is a jQuery based replacement for select boxes. It supports searching,
79
+ remote data sets, and infinite scrolling of results. This gem integrates Select2
80
+ with Rails asset pipeline for easy of use.
81
+ email:
74
82
  - argerim@gmail.com
75
83
  executables: []
76
-
77
84
  extensions: []
78
-
79
85
  extra_rdoc_files: []
80
-
81
- files:
86
+ files:
82
87
  - .gitignore
83
88
  - Gemfile
84
89
  - LICENSE
@@ -93,35 +98,34 @@ files:
93
98
  - vendor/assets/images/spinner.gif
94
99
  - vendor/assets/javascripts/select2.js
95
100
  - vendor/assets/stylesheets/select2.css.sass
96
- has_rdoc: true
97
101
  homepage: https://github.com/argerim/select2-rails
98
102
  licenses: []
99
-
100
103
  post_install_message:
101
104
  rdoc_options: []
102
-
103
- require_paths:
105
+ require_paths:
104
106
  - lib
105
- required_ruby_version: !ruby/object:Gem::Requirement
106
- requirements:
107
- - - ">="
108
- - !ruby/object:Gem::Version
109
- segments:
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ! '>='
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ segments:
110
114
  - 0
111
- version: "0"
112
- required_rubygems_version: !ruby/object:Gem::Requirement
113
- requirements:
114
- - - ">="
115
- - !ruby/object:Gem::Version
116
- segments:
115
+ hash: -2917332029930381823
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ! '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ segments:
117
123
  - 0
118
- version: "0"
124
+ hash: -2917332029930381823
119
125
  requirements: []
120
-
121
126
  rubyforge_project:
122
- rubygems_version: 1.3.6
127
+ rubygems_version: 1.8.24
123
128
  signing_key:
124
129
  specification_version: 3
125
130
  summary: Integrate Select2 javascript library with Rails asset pipeline
126
131
  test_files: []
127
-