select2-rails 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  module Select2
2
2
  module Rails
3
- VERSION = "0.0.5"
3
+ VERSION = "0.0.6"
4
4
  end
5
5
  end
@@ -12,6 +12,26 @@
12
12
  distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and limitations under the License.
14
14
  */
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
+ }
33
+ })(jQuery);
34
+
15
35
  (function ($, undefined) {
16
36
  "use strict";
17
37
  /*global document, window, jQuery, console */
@@ -20,7 +40,7 @@
20
40
  return;
21
41
  }
22
42
 
23
- var KEY, AbstractSelect2, SingleSelect2, MultiSelect2, nextUid;
43
+ var KEY, AbstractSelect2, SingleSelect2, MultiSelect2, nextUid, sizer;
24
44
 
25
45
  KEY = {
26
46
  TAB: 9,
@@ -134,17 +154,18 @@
134
154
  });
135
155
  }
136
156
 
157
+ $(document).delegate("*", "mousemove", function (e) {
158
+ $(document).data("select2-lastpos", {x: e.pageX, y: e.pageY});
159
+ });
160
+
137
161
  /**
138
162
  * filters mouse events so an event is fired only if the mouse moved.
139
163
  *
140
164
  * filters out mouse events that occur when mouse is stationary but
141
165
  * the elements under the pointer are scrolled.
142
- */
143
- $(document).delegate("*", "mousemove", function (e) {
144
- $(document).data("select2-lastpos", {x: e.pageX, y: e.pageY});
145
- });
166
+ */
146
167
  function installFilteredMouseMove(element) {
147
- element.bind("mousemove", function (e) {
168
+ element.bind("mousemove", function (e) {
148
169
  var lastpos = $(document).data("select2-lastpos");
149
170
  if (lastpos === undefined || lastpos.x !== e.pageX || lastpos.y !== e.pageY) {
150
171
  $(e.target).trigger("mousemove-filtered", e);
@@ -180,26 +201,42 @@
180
201
  event.stopPropagation();
181
202
  }
182
203
 
183
- function measureTextWidth(e) {
184
- var sizer, width;
185
- sizer = $("<div></div>").css({
186
- position: "absolute",
187
- left: "-1000px",
188
- top: "-1000px",
189
- display: "none",
190
- fontSize: e.css("fontSize"),
191
- fontFamily: e.css("fontFamily"),
192
- fontStyle: e.css("fontStyle"),
193
- fontWeight: e.css("fontWeight"),
194
- letterSpacing: e.css("letterSpacing"),
195
- textTransform: e.css("textTransform"),
196
- whiteSpace: "nowrap"
197
- });
198
- sizer.text(e.val());
199
- $("body").append(sizer);
200
- width = sizer.width();
201
- sizer.remove();
202
- return width;
204
+ function measureTextWidth(e) {
205
+ if (!sizer){
206
+ var style = e[0].currentStyle || window.getComputedStyle(e[0], null);
207
+ sizer = $("<div></div>").css({
208
+ position: "absolute",
209
+ left: "-1000px",
210
+ top: "-1000px",
211
+ display: "none",
212
+ fontSize: style.fontSize,
213
+ fontFamily: style.fontFamily,
214
+ fontStyle: style.fontStyle,
215
+ fontWeight: style.fontWeight,
216
+ letterSpacing: style.letterSpacing,
217
+ textTransform: style.textTransform,
218
+ whiteSpace: "nowrap"
219
+ });
220
+ $("body").append(sizer);
221
+ }
222
+ sizer.text(e.val());
223
+ return sizer.width();
224
+ }
225
+
226
+ function markMatch(text, term, markup) {
227
+ var match=text.toUpperCase().indexOf(term.toUpperCase()),
228
+ tl=term.length;
229
+
230
+ if (match<0) {
231
+ markup.push(text);
232
+ return;
233
+ }
234
+
235
+ markup.push(text.substring(0, match));
236
+ markup.push("<span class='select2-match'>");
237
+ markup.push(text.substring(match, match + tl));
238
+ markup.push("</span>");
239
+ markup.push(text.substring(match + tl, text.length));
203
240
  }
204
241
 
205
242
  /**
@@ -229,17 +266,18 @@
229
266
  requestSequence += 1; // increment the sequence
230
267
  var requestNumber = requestSequence, // this request's sequence number
231
268
  data = options.data, // ajax data function
232
- transport = options.transport || $.ajax;
233
-
269
+ transport = options.transport || $.ajax,
270
+ type = options.type || 'GET'; // set type of request (GET or POST)
271
+
234
272
  data = data.call(this, query.term, query.page, query.context);
235
273
 
236
- if( null !== handler){
237
- handler.abort();
238
- }
274
+ if( null !== handler) { handler.abort(); }
275
+
239
276
  handler = transport.call(null, {
240
277
  url: options.url,
241
278
  dataType: options.dataType,
242
279
  data: data,
280
+ type: type,
243
281
  success: function (data) {
244
282
  if (requestNumber < requestSequence) {
245
283
  return;
@@ -337,6 +375,10 @@
337
375
  });
338
376
  });
339
377
 
378
+ function evaluate(val) {
379
+ return $.isFunction(val) ? val() : val;
380
+ }
381
+
340
382
  /**
341
383
  * Creates a new class
342
384
  *
@@ -354,6 +396,7 @@
354
396
 
355
397
  AbstractSelect2 = clazz(Object, {
356
398
 
399
+ // abstract
357
400
  bind: function (func) {
358
401
  var self = this;
359
402
  return function () {
@@ -361,6 +404,7 @@
361
404
  };
362
405
  },
363
406
 
407
+ // abstract
364
408
  init: function (opts) {
365
409
  var results, search, resultsSelector = ".select2-results";
366
410
 
@@ -377,11 +421,15 @@
377
421
 
378
422
  this.enabled=true;
379
423
  this.container = this.createContainer();
424
+ this.body = opts.element.closest("body"); // cache for future access
380
425
 
381
426
  if (opts.element.attr("class") !== undefined) {
382
427
  this.container.addClass(opts.element.attr("class"));
383
428
  }
384
429
 
430
+ this.container.css(evaluate(opts.containerCss));
431
+ this.container.addClass(evaluate(opts.containerCssClass));
432
+
385
433
  // swap container for the element
386
434
  this.opts.element
387
435
  .data("select2", this)
@@ -390,10 +438,12 @@
390
438
  this.container.data("select2", this);
391
439
 
392
440
  this.dropdown = this.container.find(".select2-drop");
441
+ this.dropdown.css(evaluate(opts.dropdownCss));
442
+ this.dropdown.addClass(evaluate(opts.dropdownCssClass));
393
443
  this.dropdown.data("select2", this);
394
444
 
395
445
  this.results = results = this.container.find(resultsSelector);
396
- this.search = search = this.container.find("input[type=text]");
446
+ this.search = search = this.container.find("input.select2-input");
397
447
 
398
448
  this.resultsPage = 0;
399
449
  this.context = null;
@@ -448,6 +498,7 @@
448
498
  if (opts.element.is(":disabled")) this.disable();
449
499
  },
450
500
 
501
+ // abstract
451
502
  destroy: function () {
452
503
  var select2 = this.opts.element.data("select2");
453
504
  if (select2 !== undefined) {
@@ -456,13 +507,14 @@
456
507
  select2.opts.element
457
508
  .removeData("select2")
458
509
  .unbind(".select2")
459
- .show();
510
+ .show();
460
511
  }
461
512
  },
462
513
 
514
+ // abstract
463
515
  prepareOpts: function (opts) {
464
516
  var element, select, idKey;
465
-
517
+
466
518
  element = opts.element;
467
519
 
468
520
  if (element.get(0).tagName.toLowerCase() === "select") {
@@ -479,8 +531,12 @@
479
531
  }
480
532
 
481
533
  opts = $.extend({}, {
482
- populateResults: function(container, results) {
483
- var uidToData={}, populate, markup=[], uid, data, result, children;
534
+ containerCss: {},
535
+ dropdownCss: {},
536
+ containerCssClass: "",
537
+ dropdownCssClass: "",
538
+ populateResults: function(container, results, query) {
539
+ var uidToData={}, populate, markup=[], uid, data, result, children, formatted;
484
540
 
485
541
  populate=function(results, depth) {
486
542
 
@@ -503,7 +559,13 @@
503
559
  uidToData[uid]=result;
504
560
  }
505
561
 
506
- markup.push("><div class='select2-result-label'>"+opts.formatResult(result)+"</div>");
562
+ markup.push("><div class='select2-result-label'>");
563
+ formatted=opts.formatResult(result, query, markup);
564
+ // for backwards compat with <3.0 versions
565
+ if (formatted!==undefined) {
566
+ markup.push(formatted);
567
+ }
568
+ markup.push("</div>");
507
569
 
508
570
  if (compound) {
509
571
  markup.push("<ul class='select2-result-sub'>");
@@ -525,22 +587,19 @@
525
587
  }
526
588
 
527
589
  for (uid in uidToData) {
528
- $("#select2-result-"+uid).data("select2-data", uidToData[uid]);
590
+ $("#select2-result-"+uid, container).data("select2-data", uidToData[uid]);
529
591
  }
530
592
 
531
593
  },
532
- formatResult: function(result) {
533
- return result.text;
594
+ formatResult: function(result, query, markup) {
595
+ markMatch(result.text, query.term, markup);
534
596
  },
535
597
  formatSelection: function (data) {
536
- if (data.fullText) {
537
- return data.fullText;
538
- } else {
539
- return data.text;
540
- }
598
+ return data.fullText || data.text;
541
599
  },
542
600
  formatNoMatches: function () { return "No matches found"; },
543
601
  formatInputTooShort: function (input, min) { return "Please enter " + (min - input.length) + " more characters"; },
602
+ formatLoadMore: function (pageNumber) { return "Loading more results..."; },
544
603
  minimumResultsForSearch: 0,
545
604
  minimumInputLength: 0,
546
605
  id: function (e) { return e.id; },
@@ -558,7 +617,7 @@
558
617
  opts.query = this.bind(function (query) {
559
618
  var data = { results: [], more: false },
560
619
  term = query.term,
561
- process;
620
+ children, firstChild, process;
562
621
 
563
622
  process=function(element, collection) {
564
623
  var group;
@@ -568,14 +627,24 @@
568
627
  }
569
628
  } else if (element.is("optgroup")) {
570
629
  group={text:element.attr("label"), children:[]};
571
- element.children().each(function() { process($(this), group.children); });
630
+ element.children().each2(function(i, elm) { process(elm, group.children); });
572
631
  if (group.children.length>0) {
573
632
  collection.push(group);
574
633
  }
575
634
  }
576
635
  };
577
636
 
578
- element.children().each(function() { process($(this), data.results); });
637
+ children=element.children();
638
+
639
+ // ignore the placeholder option if there is one
640
+ if (this.getPlaceholder() !== undefined && children.length > 0) {
641
+ firstChild = children[0];
642
+ if ($(firstChild).text() === "") {
643
+ children=children.not(firstChild);
644
+ }
645
+ }
646
+
647
+ children.each2(function(i, elm) { process(elm, data.results); });
579
648
 
580
649
  query.callback(data);
581
650
  });
@@ -610,6 +679,7 @@
610
679
  /**
611
680
  * Monitor the original element for changes and update select2 accordingly
612
681
  */
682
+ // abstract
613
683
  monitorSource: function () {
614
684
  this.opts.element.bind("change.select2", this.bind(function (e) {
615
685
  if (this.opts.element.data("select2-change-triggered") !== true) {
@@ -621,14 +691,19 @@
621
691
  /**
622
692
  * Triggers the change event on the source element
623
693
  */
624
- triggerChange: function () {
625
- // Prevents recursive triggering
694
+ // abstract
695
+ triggerChange: function (details) {
696
+
697
+ details = details || {};
698
+ details= $.extend({}, details, { type: "change", val: this.val() });
699
+ // prevents recursive triggering
626
700
  this.opts.element.data("select2-change-triggered", true);
627
- this.opts.element.trigger("change");
701
+ this.opts.element.trigger(details);
628
702
  this.opts.element.data("select2-change-triggered", false);
629
703
  },
630
704
 
631
705
 
706
+ // abstract
632
707
  enable: function() {
633
708
  if (this.enabled) return;
634
709
 
@@ -636,6 +711,7 @@
636
711
  this.container.removeClass("select2-container-disabled");
637
712
  },
638
713
 
714
+ // abstract
639
715
  disable: function() {
640
716
  if (!this.enabled) return;
641
717
 
@@ -645,10 +721,12 @@
645
721
  this.container.addClass("select2-container-disabled");
646
722
  },
647
723
 
724
+ // abstract
648
725
  opened: function () {
649
726
  return this.container.hasClass("select2-dropdown-open");
650
727
  },
651
728
 
729
+ // abstract
652
730
  positionDropdown: function() {
653
731
  var offset = this.container.offset();
654
732
  var height = this.container.outerHeight();
@@ -658,17 +736,20 @@
658
736
  left: offset.left,
659
737
  width: width
660
738
  }
661
- if (this.opts.dropdownZIndex !== undefined) {
662
- css["z-index"] = this.opts.dropdownZIndex;
663
- }
664
739
  this.dropdown.css(css);
665
740
  },
666
741
 
742
+ // abstract
667
743
  open: function () {
668
744
  if (this.opened()) return;
669
745
 
670
746
  this.container.addClass("select2-dropdown-open").addClass("select2-container-active");
671
- this.dropdown.detach().appendTo('body').addClass("select2-drop-active");
747
+ if(this.dropdown[0] !== this.body.children().last()[0]) {
748
+ // ensure the dropdown is the last child of body, so the z-index is always respected correctly
749
+ this.dropdown.detach().appendTo(this.body);
750
+ }
751
+
752
+ this.dropdown.addClass("select2-drop-active");
672
753
 
673
754
  this.positionDropdown();
674
755
 
@@ -678,6 +759,7 @@
678
759
  this.focusSearch();
679
760
  },
680
761
 
762
+ // abstract
681
763
  close: function () {
682
764
  if (!this.opened()) return;
683
765
 
@@ -687,17 +769,20 @@
687
769
  this.clearSearch();
688
770
  },
689
771
 
772
+ // abstract
690
773
  clearSearch: function () {
691
774
 
692
775
  },
693
776
 
777
+ // abstract
694
778
  ensureHighlightVisible: function () {
695
779
  var results = this.results, children, index, child, hb, rb, y, more;
696
-
697
- children = results.find(".select2-result");
780
+
698
781
  index = this.highlight();
699
782
 
700
783
  if (index < 0) return;
784
+
785
+ children = results.find(".select2-result");
701
786
 
702
787
  child = $(children[index]);
703
788
 
@@ -723,6 +808,7 @@
723
808
  }
724
809
  },
725
810
 
811
+ // abstract
726
812
  moveHighlight: function (delta) {
727
813
  var choices = this.results.find(".select2-result"),
728
814
  index = this.highlight();
@@ -736,6 +822,7 @@
736
822
  }
737
823
  },
738
824
 
825
+ // abstract
739
826
  highlight: function (index) {
740
827
  var choices = this.results.find(".select2-result .select2-result-label");
741
828
 
@@ -755,24 +842,28 @@
755
842
  $(choices[index]).addClass("select2-highlighted");
756
843
  this.ensureHighlightVisible();
757
844
 
758
- if (this.opened()) this.focusSearch();
845
+ //if (this.opened()) this.focusSearch();
759
846
  },
760
847
 
848
+ // abstract
761
849
  highlightUnderEvent: function (event) {
762
- var el = $(event.target).closest(".select2-result");
763
- var choices = this.results.find('.select2-result');
764
- if (el.length > 0) {
850
+ var el = $(event.target).closest(".select2-result");
851
+ if (el.length > 0 && !el.is(".select2-highlighted")) {
852
+ var choices = this.results.find('.select2-result');
765
853
  this.highlight(choices.index(el));
766
854
  }
767
855
  },
768
856
 
857
+ // abstract
769
858
  loadMoreIfNeeded: function () {
770
859
  var results = this.results,
771
860
  more = results.find("li.select2-more-results"),
772
861
  below, // pixels the element is below the scroll fold, below==0 is when the element is starting to be visible
773
862
  offset = -1, // index of first element without data
774
863
  page = this.resultsPage + 1,
775
- self=this;
864
+ self=this,
865
+ term=this.search.val(),
866
+ context=this.context;
776
867
 
777
868
  if (more.length === 0) return;
778
869
  below = more.offset().top - results.offset().top - results.height();
@@ -780,14 +871,13 @@
780
871
  if (below <= 0) {
781
872
  more.addClass("select2-active");
782
873
  this.opts.query({
783
- term: this.search.val(),
874
+ term: term,
784
875
  page: page,
785
- context: this.context,
876
+ context: context,
786
877
  matcher: this.opts.matcher,
787
878
  callback: this.bind(function (data) {
788
- console.log("load more callback", data);
789
879
 
790
- self.opts.populateResults(results, data.results);
880
+ self.opts.populateResults(results, data.results, {term: term, page: page, context:context});
791
881
 
792
882
  if (data.more===true) {
793
883
  more.detach();
@@ -804,6 +894,7 @@
804
894
  /**
805
895
  * @param initial whether or not this is the call to this method right after the dropdown has been opened
806
896
  */
897
+ // abstract
807
898
  updateResults: function (initial) {
808
899
  var search = this.search, results = this.results, opts = this.opts, self=this;
809
900
 
@@ -860,34 +951,33 @@
860
951
  }
861
952
 
862
953
  results.empty();
863
- self.opts.populateResults(results, data.results);
954
+ self.opts.populateResults(results, data.results, {term: search.val(), page: this.resultsPage, context:null});
864
955
  postRender();
865
956
 
866
957
  if (data.more === true) {
867
- results.children().filter(":last").append("<li class='select2-more-results'>Loading more results...</li>");
958
+ results.children().filter(":last").append("<li class='select2-more-results'>" + opts.formatLoadMore(this.resultsPage) + "</li>");
868
959
  }
869
960
 
870
961
  this.postprocessResults(data, initial);
871
962
  })});
872
963
  },
873
964
 
965
+ // abstract
874
966
  cancel: function () {
875
967
  this.close();
876
968
  },
877
969
 
970
+ // abstract
878
971
  blur: function () {
879
- /* we do this in a timeout so that current event processing can complete before this code is executed.
880
- this allows tab index to be preserved even if this code blurs the textfield */
881
- window.setTimeout(this.bind(function () {
882
- this.close();
883
- this.container.removeClass("select2-container-active");
884
- this.dropdown.removeClass("select2-drop-active");
885
- this.clearSearch();
886
- this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
887
- this.search.blur();
888
- }), 10);
972
+ this.close();
973
+ this.container.removeClass("select2-container-active");
974
+ this.dropdown.removeClass("select2-drop-active");
975
+ if (this.search.is(":focus")) { this.search.blur(); }
976
+ this.clearSearch();
977
+ this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
889
978
  },
890
979
 
980
+ // abstract
891
981
  focusSearch: function () {
892
982
  /* we do this in a timeout so that current event processing can complete before this code is executed.
893
983
  this makes sure the search field is focussed even if the current event would blur it */
@@ -896,6 +986,7 @@
896
986
  }), 10);
897
987
  },
898
988
 
989
+ // abstract
899
990
  selectHighlighted: function () {
900
991
  var data = this.results.find(".select2-highlighted").not(".select2-disabled").closest('.select2-result').not('.select2-result-unselectable').data("select2-data");
901
992
  if (data) {
@@ -903,6 +994,7 @@
903
994
  }
904
995
  },
905
996
 
997
+ // abstract
906
998
  getPlaceholder: function () {
907
999
  return this.opts.element.attr("placeholder") || this.opts.element.data("placeholder") || this.opts.placeholder;
908
1000
  },
@@ -915,6 +1007,7 @@
915
1007
  *
916
1008
  * @returns The width string (with units) for the container.
917
1009
  */
1010
+ // abstract
918
1011
  getContainerWidth: function () {
919
1012
  var style, attrs, matches, i, l;
920
1013
  if (this.opts.width !== undefined)
@@ -936,24 +1029,26 @@
936
1029
 
937
1030
  SingleSelect2 = clazz(AbstractSelect2, {
938
1031
 
1032
+ // single
939
1033
  createContainer: function () {
940
1034
  return $("<div></div>", {
941
1035
  "class": "select2-container",
942
1036
  "style": "width: " + this.getContainerWidth()
943
1037
  }).html([
944
- " <a href='javascript:void(0)' class='select2-choice'>",
1038
+ " <a href='javascript:void(0)' class='select2-choice'><input type='text' class='select2-offscreen select2-focusser'/>",
945
1039
  " <span></span><abbr class='select2-search-choice-close' style='display:none;'></abbr>",
946
1040
  " <div><b></b></div>" ,
947
1041
  "</a>",
948
1042
  " <div class='select2-drop' style='display:none;'>" ,
949
1043
  " <div class='select2-search'>" ,
950
- " <input type='text' autocomplete='off'/>" ,
1044
+ " <input type='text' autocomplete='off' class='select2-input'/>" ,
951
1045
  " </div>" ,
952
1046
  " <ul class='select2-results'>" ,
953
1047
  " </ul>" ,
954
1048
  "</div>"].join(""));
955
1049
  },
956
1050
 
1051
+ // single
957
1052
  open: function () {
958
1053
 
959
1054
  if (this.opened()) return;
@@ -962,25 +1057,30 @@
962
1057
 
963
1058
  },
964
1059
 
1060
+ // single
965
1061
  close: function () {
966
1062
  if (!this.opened()) return;
967
1063
  this.parent.close.apply(this, arguments);
968
1064
  },
969
1065
 
1066
+ // single
970
1067
  focus: function () {
971
1068
  this.close();
972
1069
  this.selection.focus();
973
1070
  },
974
1071
 
1072
+ // single
975
1073
  isFocused: function () {
976
1074
  return this.selection.is(":focus");
977
1075
  },
978
1076
 
1077
+ // single
979
1078
  cancel: function () {
980
1079
  this.parent.cancel.apply(this, arguments);
981
1080
  this.selection.focus();
982
1081
  },
983
1082
 
1083
+ // single
984
1084
  initContainer: function () {
985
1085
 
986
1086
  var selection,
@@ -988,7 +1088,8 @@
988
1088
  dropdown = this.dropdown,
989
1089
  containers = $([this.container.get(0), this.dropdown.get(0)]),
990
1090
  clickingInside = false,
991
- selector = ".select2-choice";
1091
+ selector = ".select2-choice",
1092
+ focusser=container.find("input.select2-focusser");
992
1093
 
993
1094
  this.selection = selection = container.find(selector);
994
1095
 
@@ -1006,7 +1107,7 @@
1006
1107
  return;
1007
1108
  case KEY.ESC:
1008
1109
  this.cancel(e);
1009
- e.preventDefault();
1110
+ killEvent(e);
1010
1111
  return;
1011
1112
  }
1012
1113
  }));
@@ -1039,9 +1140,10 @@
1039
1140
  }
1040
1141
  }));
1041
1142
  containers.delegate(selector, "focus", function () { if (this.enabled) { containers.addClass("select2-container-active"); dropdown.addClass("select2-drop-active"); }});
1042
- containers.delegate(selector, "blur", this.bind(function () {
1143
+ containers.delegate(selector, "blur", this.bind(function (e) {
1043
1144
  if (clickingInside) return;
1044
- if (!this.opened()) this.blur();
1145
+ if (e.target===focusser.get(0)) return; // ignore blurs from focusser
1146
+ if (!this.opened()) { this.blur(); }
1045
1147
  }));
1046
1148
 
1047
1149
  selection.delegate("abbr", "click", this.bind(function (e) {
@@ -1050,14 +1152,24 @@
1050
1152
  killEvent(e);
1051
1153
  this.close();
1052
1154
  this.triggerChange();
1155
+ selection.focus();
1053
1156
  }));
1054
1157
 
1055
1158
  this.setPlaceholder();
1159
+
1160
+ focusser.bind("focus", function() { selection.focus(); });
1161
+ selection.bind("focus", this.bind(function() {
1162
+ focusser.hide();
1163
+ this.container.addClass("select2-container-active");
1164
+ }));
1165
+ selection.bind("blur", function() { focusser.show(); });
1166
+ this.opts.element.bind("open", function() { focusser.hide(); });
1056
1167
  },
1057
1168
 
1058
1169
  /**
1059
1170
  * Sets selection based on source element's value
1060
1171
  */
1172
+ // single
1061
1173
  initSelection: function () {
1062
1174
  var selected;
1063
1175
  if (this.opts.element.val() === "") {
@@ -1073,6 +1185,7 @@
1073
1185
  this.setPlaceholder();
1074
1186
  },
1075
1187
 
1188
+ // single
1076
1189
  prepareOpts: function () {
1077
1190
  var opts = this.parent.prepareOpts.apply(this, arguments);
1078
1191
 
@@ -1088,6 +1201,7 @@
1088
1201
  return opts;
1089
1202
  },
1090
1203
 
1204
+ // single
1091
1205
  setPlaceholder: function () {
1092
1206
  var placeholder = this.getPlaceholder();
1093
1207
 
@@ -1107,13 +1221,14 @@
1107
1221
  }
1108
1222
  },
1109
1223
 
1224
+ // single
1110
1225
  postprocessResults: function (data, initial) {
1111
1226
  var selected = 0, self = this, showSearchInput = true;
1112
1227
 
1113
1228
  // find the selected element in the result list
1114
1229
 
1115
- this.results.find(".select2-result").each(function (i) {
1116
- if (equal(self.id($(this).data("select2-data")), self.opts.element.val())) {
1230
+ this.results.find(".select2-result").each2(function (i, elm) {
1231
+ if (equal(self.id(elm.data("select2-data")), self.opts.element.val())) {
1117
1232
  selected = i;
1118
1233
  return false;
1119
1234
  }
@@ -1126,15 +1241,18 @@
1126
1241
  // hide the search box if this is the first we got the results and there are a few of them
1127
1242
 
1128
1243
  if (initial === true) {
1244
+ // TODO below we use data.results.length, but what we really need is something recursive to calc the length
1245
+ // TODO in case there are optgroups
1129
1246
  showSearchInput = this.showSearchInput = data.results.length >= this.opts.minimumResultsForSearch;
1130
- this.container.find(".select2-search")[showSearchInput ? "removeClass" : "addClass"]("select2-search-hidden");
1247
+ this.dropdown.find(".select2-search")[showSearchInput ? "removeClass" : "addClass"]("select2-search-hidden");
1131
1248
 
1132
1249
  //add "select2-with-searchbox" to the container if search box is shown
1133
- this.container[showSearchInput ? "addClass" : "removeClass"]("select2-with-searchbox");
1250
+ $(this.dropdown, this.container)[showSearchInput ? "addClass" : "removeClass"]("select2-with-searchbox");
1134
1251
  }
1135
1252
 
1136
1253
  },
1137
1254
 
1255
+ // single
1138
1256
  onSelect: function (data) {
1139
1257
  var old = this.opts.element.val();
1140
1258
 
@@ -1146,6 +1264,7 @@
1146
1264
  if (!equal(old, this.id(data))) { this.triggerChange(); }
1147
1265
  },
1148
1266
 
1267
+ // single
1149
1268
  updateSelection: function (data) {
1150
1269
  this.selection
1151
1270
  .find("span")
@@ -1158,6 +1277,7 @@
1158
1277
  }
1159
1278
  },
1160
1279
 
1280
+ // single
1161
1281
  val: function () {
1162
1282
  var val, data = null;
1163
1283
 
@@ -1171,8 +1291,8 @@
1171
1291
  // val is an id
1172
1292
  this.select
1173
1293
  .val(val)
1174
- .find(":selected").each(function () {
1175
- data = {id: $(this).attr("value"), text: $(this).text()};
1294
+ .find(":selected").each2(function (i, elm) {
1295
+ data = {id: elm.attr("value"), text: elm.text()};
1176
1296
  return false;
1177
1297
  });
1178
1298
  this.updateSelection(data);
@@ -1185,6 +1305,7 @@
1185
1305
 
1186
1306
  },
1187
1307
 
1308
+ // single
1188
1309
  clearSearch: function () {
1189
1310
  this.search.val("");
1190
1311
  }
@@ -1192,6 +1313,7 @@
1192
1313
 
1193
1314
  MultiSelect2 = clazz(AbstractSelect2, {
1194
1315
 
1316
+ // multi
1195
1317
  createContainer: function () {
1196
1318
  return $("<div></div>", {
1197
1319
  "class": "select2-container select2-container-multi",
@@ -1200,7 +1322,7 @@
1200
1322
  " <ul class='select2-choices'>",
1201
1323
  //"<li class='select2-search-choice'><span>California</span><a href="javascript:void(0)" class="select2-search-choice-close"></a></li>" ,
1202
1324
  " <li class='select2-search-field'>" ,
1203
- " <input type='text' autocomplete='off' style='width: 25px;'>" ,
1325
+ " <input type='text' autocomplete='off' style='width: 25px;' class='select2-input'>" ,
1204
1326
  " </li>" ,
1205
1327
  "</ul>" ,
1206
1328
  "<div class='select2-drop select2-drop-multi' style='display:none;'>" ,
@@ -1209,6 +1331,7 @@
1209
1331
  "</div>"].join(""));
1210
1332
  },
1211
1333
 
1334
+ // multi
1212
1335
  prepareOpts: function () {
1213
1336
  var opts = this.parent.prepareOpts.apply(this, arguments);
1214
1337
 
@@ -1219,11 +1342,11 @@
1219
1342
  // TODO validate placeholder is a string if specified
1220
1343
 
1221
1344
  if (opts.element.get(0).tagName.toLowerCase() === "select") {
1222
- // install sthe selection initializer
1345
+ // install the selection initializer
1223
1346
  opts.initSelection = function (element) {
1224
1347
  var data = [];
1225
- element.find(":selected").each(function () {
1226
- data.push({id: $(this).attr("value"), text: $(this).text()});
1348
+ element.find(":selected").each2(function (i, elm) {
1349
+ data.push({id: elm.attr("value"), text: elm.text()});
1227
1350
  });
1228
1351
  return data;
1229
1352
  };
@@ -1232,6 +1355,7 @@
1232
1355
  return opts;
1233
1356
  },
1234
1357
 
1358
+ // multi
1235
1359
  initContainer: function () {
1236
1360
 
1237
1361
  var selector = ".select2-choices", selection;
@@ -1276,7 +1400,7 @@
1276
1400
  return;
1277
1401
  case KEY.ESC:
1278
1402
  this.cancel(e);
1279
- e.preventDefault();
1403
+ killEvent(e);
1280
1404
  return;
1281
1405
  }
1282
1406
  }
@@ -1313,6 +1437,7 @@
1313
1437
  this.clearSearch();
1314
1438
  },
1315
1439
 
1440
+ // multi
1316
1441
  enable: function() {
1317
1442
  if (this.enabled) return;
1318
1443
 
@@ -1321,6 +1446,7 @@
1321
1446
  this.search.show();
1322
1447
  },
1323
1448
 
1449
+ // multi
1324
1450
  disable: function() {
1325
1451
  if (!this.enabled) return;
1326
1452
 
@@ -1329,6 +1455,7 @@
1329
1455
  this.search.hide();
1330
1456
  },
1331
1457
 
1458
+ // multi
1332
1459
  initSelection: function () {
1333
1460
  var data;
1334
1461
  if (this.opts.element.val() === "") {
@@ -1347,11 +1474,11 @@
1347
1474
  this.clearSearch();
1348
1475
  },
1349
1476
 
1477
+ // multi
1350
1478
  clearSearch: function () {
1351
1479
  var placeholder = this.getPlaceholder();
1352
1480
 
1353
1481
  if (placeholder !== undefined && this.getVal().length === 0 && this.search.hasClass("select2-focused") === false) {
1354
-
1355
1482
  this.search.val(placeholder).addClass("select2-default");
1356
1483
  // stretch the search box to full width of the container so as much of the placeholder is visible as possible
1357
1484
  this.search.width(this.getContainerWidth());
@@ -1360,33 +1487,40 @@
1360
1487
  }
1361
1488
  },
1362
1489
 
1490
+ // multi
1363
1491
  clearPlaceholder: function () {
1364
1492
  if (this.search.hasClass("select2-default")) {
1365
1493
  this.search.val("").removeClass("select2-default");
1366
1494
  }
1367
1495
  },
1368
1496
 
1497
+ // multi
1369
1498
  open: function () {
1370
1499
  if (this.opened()) return;
1371
1500
  this.parent.open.apply(this, arguments);
1372
- this.resizeSearch();
1501
+ this.clearPlaceholder();
1502
+ this.resizeSearch();
1373
1503
  this.focusSearch();
1374
1504
  },
1375
1505
 
1506
+ // multi
1376
1507
  close: function () {
1377
1508
  if (!this.opened()) return;
1378
1509
  this.parent.close.apply(this, arguments);
1379
1510
  },
1380
1511
 
1512
+ // multi
1381
1513
  focus: function () {
1382
1514
  this.close();
1383
1515
  this.search.focus();
1384
1516
  },
1385
1517
 
1518
+ // multi
1386
1519
  isFocused: function () {
1387
1520
  return this.search.hasClass("select2-focused");
1388
1521
  },
1389
1522
 
1523
+ // multi
1390
1524
  updateSelection: function (data) {
1391
1525
  var ids = [], filtered = [], self = this;
1392
1526
 
@@ -1406,6 +1540,7 @@
1406
1540
  self.postprocessResults();
1407
1541
  },
1408
1542
 
1543
+ // multi
1409
1544
  onSelect: function (data) {
1410
1545
  this.addSelectedChoice(data);
1411
1546
  if (this.select) { this.postprocessResults(); }
@@ -1420,16 +1555,18 @@
1420
1555
 
1421
1556
  // since its not possible to select an element that has already been
1422
1557
  // added we do not need to check if this is a new element before firing change
1423
- this.triggerChange();
1558
+ this.triggerChange({ added: data });
1424
1559
 
1425
1560
  this.focusSearch();
1426
1561
  },
1427
1562
 
1563
+ // multi
1428
1564
  cancel: function () {
1429
1565
  this.close();
1430
1566
  this.focusSearch();
1431
1567
  },
1432
1568
 
1569
+ // multi
1433
1570
  addSelectedChoice: function (data) {
1434
1571
  var choice,
1435
1572
  id = this.id(data),
@@ -1465,8 +1602,10 @@
1465
1602
  this.setVal(val);
1466
1603
  },
1467
1604
 
1605
+ // multi
1468
1606
  unselect: function (selected) {
1469
1607
  var val = this.getVal(),
1608
+ data,
1470
1609
  index;
1471
1610
 
1472
1611
  selected = selected.closest(".select2-search-choice");
@@ -1475,7 +1614,9 @@
1475
1614
  throw "Invalid argument: " + selected + ". Must be .select2-search-choice";
1476
1615
  }
1477
1616
 
1478
- index = indexOf(this.id(selected.data("select2-data")), val);
1617
+ data = selected.data("select2-data");
1618
+
1619
+ index = indexOf(this.id(data), val);
1479
1620
 
1480
1621
  if (index >= 0) {
1481
1622
  val.splice(index, 1);
@@ -1483,16 +1624,17 @@
1483
1624
  if (this.select) this.postprocessResults();
1484
1625
  }
1485
1626
  selected.remove();
1486
- this.triggerChange();
1627
+ this.triggerChange({ removed: data });
1487
1628
  },
1488
1629
 
1630
+ // multi
1489
1631
  postprocessResults: function () {
1490
1632
  var val = this.getVal(),
1491
1633
  choices = this.results.find(".select2-result"),
1492
1634
  self = this;
1493
1635
 
1494
- choices.each(function () {
1495
- var choice = $(this), id = self.id(choice.data("select2-data"));
1636
+ choices.each2(function (i, choice) {
1637
+ var id = self.id(choice.data("select2-data"));
1496
1638
  if (indexOf(id, val) >= 0) {
1497
1639
  choice.addClass("select2-disabled");
1498
1640
  } else {
@@ -1500,8 +1642,8 @@
1500
1642
  }
1501
1643
  });
1502
1644
 
1503
- choices.each(function (i) {
1504
- if (!$(this).hasClass("select2-disabled")) {
1645
+ choices.each2(function (i, choice) {
1646
+ if (!choice.hasClass("select2-disabled")) {
1505
1647
  self.highlight(i);
1506
1648
  return false;
1507
1649
  }
@@ -1509,9 +1651,11 @@
1509
1651
 
1510
1652
  },
1511
1653
 
1654
+ // multi
1512
1655
  resizeSearch: function () {
1513
1656
 
1514
- var minimumWidth, left, maxWidth, containerLeft, searchWidth;
1657
+ var minimumWidth, left, maxWidth, containerLeft, searchWidth,
1658
+ sideBorderPadding = getSideBorderPadding(this.search);
1515
1659
 
1516
1660
  minimumWidth = measureTextWidth(this.search) + 10;
1517
1661
 
@@ -1520,18 +1664,19 @@
1520
1664
  maxWidth = this.selection.width();
1521
1665
  containerLeft = this.selection.offset().left;
1522
1666
 
1523
- searchWidth = maxWidth - (left - containerLeft) - getSideBorderPadding(this.search);
1667
+ searchWidth = maxWidth - (left - containerLeft) - sideBorderPadding;
1524
1668
 
1525
1669
  if (searchWidth < minimumWidth) {
1526
- searchWidth = maxWidth - getSideBorderPadding(this.search);
1670
+ searchWidth = maxWidth - sideBorderPadding;
1527
1671
  }
1528
1672
 
1529
1673
  if (searchWidth < 40) {
1530
- searchWidth = maxWidth - getSideBorderPadding(this.search);
1674
+ searchWidth = maxWidth - sideBorderPadding;
1531
1675
  }
1532
1676
  this.search.width(searchWidth);
1533
1677
  },
1534
1678
 
1679
+ // multi
1535
1680
  getVal: function () {
1536
1681
  var val;
1537
1682
  if (this.select) {
@@ -1543,6 +1688,7 @@
1543
1688
  }
1544
1689
  },
1545
1690
 
1691
+ // multi
1546
1692
  setVal: function (val) {
1547
1693
  var unique;
1548
1694
  if (this.select) {
@@ -1557,6 +1703,7 @@
1557
1703
  }
1558
1704
  },
1559
1705
 
1706
+ // multi
1560
1707
  val: function () {
1561
1708
  var val, data = [], self=this;
1562
1709
 
@@ -1584,6 +1731,8 @@
1584
1731
 
1585
1732
  this.clearSearch();
1586
1733
  },
1734
+
1735
+ // multi
1587
1736
  onSortStart: function() {
1588
1737
  if (this.select) {
1589
1738
  throw new Error("Sorting of elements is not supported when attached to <select>. Attach to <input type='hidden'/> instead.");
@@ -1594,6 +1743,8 @@
1594
1743
  // hide the container
1595
1744
  this.searchContainer.hide();
1596
1745
  },
1746
+
1747
+ // multi
1597
1748
  onSortEnd:function() {
1598
1749
 
1599
1750
  var val=[], self=this;
@@ -1665,7 +1816,8 @@
1665
1816
  local: local,
1666
1817
  tags: tags
1667
1818
  }, util: {
1668
- debounce: debounce
1819
+ debounce: debounce,
1820
+ markMatch: markMatch
1669
1821
  }, "class": {
1670
1822
  "abstract": AbstractSelect2,
1671
1823
  "single": SingleSelect2,
@@ -287,8 +287,7 @@
287
287
  &.select2-result-with-children .select2-result-label
288
288
  font-weight: bold
289
289
  .select2-result-label
290
- line-height: 80%
291
- padding: 7px 7px 8px
290
+ padding: 3px 7px 4px
292
291
  margin: 0
293
292
  cursor: pointer
294
293
  .select2-highlighted
@@ -386,6 +385,8 @@
386
385
  -moz-box-shadow: none
387
386
  -o-box-shadow: none
388
387
  box-shadow: none
388
+ &.select2-active
389
+ background: white image-url('spinner.gif') no-repeat 100% !important
389
390
 
390
391
  .select2-default
391
392
  color: #999 !important
@@ -454,3 +455,10 @@
454
455
  /* disabled styles
455
456
 
456
457
  /* end multiselect
458
+
459
+ .select2-match
460
+ text-decoration: underline
461
+
462
+ .select2-offscreen
463
+ position: absolute
464
+ left: -1000px
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: select2-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-15 00:00:00.000000000 Z
12
+ date: 2012-06-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: thor
16
- requirement: &87475570 !ruby/object:Gem::Requirement
16
+ requirement: &83485240 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0.14'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *87475570
24
+ version_requirements: *83485240
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: bundler
27
- requirement: &87475320 !ruby/object:Gem::Requirement
27
+ requirement: &83484990 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '1.0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *87475320
35
+ version_requirements: *83484990
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rails
38
- requirement: &87475090 !ruby/object:Gem::Requirement
38
+ requirement: &83484760 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '3.0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *87475090
46
+ version_requirements: *83484760
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: sass
49
- requirement: &87474860 !ruby/object:Gem::Requirement
49
+ requirement: &83484530 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: '3.1'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *87474860
57
+ version_requirements: *83484530
58
58
  description: Select2 is a jQuery based replacement for select boxes. It supports searching,
59
59
  remote data sets, and infinite scrolling of results. This gem integrates Select2
60
60
  with Rails asset pipeline for easy of use.