select2-rails 2.1.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/select2-rails/version.rb +1 -1
- data/vendor/assets/javascripts/select2.js +928 -312
- data/vendor/assets/stylesheets/select2.css.sass +201 -118
- metadata +79 -83
@@ -1,7 +1,7 @@
|
|
1
1
|
/*
|
2
2
|
Copyright 2012 Igor Vaynberg
|
3
|
-
|
4
|
-
Version:
|
3
|
+
|
4
|
+
Version: 3.0 Timestamp: Tue Jul 31 21:09:16 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:
|
@@ -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;
|
43
|
+
var KEY, AbstractSelect2, SingleSelect2, MultiSelect2, nextUid, sizer;
|
24
44
|
|
25
45
|
KEY = {
|
26
46
|
TAB: 9,
|
@@ -59,6 +79,9 @@
|
|
59
79
|
case KEY.ALT:
|
60
80
|
return true;
|
61
81
|
}
|
82
|
+
|
83
|
+
if (k.metaKey) return true;
|
84
|
+
|
62
85
|
return false;
|
63
86
|
},
|
64
87
|
isFunctionKey: function (k) {
|
@@ -67,6 +90,16 @@
|
|
67
90
|
}
|
68
91
|
};
|
69
92
|
|
93
|
+
nextUid=(function() { var counter=1; return function() { return counter++; }; }());
|
94
|
+
|
95
|
+
function escapeMarkup(markup) {
|
96
|
+
if (markup && typeof(markup) === "string") {
|
97
|
+
return markup.replace("&", "&");
|
98
|
+
} else {
|
99
|
+
return markup;
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
70
103
|
function indexOf(value, array) {
|
71
104
|
var i = 0, l = array.length, v;
|
72
105
|
|
@@ -90,7 +123,7 @@
|
|
90
123
|
}
|
91
124
|
|
92
125
|
/**
|
93
|
-
* Compares equality of a and b taking into account that a and b may be strings, in which case
|
126
|
+
* Compares equality of a and b taking into account that a and b may be strings, in which case localeCompare is used
|
94
127
|
* @param a
|
95
128
|
* @param b
|
96
129
|
*/
|
@@ -122,28 +155,34 @@
|
|
122
155
|
}
|
123
156
|
|
124
157
|
function installKeyUpChangeEvent(element) {
|
158
|
+
var key="keyup-change-value";
|
125
159
|
element.bind("keydown", function () {
|
126
|
-
|
160
|
+
if ($.data(element, key) === undefined) {
|
161
|
+
$.data(element, key, element.val());
|
162
|
+
}
|
127
163
|
});
|
128
164
|
element.bind("keyup", function () {
|
129
|
-
|
165
|
+
var val= $.data(element, key);
|
166
|
+
if (val !== undefined && element.val() !== val) {
|
167
|
+
$.removeData(element, key);
|
130
168
|
element.trigger("keyup-change");
|
131
169
|
}
|
132
170
|
});
|
133
171
|
}
|
134
172
|
|
173
|
+
$(document).delegate("*", "mousemove", function (e) {
|
174
|
+
$.data(document, "select2-lastpos", {x: e.pageX, y: e.pageY});
|
175
|
+
});
|
176
|
+
|
135
177
|
/**
|
136
178
|
* filters mouse events so an event is fired only if the mouse moved.
|
137
179
|
*
|
138
180
|
* filters out mouse events that occur when mouse is stationary but
|
139
181
|
* the elements under the pointer are scrolled.
|
140
182
|
*/
|
141
|
-
$(document).delegate("*", "mousemove", function (e) {
|
142
|
-
$(document).data("select2-lastpos", {x: e.pageX, y: e.pageY});
|
143
|
-
});
|
144
183
|
function installFilteredMouseMove(element) {
|
145
|
-
|
146
|
-
var lastpos =
|
184
|
+
element.bind("mousemove", function (e) {
|
185
|
+
var lastpos = $.data(document, "select2-lastpos");
|
147
186
|
if (lastpos === undefined || lastpos.x !== e.pageX || lastpos.y !== e.pageY) {
|
148
187
|
$(e.target).trigger("mousemove-filtered", e);
|
149
188
|
}
|
@@ -166,6 +205,20 @@
|
|
166
205
|
};
|
167
206
|
}
|
168
207
|
|
208
|
+
/**
|
209
|
+
* A simple implementation of a thunk
|
210
|
+
* @param formula function used to lazily initialize the thunk
|
211
|
+
* @return {Function}
|
212
|
+
*/
|
213
|
+
function thunk(formula) {
|
214
|
+
var evaluated = false,
|
215
|
+
value;
|
216
|
+
return function() {
|
217
|
+
if (evaluated === false) { value = formula(); evaluated = true; }
|
218
|
+
return value;
|
219
|
+
};
|
220
|
+
};
|
221
|
+
|
169
222
|
function installDebouncedScroll(threshold, element) {
|
170
223
|
var notify = debounce(threshold, function (e) { element.trigger("scroll-debounced", e);});
|
171
224
|
element.bind("scroll", function (e) {
|
@@ -179,25 +232,41 @@
|
|
179
232
|
}
|
180
233
|
|
181
234
|
function measureTextWidth(e) {
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
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);
|
251
|
+
}
|
196
252
|
sizer.text(e.val());
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
253
|
+
return sizer.width();
|
254
|
+
}
|
255
|
+
|
256
|
+
function markMatch(text, term, markup) {
|
257
|
+
var match=text.toUpperCase().indexOf(term.toUpperCase()),
|
258
|
+
tl=term.length;
|
259
|
+
|
260
|
+
if (match<0) {
|
261
|
+
markup.push(text);
|
262
|
+
return;
|
263
|
+
}
|
264
|
+
|
265
|
+
markup.push(text.substring(0, match));
|
266
|
+
markup.push("<span class='select2-match'>");
|
267
|
+
markup.push(text.substring(match, match + tl));
|
268
|
+
markup.push("</span>");
|
269
|
+
markup.push(text.substring(match + tl, text.length));
|
201
270
|
}
|
202
271
|
|
203
272
|
/**
|
@@ -227,17 +296,18 @@
|
|
227
296
|
requestSequence += 1; // increment the sequence
|
228
297
|
var requestNumber = requestSequence, // this request's sequence number
|
229
298
|
data = options.data, // ajax data function
|
230
|
-
transport = options.transport || $.ajax
|
299
|
+
transport = options.transport || $.ajax,
|
300
|
+
type = options.type || 'GET'; // set type of request (GET or POST)
|
231
301
|
|
232
302
|
data = data.call(this, query.term, query.page, query.context);
|
233
303
|
|
234
|
-
if( null !== handler){
|
235
|
-
|
236
|
-
}
|
304
|
+
if( null !== handler) { handler.abort(); }
|
305
|
+
|
237
306
|
handler = transport.call(null, {
|
238
307
|
url: options.url,
|
239
308
|
dataType: options.dataType,
|
240
309
|
data: data,
|
310
|
+
type: type,
|
241
311
|
success: function (data) {
|
242
312
|
if (requestNumber < requestSequence) {
|
243
313
|
return;
|
@@ -267,12 +337,16 @@
|
|
267
337
|
*/
|
268
338
|
function local(options) {
|
269
339
|
var data = options, // data elements
|
340
|
+
dataText,
|
270
341
|
text = function (item) { return ""+item.text; }; // function used to retrieve the text portion of a data item that is matched against the search
|
271
342
|
|
272
343
|
if (!$.isArray(data)) {
|
273
344
|
text = data.text;
|
274
345
|
// if text is not a function we assume it to be a key name
|
275
|
-
if (!$.isFunction(text))
|
346
|
+
if (!$.isFunction(text)) {
|
347
|
+
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
|
348
|
+
text = function (item) { return item[dataText]; };
|
349
|
+
}
|
276
350
|
data = data.results;
|
277
351
|
}
|
278
352
|
|
@@ -312,18 +386,53 @@
|
|
312
386
|
};
|
313
387
|
}
|
314
388
|
|
389
|
+
/**
|
390
|
+
* Checks if the formatter function should be used.
|
391
|
+
*
|
392
|
+
* Throws an error if it is not a function. Returns true if it should be used,
|
393
|
+
* false if no formatting should be performed.
|
394
|
+
*
|
395
|
+
* @param formatter
|
396
|
+
*/
|
397
|
+
function checkFormatter(formatter, formatterName) {
|
398
|
+
if ($.isFunction(formatter)) return true;
|
399
|
+
if (!formatter) return fasle;
|
400
|
+
throw new Error("formatterName must be a function or a falsy value");
|
401
|
+
}
|
402
|
+
|
315
403
|
/**
|
316
404
|
* blurs any Select2 container that has focus when an element outside them was clicked or received focus
|
405
|
+
*
|
406
|
+
* also takes care of clicks on label tags that point to the source element
|
317
407
|
*/
|
318
408
|
$(document).ready(function () {
|
319
|
-
$(document).delegate("*", "mousedown
|
320
|
-
var target = $(e.target).closest("div.select2-container").get(0);
|
321
|
-
|
322
|
-
|
323
|
-
|
409
|
+
$(document).delegate("*", "mousedown touchend", function (e) {
|
410
|
+
var target = $(e.target).closest("div.select2-container").get(0), attr;
|
411
|
+
if (target) {
|
412
|
+
$(document).find("div.select2-container-active").each(function () {
|
413
|
+
if (this !== target) $(this).data("select2").blur();
|
414
|
+
});
|
415
|
+
} else {
|
416
|
+
target = $(e.target).closest("div.select2-drop").get(0);
|
417
|
+
$(document).find("div.select2-drop-active").each(function () {
|
418
|
+
if (this !== target) $(this).data("select2").blur();
|
419
|
+
});
|
420
|
+
}
|
421
|
+
|
422
|
+
target=$(e.target);
|
423
|
+
attr = target.attr("for");
|
424
|
+
if ("LABEL" === e.target.tagName && attr && attr.length > 0) {
|
425
|
+
target = $("#"+attr);
|
426
|
+
target = target.data("select2");
|
427
|
+
if (target !== undefined) { target.focus(); e.preventDefault();}
|
428
|
+
}
|
324
429
|
});
|
325
430
|
});
|
326
431
|
|
432
|
+
function evaluate(val) {
|
433
|
+
return $.isFunction(val) ? val() : val;
|
434
|
+
}
|
435
|
+
|
327
436
|
/**
|
328
437
|
* Creates a new class
|
329
438
|
*
|
@@ -341,6 +450,7 @@
|
|
341
450
|
|
342
451
|
AbstractSelect2 = clazz(Object, {
|
343
452
|
|
453
|
+
// abstract
|
344
454
|
bind: function (func) {
|
345
455
|
var self = this;
|
346
456
|
return function () {
|
@@ -348,6 +458,7 @@
|
|
348
458
|
};
|
349
459
|
},
|
350
460
|
|
461
|
+
// abstract
|
351
462
|
init: function (opts) {
|
352
463
|
var results, search, resultsSelector = ".select2-results";
|
353
464
|
|
@@ -365,10 +476,16 @@
|
|
365
476
|
this.enabled=true;
|
366
477
|
this.container = this.createContainer();
|
367
478
|
|
479
|
+
// cache the body so future lookups are cheap
|
480
|
+
this.body = thunk(function() { return opts.element.closest("body"); });
|
481
|
+
|
368
482
|
if (opts.element.attr("class") !== undefined) {
|
369
483
|
this.container.addClass(opts.element.attr("class"));
|
370
484
|
}
|
371
485
|
|
486
|
+
this.container.css(evaluate(opts.containerCss));
|
487
|
+
this.container.addClass(evaluate(opts.containerCssClass));
|
488
|
+
|
372
489
|
// swap container for the element
|
373
490
|
this.opts.element
|
374
491
|
.data("select2", this)
|
@@ -377,20 +494,27 @@
|
|
377
494
|
this.container.data("select2", this);
|
378
495
|
|
379
496
|
this.dropdown = this.container.find(".select2-drop");
|
497
|
+
this.dropdown.css(evaluate(opts.dropdownCss));
|
498
|
+
this.dropdown.addClass(evaluate(opts.dropdownCssClass));
|
499
|
+
this.dropdown.data("select2", this);
|
500
|
+
|
380
501
|
this.results = results = this.container.find(resultsSelector);
|
381
|
-
this.search = search = this.container.find("input
|
502
|
+
this.search = search = this.container.find("input.select2-input");
|
503
|
+
|
504
|
+
search.attr("tabIndex", this.opts.element.attr("tabIndex"));
|
382
505
|
|
383
506
|
this.resultsPage = 0;
|
384
507
|
this.context = null;
|
385
508
|
|
386
509
|
// initialize the container
|
387
510
|
this.initContainer();
|
511
|
+
this.initContainerWidth();
|
388
512
|
|
389
513
|
installFilteredMouseMove(this.results);
|
390
|
-
this.
|
514
|
+
this.dropdown.delegate(resultsSelector, "mousemove-filtered", this.bind(this.highlightUnderEvent));
|
391
515
|
|
392
516
|
installDebouncedScroll(80, this.results);
|
393
|
-
this.
|
517
|
+
this.dropdown.delegate(resultsSelector, "scroll-debounced", this.bind(this.loadMoreIfNeeded));
|
394
518
|
|
395
519
|
// if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel
|
396
520
|
if ($.fn.mousewheel) {
|
@@ -408,19 +532,24 @@
|
|
408
532
|
|
409
533
|
installKeyUpChangeEvent(search);
|
410
534
|
search.bind("keyup-change", this.bind(this.updateResults));
|
411
|
-
search.bind("focus", function () { search.addClass("select2-focused");});
|
535
|
+
search.bind("focus", function () { search.addClass("select2-focused"); if (search.val() === " ") search.val(""); });
|
412
536
|
search.bind("blur", function () { search.removeClass("select2-focused");});
|
413
537
|
|
414
|
-
this.
|
415
|
-
if ($(e.target).closest(".select2-result:not(.select2-disabled)").length > 0) {
|
538
|
+
this.dropdown.delegate(resultsSelector, "mouseup", this.bind(function (e) {
|
539
|
+
if ($(e.target).closest(".select2-result-selectable:not(.select2-disabled)").length > 0) {
|
416
540
|
this.highlightUnderEvent(e);
|
417
541
|
this.selectHighlighted(e);
|
418
542
|
} else {
|
419
|
-
killEvent(e);
|
420
543
|
this.focusSearch();
|
421
544
|
}
|
545
|
+
killEvent(e);
|
422
546
|
}));
|
423
547
|
|
548
|
+
// trap all mouse events from leaving the dropdown. sometimes there may be a modal that is listening
|
549
|
+
// for mouse events outside of itself so it can close itself. since the dropdown is now outside the select2's
|
550
|
+
// dom it will trigger the popup close, which is not what we want
|
551
|
+
this.dropdown.bind("click mouseup mousedown", function (e) { e.stopPropagation(); });
|
552
|
+
|
424
553
|
if ($.isFunction(this.opts.initSelection)) {
|
425
554
|
// initialize selection based on the current value of the source element
|
426
555
|
this.initSelection();
|
@@ -433,10 +562,12 @@
|
|
433
562
|
if (opts.element.is(":disabled")) this.disable();
|
434
563
|
},
|
435
564
|
|
565
|
+
// abstract
|
436
566
|
destroy: function () {
|
437
567
|
var select2 = this.opts.element.data("select2");
|
438
568
|
if (select2 !== undefined) {
|
439
569
|
select2.container.remove();
|
570
|
+
select2.dropdown.remove();
|
440
571
|
select2.opts.element
|
441
572
|
.removeData("select2")
|
442
573
|
.unbind(".select2")
|
@@ -444,8 +575,9 @@
|
|
444
575
|
}
|
445
576
|
},
|
446
577
|
|
578
|
+
// abstract
|
447
579
|
prepareOpts: function (opts) {
|
448
|
-
var element, select, idKey;
|
580
|
+
var element, select, idKey, ajaxUrl;
|
449
581
|
|
450
582
|
element = opts.element;
|
451
583
|
|
@@ -453,6 +585,9 @@
|
|
453
585
|
this.select = select = opts.element;
|
454
586
|
}
|
455
587
|
|
588
|
+
//Custom tags separator.
|
589
|
+
opts.separator = opts.separator || ",";
|
590
|
+
|
456
591
|
if (select) {
|
457
592
|
// these options are not allowed when attached to a select because they are picked up off the element itself
|
458
593
|
$.each(["id", "multiple", "ajax", "query", "createSearchChoice", "initSelection", "data", "tags"], function () {
|
@@ -463,17 +598,50 @@
|
|
463
598
|
}
|
464
599
|
|
465
600
|
opts = $.extend({}, {
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
601
|
+
populateResults: function(container, results, query) {
|
602
|
+
var populate, data, result, children, id=this.opts.id;
|
603
|
+
|
604
|
+
populate=function(results, container, depth) {
|
605
|
+
|
606
|
+
var i, l, result, selectable, compound, node, label, innerContainer, formatted;
|
607
|
+
for (i = 0, l = results.length; i < l; i = i + 1) {
|
608
|
+
|
609
|
+
result=results[i];
|
610
|
+
selectable=id(result) !== undefined;
|
611
|
+
compound=("children" in result) && result.children.length > 0;
|
612
|
+
|
613
|
+
node=$("<li></li>");
|
614
|
+
node.addClass("select2-results-dept-"+depth);
|
615
|
+
node.addClass("select2-result");
|
616
|
+
node.addClass(selectable ? "select2-result-selectable" : "select2-result-unselectable");
|
617
|
+
if (compound) { node.addClass("select2-result-with-children"); }
|
618
|
+
|
619
|
+
label=$("<div></div>");
|
620
|
+
label.addClass("select2-result-label");
|
621
|
+
|
622
|
+
formatted=opts.formatResult(result, label, query);
|
623
|
+
if (formatted!==undefined) {
|
624
|
+
label.html(escapeMarkup(formatted));
|
625
|
+
}
|
626
|
+
|
627
|
+
node.append(label);
|
628
|
+
|
629
|
+
if (compound) {
|
630
|
+
|
631
|
+
innerContainer=$("<ul></ul>");
|
632
|
+
innerContainer.addClass("select2-result-sub");
|
633
|
+
populate(result.children, innerContainer, depth+1);
|
634
|
+
node.append(innerContainer);
|
635
|
+
}
|
636
|
+
|
637
|
+
node.data("select2-data", result);
|
638
|
+
container.append(node);
|
639
|
+
}
|
640
|
+
};
|
641
|
+
|
642
|
+
populate(results, container, 0);
|
475
643
|
}
|
476
|
-
}, opts);
|
644
|
+
}, $.fn.select2.defaults, opts);
|
477
645
|
|
478
646
|
if (typeof(opts.id) !== "function") {
|
479
647
|
idKey = opts.id;
|
@@ -482,19 +650,37 @@
|
|
482
650
|
|
483
651
|
if (select) {
|
484
652
|
opts.query = this.bind(function (query) {
|
485
|
-
var data = {results: [], more: false},
|
653
|
+
var data = { results: [], more: false },
|
486
654
|
term = query.term,
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
655
|
+
children, firstChild, process;
|
656
|
+
|
657
|
+
process=function(element, collection) {
|
658
|
+
var group;
|
659
|
+
if (element.is("option")) {
|
660
|
+
if (query.matcher(term, element.text(), element)) {
|
661
|
+
collection.push({id:element.attr("value"), text:element.text(), element: element.get()});
|
662
|
+
}
|
663
|
+
} else if (element.is("optgroup")) {
|
664
|
+
group={text:element.attr("label"), children:[], element: element.get()};
|
665
|
+
element.children().each2(function(i, elm) { process(elm, group.children); });
|
666
|
+
if (group.children.length>0) {
|
667
|
+
collection.push(group);
|
668
|
+
}
|
669
|
+
}
|
670
|
+
};
|
491
671
|
|
492
|
-
|
672
|
+
children=element.children();
|
493
673
|
|
494
|
-
|
495
|
-
|
674
|
+
// ignore the placeholder option if there is one
|
675
|
+
if (this.getPlaceholder() !== undefined && children.length > 0) {
|
676
|
+
firstChild = children[0];
|
677
|
+
if ($(firstChild).text() === "") {
|
678
|
+
children=children.not(firstChild);
|
496
679
|
}
|
497
|
-
}
|
680
|
+
}
|
681
|
+
|
682
|
+
children.each2(function(i, elm) { process(elm, data.results); });
|
683
|
+
|
498
684
|
query.callback(data);
|
499
685
|
});
|
500
686
|
// this is needed because inside val() we construct choices from options and there id is hardcoded
|
@@ -502,18 +688,26 @@
|
|
502
688
|
} else {
|
503
689
|
if (!("query" in opts)) {
|
504
690
|
if ("ajax" in opts) {
|
691
|
+
ajaxUrl = opts.element.data("ajax-url");
|
692
|
+
if (ajaxUrl && ajaxUrl.length > 0) {
|
693
|
+
opts.ajax.url = ajaxUrl;
|
694
|
+
}
|
505
695
|
opts.query = ajax(opts.ajax);
|
506
696
|
} else if ("data" in opts) {
|
507
697
|
opts.query = local(opts.data);
|
508
698
|
} else if ("tags" in opts) {
|
509
699
|
opts.query = tags(opts.tags);
|
510
700
|
opts.createSearchChoice = function (term) { return {id: term, text: term}; };
|
511
|
-
opts.initSelection = function (element) {
|
701
|
+
opts.initSelection = function (element, callback) {
|
512
702
|
var data = [];
|
513
|
-
$(splitVal(element.val(),
|
514
|
-
|
703
|
+
$(splitVal(element.val(), opts.separator)).each(function () {
|
704
|
+
var id = this, text = this, tags=opts.tags;
|
705
|
+
if ($.isFunction(tags)) tags=tags();
|
706
|
+
$(tags).each(function() { if (equal(this.id, id)) { text = this.text; return false; } });
|
707
|
+
data.push({id: id, text: text});
|
515
708
|
});
|
516
|
-
|
709
|
+
|
710
|
+
callback(data);
|
517
711
|
};
|
518
712
|
}
|
519
713
|
}
|
@@ -528,6 +722,7 @@
|
|
528
722
|
/**
|
529
723
|
* Monitor the original element for changes and update select2 accordingly
|
530
724
|
*/
|
725
|
+
// abstract
|
531
726
|
monitorSource: function () {
|
532
727
|
this.opts.element.bind("change.select2", this.bind(function (e) {
|
533
728
|
if (this.opts.element.data("select2-change-triggered") !== true) {
|
@@ -539,14 +734,23 @@
|
|
539
734
|
/**
|
540
735
|
* Triggers the change event on the source element
|
541
736
|
*/
|
542
|
-
|
543
|
-
|
737
|
+
// abstract
|
738
|
+
triggerChange: function (details) {
|
739
|
+
|
740
|
+
details = details || {};
|
741
|
+
details= $.extend({}, details, { type: "change", val: this.val() });
|
742
|
+
// prevents recursive triggering
|
544
743
|
this.opts.element.data("select2-change-triggered", true);
|
545
|
-
this.opts.element.trigger(
|
744
|
+
this.opts.element.trigger(details);
|
546
745
|
this.opts.element.data("select2-change-triggered", false);
|
746
|
+
|
747
|
+
// some validation frameworks ignore the change event and listen instead to keyup, click for selects
|
748
|
+
// so here we trigger the click event manually
|
749
|
+
this.opts.element.click();
|
547
750
|
},
|
548
751
|
|
549
752
|
|
753
|
+
// abstract
|
550
754
|
enable: function() {
|
551
755
|
if (this.enabled) return;
|
552
756
|
|
@@ -554,6 +758,7 @@
|
|
554
758
|
this.container.removeClass("select2-container-disabled");
|
555
759
|
},
|
556
760
|
|
761
|
+
// abstract
|
557
762
|
disable: function() {
|
558
763
|
if (!this.enabled) return;
|
559
764
|
|
@@ -563,42 +768,156 @@
|
|
563
768
|
this.container.addClass("select2-container-disabled");
|
564
769
|
},
|
565
770
|
|
771
|
+
// abstract
|
566
772
|
opened: function () {
|
567
773
|
return this.container.hasClass("select2-dropdown-open");
|
568
774
|
},
|
569
775
|
|
776
|
+
// abstract
|
777
|
+
positionDropdown: function() {
|
778
|
+
var offset = this.container.offset(),
|
779
|
+
height = this.container.outerHeight(),
|
780
|
+
width = this.container.outerWidth(),
|
781
|
+
dropHeight = this.dropdown.outerHeight(),
|
782
|
+
viewportBottom = $(window).scrollTop() + document.documentElement.clientHeight,
|
783
|
+
dropTop = offset.top + height,
|
784
|
+
enoughRoomBelow = dropTop + dropHeight <= viewportBottom,
|
785
|
+
enoughRoomAbove = (offset.top - dropHeight) >= this.body().scrollTop(),
|
786
|
+
aboveNow = this.dropdown.hasClass("select2-drop-above"),
|
787
|
+
above,
|
788
|
+
css;
|
789
|
+
|
790
|
+
// console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow);
|
791
|
+
// console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body().scrollTop(), "enough?", enoughRoomAbove);
|
792
|
+
|
793
|
+
// always prefer the current above/below alignment, unless there is not enough room
|
794
|
+
|
795
|
+
if (aboveNow) {
|
796
|
+
above = true;
|
797
|
+
if (!enoughRoomAbove && enoughRoomBelow) above = false;
|
798
|
+
} else {
|
799
|
+
above = false;
|
800
|
+
if (!enoughRoomBelow && enoughRoomAbove) above = true;
|
801
|
+
}
|
802
|
+
|
803
|
+
if (above) {
|
804
|
+
dropTop = offset.top - dropHeight;
|
805
|
+
this.container.addClass("select2-drop-above");
|
806
|
+
this.dropdown.addClass("select2-drop-above");
|
807
|
+
}
|
808
|
+
else {
|
809
|
+
this.container.removeClass("select2-drop-above");
|
810
|
+
this.dropdown.removeClass("select2-drop-above");
|
811
|
+
}
|
812
|
+
|
813
|
+
css = {
|
814
|
+
top:dropTop,
|
815
|
+
left:offset.left,
|
816
|
+
width:width
|
817
|
+
};
|
818
|
+
|
819
|
+
this.dropdown.css(css);
|
820
|
+
},
|
821
|
+
|
822
|
+
// abstract
|
823
|
+
shouldOpen: function() {
|
824
|
+
var event;
|
825
|
+
|
826
|
+
if (this.opened()) return false;
|
827
|
+
|
828
|
+
event = jQuery.Event("open");
|
829
|
+
this.opts.element.trigger(event);
|
830
|
+
return !event.isDefaultPrevented();
|
831
|
+
},
|
832
|
+
|
833
|
+
// abstract
|
834
|
+
clearDropdownAlignmentPreference: function() {
|
835
|
+
// clear the classes used to figure out the preference of where the dropdown should be opened
|
836
|
+
this.container.removeClass("select2-drop-above");
|
837
|
+
this.dropdown.removeClass("select2-drop-above");
|
838
|
+
},
|
839
|
+
|
840
|
+
/**
|
841
|
+
* Opens the dropdown
|
842
|
+
*
|
843
|
+
* @return {Boolean} whether or not dropdown was opened. This method will return false if, for example,
|
844
|
+
* the dropdown is already open, or if the 'open' event listener on the element called preventDefault().
|
845
|
+
*/
|
846
|
+
// abstract
|
570
847
|
open: function () {
|
571
|
-
if (this.opened()) return;
|
572
848
|
|
849
|
+
if (!this.shouldOpen()) return false;
|
850
|
+
|
851
|
+
window.setTimeout(this.bind(this.opening), 1);
|
852
|
+
|
853
|
+
return true;
|
854
|
+
},
|
855
|
+
|
856
|
+
/**
|
857
|
+
* Performs the opening of the dropdown
|
858
|
+
*/
|
859
|
+
// abstract
|
860
|
+
opening: function() {
|
861
|
+
this.clearDropdownAlignmentPreference();
|
862
|
+
|
863
|
+
if (this.search.val() === " ") { this.search.val(""); }
|
864
|
+
|
865
|
+
this.dropdown.addClass("select2-drop-active");
|
573
866
|
this.container.addClass("select2-dropdown-open").addClass("select2-container-active");
|
574
867
|
|
575
868
|
this.updateResults(true);
|
869
|
+
|
870
|
+
if(this.dropdown[0] !== this.body().children().last()[0]) {
|
871
|
+
this.dropdown.detach().appendTo(this.body());
|
872
|
+
}
|
873
|
+
|
576
874
|
this.dropdown.show();
|
577
875
|
this.ensureHighlightVisible();
|
876
|
+
|
877
|
+
this.positionDropdown();
|
878
|
+
|
578
879
|
this.focusSearch();
|
579
880
|
},
|
580
881
|
|
882
|
+
// abstract
|
581
883
|
close: function () {
|
582
884
|
if (!this.opened()) return;
|
583
885
|
|
886
|
+
this.clearDropdownAlignmentPreference();
|
887
|
+
|
584
888
|
this.dropdown.hide();
|
585
|
-
this.container.removeClass("select2-dropdown-open");
|
889
|
+
this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active");
|
586
890
|
this.results.empty();
|
587
891
|
this.clearSearch();
|
892
|
+
|
893
|
+
this.opts.element.trigger(jQuery.Event("close"));
|
588
894
|
},
|
589
895
|
|
896
|
+
// abstract
|
590
897
|
clearSearch: function () {
|
591
898
|
|
592
899
|
},
|
593
900
|
|
901
|
+
// abstract
|
594
902
|
ensureHighlightVisible: function () {
|
595
903
|
var results = this.results, children, index, child, hb, rb, y, more;
|
596
904
|
|
597
|
-
children = results.children(".select2-result");
|
598
905
|
index = this.highlight();
|
599
906
|
|
600
907
|
if (index < 0) return;
|
601
908
|
|
909
|
+
if (index == 0) {
|
910
|
+
|
911
|
+
// if the first element is highlighted scroll all the way to the top,
|
912
|
+
// that way any unselectable headers above it will also be scrolled
|
913
|
+
// into view
|
914
|
+
|
915
|
+
results.scrollTop(0);
|
916
|
+
return;
|
917
|
+
}
|
918
|
+
|
919
|
+
children = results.find(".select2-result-selectable");
|
920
|
+
|
602
921
|
child = $(children[index]);
|
603
922
|
|
604
923
|
hb = child.offset().top + child.outerHeight();
|
@@ -623,84 +942,89 @@
|
|
623
942
|
}
|
624
943
|
},
|
625
944
|
|
945
|
+
// abstract
|
626
946
|
moveHighlight: function (delta) {
|
627
|
-
var choices = this.results.
|
947
|
+
var choices = this.results.find(".select2-result-selectable"),
|
628
948
|
index = this.highlight();
|
629
949
|
|
630
950
|
while (index > -1 && index < choices.length) {
|
631
951
|
index += delta;
|
632
|
-
|
952
|
+
var choice = $(choices[index]);
|
953
|
+
if (choice.hasClass("select2-result-selectable") && !choice.hasClass("select2-disabled")) {
|
633
954
|
this.highlight(index);
|
634
955
|
break;
|
635
956
|
}
|
636
957
|
}
|
637
958
|
},
|
638
959
|
|
960
|
+
// abstract
|
639
961
|
highlight: function (index) {
|
640
|
-
var choices = this.results.
|
962
|
+
var choices = this.results.find(".select2-result-selectable").not(".select2-disabled");
|
641
963
|
|
642
964
|
if (arguments.length === 0) {
|
643
965
|
return indexOf(choices.filter(".select2-highlighted")[0], choices.get());
|
644
966
|
}
|
645
967
|
|
646
|
-
choices.removeClass("select2-highlighted");
|
647
|
-
|
648
968
|
if (index >= choices.length) index = choices.length - 1;
|
649
969
|
if (index < 0) index = 0;
|
650
970
|
|
971
|
+
choices.removeClass("select2-highlighted");
|
972
|
+
|
651
973
|
$(choices[index]).addClass("select2-highlighted");
|
652
974
|
this.ensureHighlightVisible();
|
653
975
|
|
654
|
-
if (this.opened()) this.focusSearch();
|
655
976
|
},
|
656
977
|
|
978
|
+
// abstract
|
979
|
+
countSelectableResults: function() {
|
980
|
+
return this.results.find(".select2-result-selectable").not(".select2-disabled").length;
|
981
|
+
},
|
982
|
+
|
983
|
+
// abstract
|
657
984
|
highlightUnderEvent: function (event) {
|
658
|
-
var el = $(event.target).closest(".select2-result");
|
659
|
-
if (el.length > 0) {
|
660
|
-
|
985
|
+
var el = $(event.target).closest(".select2-result-selectable");
|
986
|
+
if (el.length > 0 && !el.is(".select2-highlighted")) {
|
987
|
+
var choices = this.results.find('.select2-result-selectable');
|
988
|
+
this.highlight(choices.index(el));
|
989
|
+
} else if (el.length == 0) {
|
990
|
+
// if we are over an unselectable item remove al highlights
|
991
|
+
this.results.find(".select2-highlighted").removeClass("select2-highlighted");
|
661
992
|
}
|
662
993
|
},
|
663
994
|
|
995
|
+
// abstract
|
664
996
|
loadMoreIfNeeded: function () {
|
665
997
|
var results = this.results,
|
666
998
|
more = results.find("li.select2-more-results"),
|
667
999
|
below, // pixels the element is below the scroll fold, below==0 is when the element is starting to be visible
|
668
1000
|
offset = -1, // index of first element without data
|
669
|
-
page = this.resultsPage + 1
|
1001
|
+
page = this.resultsPage + 1,
|
1002
|
+
self=this,
|
1003
|
+
term=this.search.val(),
|
1004
|
+
context=this.context;
|
670
1005
|
|
671
1006
|
if (more.length === 0) return;
|
672
|
-
|
673
1007
|
below = more.offset().top - results.offset().top - results.height();
|
674
1008
|
|
675
1009
|
if (below <= 0) {
|
676
1010
|
more.addClass("select2-active");
|
677
1011
|
this.opts.query({
|
678
|
-
term:
|
1012
|
+
term: term,
|
679
1013
|
page: page,
|
680
|
-
context:
|
1014
|
+
context: context,
|
681
1015
|
matcher: this.opts.matcher,
|
682
1016
|
callback: this.bind(function (data) {
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
more.before(parts.join(""));
|
690
|
-
results.find(".select2-result").each(function (i) {
|
691
|
-
var e = $(this);
|
692
|
-
if (e.data("select2-data") !== undefined) {
|
693
|
-
offset = i;
|
694
|
-
} else {
|
695
|
-
e.data("select2-data", data.results[i - offset - 1]);
|
696
|
-
}
|
697
|
-
});
|
698
|
-
if (data.more) {
|
699
|
-
more.removeClass("select2-active");
|
1017
|
+
|
1018
|
+
self.opts.populateResults.call(this, results, data.results, {term: term, page: page, context:context});
|
1019
|
+
|
1020
|
+
if (data.more===true) {
|
1021
|
+
more.detach().appendTo(results.children(":last")).text(self.opts.formatLoadMore(page+1));
|
1022
|
+
window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10);
|
700
1023
|
} else {
|
701
1024
|
more.remove();
|
702
1025
|
}
|
703
|
-
|
1026
|
+
self.positionDropdown();
|
1027
|
+
self.resultsPage = page;
|
704
1028
|
})});
|
705
1029
|
}
|
706
1030
|
},
|
@@ -708,23 +1032,37 @@
|
|
708
1032
|
/**
|
709
1033
|
* @param initial whether or not this is the call to this method right after the dropdown has been opened
|
710
1034
|
*/
|
1035
|
+
// abstract
|
711
1036
|
updateResults: function (initial) {
|
712
|
-
var search = this.search, results = this.results, opts = this.opts, self=this;
|
1037
|
+
var search = this.search, results = this.results, opts = this.opts, data, self=this;
|
713
1038
|
|
714
1039
|
// if the search is currently hidden we do not alter the results
|
715
|
-
if (initial !== true && this.showSearchInput === false) {
|
1040
|
+
if (initial !== true && (this.showSearchInput === false || !this.opened())) {
|
716
1041
|
return;
|
717
1042
|
}
|
718
1043
|
|
719
1044
|
search.addClass("select2-active");
|
720
1045
|
|
721
|
-
function
|
722
|
-
results.html(html);
|
1046
|
+
function postRender() {
|
723
1047
|
results.scrollTop(0);
|
724
1048
|
search.removeClass("select2-active");
|
1049
|
+
self.positionDropdown();
|
725
1050
|
}
|
726
1051
|
|
727
|
-
|
1052
|
+
function render(html) {
|
1053
|
+
results.html(escapeMarkup(html));
|
1054
|
+
postRender();
|
1055
|
+
}
|
1056
|
+
|
1057
|
+
if (opts.maximumSelectionSize >=1) {
|
1058
|
+
data = this.data();
|
1059
|
+
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;
|
1062
|
+
}
|
1063
|
+
}
|
1064
|
+
|
1065
|
+
if (search.val().length < opts.minimumInputLength && checkFormatter(opts.formatInputTooShort, "formatInputTooShort")) {
|
728
1066
|
render("<li class='select2-no-results'>" + opts.formatInputTooShort(search.val(), opts.minimumInputLength) + "</li>");
|
729
1067
|
return;
|
730
1068
|
}
|
@@ -736,8 +1074,7 @@
|
|
736
1074
|
context: null,
|
737
1075
|
matcher: opts.matcher,
|
738
1076
|
callback: this.bind(function (data) {
|
739
|
-
var
|
740
|
-
def; // default choice
|
1077
|
+
var def; // default choice
|
741
1078
|
|
742
1079
|
// save context, if any
|
743
1080
|
this.context = (data.context===undefined) ? null : data.context;
|
@@ -755,63 +1092,70 @@
|
|
755
1092
|
}
|
756
1093
|
}
|
757
1094
|
|
758
|
-
if (data.results.length === 0) {
|
1095
|
+
if (data.results.length === 0 && checkFormatter(opts.formatNoMatches, "formatNoMatches")) {
|
759
1096
|
render("<li class='select2-no-results'>" + opts.formatNoMatches(search.val()) + "</li>");
|
760
1097
|
return;
|
761
1098
|
}
|
762
1099
|
|
763
|
-
|
764
|
-
|
765
|
-
parts.push(opts.formatResult(this));
|
766
|
-
parts.push("</li>");
|
767
|
-
});
|
1100
|
+
results.empty();
|
1101
|
+
self.opts.populateResults.call(this, results, data.results, {term: search.val(), page: this.resultsPage, context:null});
|
768
1102
|
|
769
|
-
if (data.more === true) {
|
770
|
-
|
1103
|
+
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>");
|
1105
|
+
window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10);
|
771
1106
|
}
|
772
1107
|
|
773
|
-
render(parts.join(""));
|
774
|
-
results.children(".select2-result").each(function (i) {
|
775
|
-
var d = data.results[i];
|
776
|
-
$(this).data("select2-data", d);
|
777
|
-
});
|
778
1108
|
this.postprocessResults(data, initial);
|
1109
|
+
|
1110
|
+
postRender();
|
779
1111
|
})});
|
780
1112
|
},
|
781
1113
|
|
1114
|
+
// abstract
|
782
1115
|
cancel: function () {
|
783
1116
|
this.close();
|
784
1117
|
},
|
785
1118
|
|
1119
|
+
// abstract
|
786
1120
|
blur: function () {
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
this.search.blur();
|
795
|
-
}), 10);
|
1121
|
+
this.close();
|
1122
|
+
this.container.removeClass("select2-container-active");
|
1123
|
+
this.dropdown.removeClass("select2-drop-active");
|
1124
|
+
// synonymous to .is(':focus'), which is available in jquery >= 1.6
|
1125
|
+
if (this.search[0] === document.activeElement) { this.search.blur(); }
|
1126
|
+
this.clearSearch();
|
1127
|
+
this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
|
796
1128
|
},
|
797
1129
|
|
1130
|
+
// abstract
|
798
1131
|
focusSearch: function () {
|
799
1132
|
/* we do this in a timeout so that current event processing can complete before this code is executed.
|
800
1133
|
this makes sure the search field is focussed even if the current event would blur it */
|
801
1134
|
window.setTimeout(this.bind(function () {
|
802
1135
|
this.search.focus();
|
1136
|
+
// reset the value so IE places the cursor at the end of the input box
|
1137
|
+
this.search.val(this.search.val());
|
803
1138
|
}), 10);
|
804
1139
|
},
|
805
1140
|
|
1141
|
+
// abstract
|
806
1142
|
selectHighlighted: function () {
|
807
|
-
var
|
1143
|
+
var index=this.highlight(),
|
1144
|
+
highlighted=this.results.find(".select2-highlighted").not(".select2-disabled"),
|
1145
|
+
data = highlighted.closest('.select2-result-selectable').data("select2-data");
|
808
1146
|
if (data) {
|
1147
|
+
highlighted.addClass("select2-disabled");
|
1148
|
+
this.highlight(index);
|
809
1149
|
this.onSelect(data);
|
810
1150
|
}
|
811
1151
|
},
|
812
1152
|
|
1153
|
+
// abstract
|
813
1154
|
getPlaceholder: function () {
|
814
|
-
return this.opts.element.attr("placeholder") ||
|
1155
|
+
return this.opts.element.attr("placeholder") ||
|
1156
|
+
this.opts.element.attr("data-placeholder") || // jquery 1.4 compat
|
1157
|
+
this.opts.element.data("placeholder") ||
|
1158
|
+
this.opts.placeholder;
|
815
1159
|
},
|
816
1160
|
|
817
1161
|
/**
|
@@ -819,178 +1163,296 @@
|
|
819
1163
|
* derived first from option `width` passed to select2, then
|
820
1164
|
* the inline 'style' on the original element, and finally
|
821
1165
|
* falls back to the jQuery calculated element width.
|
822
|
-
*
|
823
|
-
* @returns The width string (with units) for the container.
|
824
1166
|
*/
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
1167
|
+
// abstract
|
1168
|
+
initContainerWidth: function () {
|
1169
|
+
function resolveContainerWidth() {
|
1170
|
+
var style, attrs, matches, i, l;
|
1171
|
+
|
1172
|
+
if (this.opts.width === "off") {
|
1173
|
+
return null;
|
1174
|
+
} else if (this.opts.width === "element"){
|
1175
|
+
return this.opts.element.outerWidth() === 0 ? 'auto' : this.opts.element.outerWidth() + 'px';
|
1176
|
+
} else if (this.opts.width === "copy" || this.opts.width === "resolve") {
|
1177
|
+
// check if there is inline style on the element that contains width
|
1178
|
+
style = this.opts.element.attr('style');
|
1179
|
+
if (style !== undefined) {
|
1180
|
+
attrs = style.split(';');
|
1181
|
+
for (i = 0, l = attrs.length; i < l; i = i + 1) {
|
1182
|
+
matches = attrs[i].replace(/\s/g, '')
|
1183
|
+
.match(/width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/);
|
1184
|
+
if (matches !== null && matches.length >= 1)
|
1185
|
+
return matches[1];
|
1186
|
+
}
|
1187
|
+
}
|
1188
|
+
|
1189
|
+
if (this.opts.width === "resolve") {
|
1190
|
+
// next check if css('width') can resolve a width that is percent based, this is sometimes possible
|
1191
|
+
// when attached to input type=hidden or elements hidden via css
|
1192
|
+
style = this.opts.element.css('width');
|
1193
|
+
if (style.indexOf("%") > 0) return style;
|
1194
|
+
|
1195
|
+
// finally, fallback on the calculated width of the element
|
1196
|
+
return (this.opts.element.outerWidth() === 0 ? 'auto' : this.opts.element.outerWidth() + 'px');
|
1197
|
+
}
|
1198
|
+
|
1199
|
+
return null;
|
1200
|
+
} else if ($.isFunction(this.opts.width)) {
|
1201
|
+
return this.opts.width();
|
1202
|
+
} else {
|
1203
|
+
return this.opts.width;
|
1204
|
+
}
|
1205
|
+
};
|
1206
|
+
|
1207
|
+
var width = resolveContainerWidth.call(this);
|
1208
|
+
if (width !== null) {
|
1209
|
+
this.container.attr("style", "width: "+width);
|
839
1210
|
}
|
840
|
-
return this.opts.element.width() + 'px';
|
841
1211
|
}
|
842
1212
|
});
|
843
1213
|
|
844
1214
|
SingleSelect2 = clazz(AbstractSelect2, {
|
845
1215
|
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
1216
|
+
// single
|
1217
|
+
|
1218
|
+
createContainer: function () {
|
1219
|
+
var container = $("<div></div>", {
|
1220
|
+
"class": "select2-container"
|
850
1221
|
}).html([
|
851
1222
|
" <a href='javascript:void(0)' class='select2-choice'>",
|
852
1223
|
" <span></span><abbr class='select2-search-choice-close' style='display:none;'></abbr>",
|
853
1224
|
" <div><b></b></div>" ,
|
854
1225
|
"</a>",
|
855
|
-
" <div class='select2-drop
|
1226
|
+
" <div class='select2-drop select2-offscreen'>" ,
|
856
1227
|
" <div class='select2-search'>" ,
|
857
|
-
" <input type='text' autocomplete='off'/>" ,
|
1228
|
+
" <input type='text' autocomplete='off' class='select2-input'/>" ,
|
858
1229
|
" </div>" ,
|
859
1230
|
" <ul class='select2-results'>" ,
|
860
1231
|
" </ul>" ,
|
861
1232
|
"</div>"].join(""));
|
1233
|
+
return container;
|
862
1234
|
},
|
863
1235
|
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
this.
|
869
|
-
|
1236
|
+
// single
|
1237
|
+
opening: function () {
|
1238
|
+
this.search.show();
|
1239
|
+
this.parent.opening.apply(this, arguments);
|
1240
|
+
this.dropdown.removeClass("select2-offscreen");
|
870
1241
|
},
|
871
1242
|
|
1243
|
+
// single
|
872
1244
|
close: function () {
|
873
1245
|
if (!this.opened()) return;
|
874
1246
|
this.parent.close.apply(this, arguments);
|
1247
|
+
this.dropdown.removeAttr("style").addClass("select2-offscreen").insertAfter(this.selection).show();
|
875
1248
|
},
|
876
1249
|
|
1250
|
+
// single
|
877
1251
|
focus: function () {
|
878
1252
|
this.close();
|
879
1253
|
this.selection.focus();
|
880
1254
|
},
|
881
1255
|
|
1256
|
+
// single
|
882
1257
|
isFocused: function () {
|
883
|
-
return this.selection.
|
1258
|
+
return this.selection[0] === document.activeElement;
|
884
1259
|
},
|
885
1260
|
|
1261
|
+
// single
|
886
1262
|
cancel: function () {
|
887
1263
|
this.parent.cancel.apply(this, arguments);
|
888
1264
|
this.selection.focus();
|
889
1265
|
},
|
890
1266
|
|
1267
|
+
// single
|
891
1268
|
initContainer: function () {
|
892
1269
|
|
893
|
-
var selection,
|
894
|
-
|
1270
|
+
var selection,
|
1271
|
+
container = this.container,
|
1272
|
+
dropdown = this.dropdown,
|
1273
|
+
clickingInside = false;
|
895
1274
|
|
896
|
-
this.selection = selection = container.find(
|
1275
|
+
this.selection = selection = container.find(".select2-choice");
|
897
1276
|
|
898
1277
|
this.search.bind("keydown", this.bind(function (e) {
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
killEvent(e);
|
904
|
-
return;
|
905
|
-
case KEY.TAB:
|
906
|
-
case KEY.ENTER:
|
907
|
-
this.selectHighlighted();
|
1278
|
+
if (!this.enabled) return;
|
1279
|
+
|
1280
|
+
if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) {
|
1281
|
+
// prevent the page from scrolling
|
908
1282
|
killEvent(e);
|
909
1283
|
return;
|
910
|
-
case KEY.ESC:
|
911
|
-
this.cancel(e);
|
912
|
-
e.preventDefault();
|
913
|
-
return;
|
914
1284
|
}
|
1285
|
+
|
1286
|
+
if (this.opened()) {
|
1287
|
+
switch (e.which) {
|
1288
|
+
case KEY.UP:
|
1289
|
+
case KEY.DOWN:
|
1290
|
+
this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
|
1291
|
+
killEvent(e);
|
1292
|
+
return;
|
1293
|
+
case KEY.TAB:
|
1294
|
+
case KEY.ENTER:
|
1295
|
+
this.selectHighlighted();
|
1296
|
+
killEvent(e);
|
1297
|
+
return;
|
1298
|
+
case KEY.ESC:
|
1299
|
+
this.cancel(e);
|
1300
|
+
killEvent(e);
|
1301
|
+
return;
|
1302
|
+
}
|
1303
|
+
} else {
|
1304
|
+
|
1305
|
+
if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) {
|
1306
|
+
return;
|
1307
|
+
}
|
1308
|
+
|
1309
|
+
this.open();
|
1310
|
+
|
1311
|
+
if (e.which === KEY.ENTER) {
|
1312
|
+
// do not propagate the event otherwise we open, and propagate enter which closes
|
1313
|
+
return;
|
1314
|
+
}
|
1315
|
+
}
|
1316
|
+
}));
|
1317
|
+
|
1318
|
+
this.search.bind("focus", this.bind(function() {
|
1319
|
+
this.selection.attr("tabIndex", "-1");
|
1320
|
+
}));
|
1321
|
+
this.search.bind("blur", this.bind(function() {
|
1322
|
+
if (!this.opened()) this.container.removeClass("select2-container-active");
|
1323
|
+
window.setTimeout(this.bind(function() { this.selection.attr("tabIndex", this.opts.element.attr("tabIndex")); }), 10);
|
915
1324
|
}));
|
916
1325
|
|
917
|
-
|
1326
|
+
selection.bind("mousedown", this.bind(function (e) {
|
918
1327
|
clickingInside = true;
|
919
1328
|
|
920
1329
|
if (this.opened()) {
|
921
1330
|
this.close();
|
922
|
-
selection.focus();
|
1331
|
+
this.selection.focus();
|
923
1332
|
} else if (this.enabled) {
|
924
1333
|
this.open();
|
925
1334
|
}
|
926
|
-
e
|
1335
|
+
killEvent(e);
|
927
1336
|
|
928
1337
|
clickingInside = false;
|
929
1338
|
}));
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
this.
|
935
|
-
|
1339
|
+
|
1340
|
+
dropdown.bind("mousedown", this.bind(function() { this.search.focus(); }));
|
1341
|
+
|
1342
|
+
selection.bind("focus", this.bind(function() {
|
1343
|
+
this.container.addClass("select2-container-active");
|
1344
|
+
// hide the search so the tab key does not focus on it
|
1345
|
+
this.search.attr("tabIndex", "-1");
|
1346
|
+
}));
|
1347
|
+
|
1348
|
+
selection.bind("blur", this.bind(function() {
|
1349
|
+
this.container.removeClass("select2-container-active");
|
1350
|
+
window.setTimeout(this.bind(function() { this.search.attr("tabIndex", this.opts.element.attr("tabIndex")); }), 10);
|
1351
|
+
}));
|
1352
|
+
|
1353
|
+
selection.bind("keydown", this.bind(function(e) {
|
1354
|
+
if (!this.enabled) return;
|
1355
|
+
|
1356
|
+
if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) {
|
936
1357
|
// prevent the page from scrolling
|
937
1358
|
killEvent(e);
|
1359
|
+
return;
|
1360
|
+
}
|
1361
|
+
|
1362
|
+
if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) {
|
1363
|
+
return;
|
938
1364
|
}
|
1365
|
+
|
1366
|
+
this.open();
|
1367
|
+
|
939
1368
|
if (e.which === KEY.ENTER) {
|
940
1369
|
// do not propagate the event otherwise we open, and propagate enter which closes
|
941
1370
|
killEvent(e);
|
1371
|
+
return;
|
942
1372
|
}
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
if (
|
947
|
-
|
1373
|
+
|
1374
|
+
// do not set the search input value for non-alpha-numeric keys
|
1375
|
+
// otherwise pressing down results in a '(' being set in the search field
|
1376
|
+
if (e.which < 48 ) { // '0' == 48
|
1377
|
+
killEvent(e);
|
1378
|
+
return;
|
1379
|
+
}
|
1380
|
+
|
1381
|
+
var keyWritten = String.fromCharCode(e.which).toLowerCase();
|
1382
|
+
|
1383
|
+
if (e.shiftKey) {
|
1384
|
+
keyWritten = keyWritten.toUpperCase();
|
1385
|
+
}
|
1386
|
+
|
1387
|
+
this.search.val(keyWritten);
|
1388
|
+
|
1389
|
+
// prevent event propagation so it doesnt replay on the now focussed search field and result in double key entry
|
1390
|
+
killEvent(e);
|
948
1391
|
}));
|
949
1392
|
|
950
|
-
selection.delegate("abbr", "
|
1393
|
+
selection.delegate("abbr", "mousedown", this.bind(function (e) {
|
951
1394
|
if (!this.enabled) return;
|
952
|
-
this.
|
1395
|
+
this.clear();
|
953
1396
|
killEvent(e);
|
954
1397
|
this.close();
|
955
1398
|
this.triggerChange();
|
1399
|
+
this.selection.focus();
|
956
1400
|
}));
|
957
1401
|
|
958
1402
|
this.setPlaceholder();
|
1403
|
+
|
1404
|
+
this.search.bind("focus", this.bind(function() {
|
1405
|
+
this.container.addClass("select2-container-active");
|
1406
|
+
}));
|
1407
|
+
},
|
1408
|
+
|
1409
|
+
// single
|
1410
|
+
clear: function() {
|
1411
|
+
this.opts.element.val("");
|
1412
|
+
this.selection.find("span").empty();
|
1413
|
+
this.selection.removeData("select2-data");
|
1414
|
+
this.setPlaceholder();
|
959
1415
|
},
|
960
1416
|
|
961
1417
|
/**
|
962
1418
|
* Sets selection based on source element's value
|
963
1419
|
*/
|
1420
|
+
// single
|
964
1421
|
initSelection: function () {
|
965
1422
|
var selected;
|
966
1423
|
if (this.opts.element.val() === "") {
|
967
|
-
this.
|
1424
|
+
this.close();
|
1425
|
+
this.setPlaceholder();
|
968
1426
|
} else {
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
1427
|
+
var self = this;
|
1428
|
+
this.opts.initSelection.call(null, this.opts.element, function(selected){
|
1429
|
+
if (selected !== undefined && selected !== null) {
|
1430
|
+
self.updateSelection(selected);
|
1431
|
+
self.close();
|
1432
|
+
self.setPlaceholder();
|
1433
|
+
}
|
1434
|
+
});
|
973
1435
|
}
|
974
|
-
|
975
|
-
this.close();
|
976
|
-
this.setPlaceholder();
|
977
1436
|
},
|
978
1437
|
|
1438
|
+
// single
|
979
1439
|
prepareOpts: function () {
|
980
1440
|
var opts = this.parent.prepareOpts.apply(this, arguments);
|
981
1441
|
|
982
1442
|
if (opts.element.get(0).tagName.toLowerCase() === "select") {
|
983
|
-
// install
|
984
|
-
opts.initSelection = function (element) {
|
1443
|
+
// install the selection initializer
|
1444
|
+
opts.initSelection = function (element, callback) {
|
985
1445
|
var selected = element.find(":selected");
|
986
1446
|
// a single select box always has a value, no need to null check 'selected'
|
987
|
-
|
1447
|
+
if ($.isFunction(callback))
|
1448
|
+
callback({id: selected.attr("value"), text: selected.text()});
|
988
1449
|
};
|
989
1450
|
}
|
990
1451
|
|
991
1452
|
return opts;
|
992
1453
|
},
|
993
1454
|
|
1455
|
+
// single
|
994
1456
|
setPlaceholder: function () {
|
995
1457
|
var placeholder = this.getPlaceholder();
|
996
1458
|
|
@@ -999,24 +1461,22 @@
|
|
999
1461
|
// check for a first blank option if attached to a select
|
1000
1462
|
if (this.select && this.select.find("option:first").text() !== "") return;
|
1001
1463
|
|
1002
|
-
|
1003
|
-
|
1004
|
-
} else {
|
1005
|
-
this.selection.find("span").html(placeholder);
|
1006
|
-
}
|
1464
|
+
this.selection.find("span").html(escapeMarkup(placeholder));
|
1465
|
+
|
1007
1466
|
this.selection.addClass("select2-default");
|
1008
1467
|
|
1009
1468
|
this.selection.find("abbr").hide();
|
1010
1469
|
}
|
1011
1470
|
},
|
1012
1471
|
|
1472
|
+
// single
|
1013
1473
|
postprocessResults: function (data, initial) {
|
1014
1474
|
var selected = 0, self = this, showSearchInput = true;
|
1015
1475
|
|
1016
1476
|
// find the selected element in the result list
|
1017
1477
|
|
1018
|
-
this.results.find(".select2-result").
|
1019
|
-
if (equal(self.id(
|
1478
|
+
this.results.find(".select2-result-selectable").each2(function (i, elm) {
|
1479
|
+
if (equal(self.id(elm.data("select2-data")), self.opts.element.val())) {
|
1020
1480
|
selected = i;
|
1021
1481
|
return false;
|
1022
1482
|
}
|
@@ -1029,15 +1489,18 @@
|
|
1029
1489
|
// hide the search box if this is the first we got the results and there are a few of them
|
1030
1490
|
|
1031
1491
|
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
|
1032
1494
|
showSearchInput = this.showSearchInput = data.results.length >= this.opts.minimumResultsForSearch;
|
1033
|
-
this.
|
1495
|
+
this.dropdown.find(".select2-search")[showSearchInput ? "removeClass" : "addClass"]("select2-search-hidden");
|
1034
1496
|
|
1035
1497
|
//add "select2-with-searchbox" to the container if search box is shown
|
1036
|
-
this.container[showSearchInput ? "addClass" : "removeClass"]("select2-with-searchbox");
|
1498
|
+
$(this.dropdown, this.container)[showSearchInput ? "addClass" : "removeClass"]("select2-with-searchbox");
|
1037
1499
|
}
|
1038
1500
|
|
1039
1501
|
},
|
1040
1502
|
|
1503
|
+
// single
|
1041
1504
|
onSelect: function (data) {
|
1042
1505
|
var old = this.opts.element.val();
|
1043
1506
|
|
@@ -1049,10 +1512,18 @@
|
|
1049
1512
|
if (!equal(old, this.id(data))) { this.triggerChange(); }
|
1050
1513
|
},
|
1051
1514
|
|
1515
|
+
// single
|
1052
1516
|
updateSelection: function (data) {
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1517
|
+
|
1518
|
+
var container=this.selection.find("span"), formatted;
|
1519
|
+
|
1520
|
+
this.selection.data("select2-data", data);
|
1521
|
+
|
1522
|
+
container.empty();
|
1523
|
+
formatted=this.opts.formatSelection(data, container);
|
1524
|
+
if (formatted !== undefined) {
|
1525
|
+
container.append(escapeMarkup(formatted));
|
1526
|
+
}
|
1056
1527
|
|
1057
1528
|
this.selection.removeClass("select2-default");
|
1058
1529
|
|
@@ -1061,8 +1532,9 @@
|
|
1061
1532
|
}
|
1062
1533
|
},
|
1063
1534
|
|
1535
|
+
// single
|
1064
1536
|
val: function () {
|
1065
|
-
var val, data = null;
|
1537
|
+
var val, data = null, self = this;
|
1066
1538
|
|
1067
1539
|
if (arguments.length === 0) {
|
1068
1540
|
return this.opts.element.val();
|
@@ -1071,70 +1543,99 @@
|
|
1071
1543
|
val = arguments[0];
|
1072
1544
|
|
1073
1545
|
if (this.select) {
|
1074
|
-
// val is an id
|
1075
1546
|
this.select
|
1076
1547
|
.val(val)
|
1077
|
-
.find(":selected").
|
1078
|
-
data = {id:
|
1548
|
+
.find(":selected").each2(function (i, elm) {
|
1549
|
+
data = {id: elm.attr("value"), text: elm.text()};
|
1079
1550
|
return false;
|
1080
1551
|
});
|
1081
1552
|
this.updateSelection(data);
|
1553
|
+
this.setPlaceholder();
|
1082
1554
|
} else {
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1555
|
+
if (this.opts.initSelection === undefined) {
|
1556
|
+
throw new Error("cannot call val() if initSelection() is not defined");
|
1557
|
+
}
|
1558
|
+
// val is an id. !val is true for [undefined,null,'']
|
1559
|
+
if (!val) {
|
1560
|
+
this.clear();
|
1561
|
+
return;
|
1562
|
+
}
|
1563
|
+
this.opts.initSelection(this.opts.element, function(data){
|
1564
|
+
self.opts.element.val(!data ? "" : self.id(data));
|
1565
|
+
self.updateSelection(data);
|
1566
|
+
self.setPlaceholder();
|
1567
|
+
});
|
1086
1568
|
}
|
1087
|
-
this.setPlaceholder();
|
1088
|
-
|
1089
1569
|
},
|
1090
1570
|
|
1571
|
+
// single
|
1091
1572
|
clearSearch: function () {
|
1092
1573
|
this.search.val("");
|
1574
|
+
},
|
1575
|
+
|
1576
|
+
// single
|
1577
|
+
data: function(value) {
|
1578
|
+
var data;
|
1579
|
+
|
1580
|
+
if (arguments.length === 0) {
|
1581
|
+
data = this.selection.data("select2-data");
|
1582
|
+
if (data == undefined) data = null;
|
1583
|
+
return data;
|
1584
|
+
} else {
|
1585
|
+
if (!value || value === "") {
|
1586
|
+
this.clear();
|
1587
|
+
} else {
|
1588
|
+
this.opts.element.val(!value ? "" : this.id(value));
|
1589
|
+
this.updateSelection(value);
|
1590
|
+
}
|
1591
|
+
}
|
1093
1592
|
}
|
1094
1593
|
});
|
1095
1594
|
|
1096
1595
|
MultiSelect2 = clazz(AbstractSelect2, {
|
1097
1596
|
|
1597
|
+
// multi
|
1098
1598
|
createContainer: function () {
|
1099
|
-
|
1100
|
-
"class": "select2-container select2-container-multi"
|
1101
|
-
"style": "width: " + this.getContainerWidth()
|
1599
|
+
var container = $("<div></div>", {
|
1600
|
+
"class": "select2-container select2-container-multi"
|
1102
1601
|
}).html([
|
1103
1602
|
" <ul class='select2-choices'>",
|
1104
1603
|
//"<li class='select2-search-choice'><span>California</span><a href="javascript:void(0)" class="select2-search-choice-close"></a></li>" ,
|
1105
1604
|
" <li class='select2-search-field'>" ,
|
1106
|
-
" <input type='text' autocomplete='off' style='width: 25px;'>" ,
|
1605
|
+
" <input type='text' autocomplete='off' style='width: 25px;' class='select2-input'>" ,
|
1107
1606
|
" </li>" ,
|
1108
1607
|
"</ul>" ,
|
1109
|
-
"<div class='select2-drop' style='display:none;'>" ,
|
1608
|
+
"<div class='select2-drop select2-drop-multi' style='display:none;'>" ,
|
1110
1609
|
" <ul class='select2-results'>" ,
|
1111
1610
|
" </ul>" ,
|
1112
1611
|
"</div>"].join(""));
|
1612
|
+
return container;
|
1113
1613
|
},
|
1114
1614
|
|
1615
|
+
// multi
|
1115
1616
|
prepareOpts: function () {
|
1116
1617
|
var opts = this.parent.prepareOpts.apply(this, arguments);
|
1117
1618
|
|
1118
|
-
opts = $.extend({}, {
|
1119
|
-
closeOnSelect: true
|
1120
|
-
}, opts);
|
1121
|
-
|
1122
1619
|
// TODO validate placeholder is a string if specified
|
1123
1620
|
|
1124
1621
|
if (opts.element.get(0).tagName.toLowerCase() === "select") {
|
1125
1622
|
// install sthe selection initializer
|
1126
|
-
opts.initSelection = function (element) {
|
1623
|
+
opts.initSelection = function (element,callback) {
|
1624
|
+
|
1127
1625
|
var data = [];
|
1128
|
-
element.find(":selected").
|
1129
|
-
data.push({id:
|
1626
|
+
element.find(":selected").each2(function (i, elm) {
|
1627
|
+
data.push({id: elm.attr("value"), text: elm.text()});
|
1130
1628
|
});
|
1131
|
-
|
1629
|
+
|
1630
|
+
if ($.isFunction(callback))
|
1631
|
+
callback(data);
|
1132
1632
|
};
|
1133
1633
|
}
|
1134
1634
|
|
1135
1635
|
return opts;
|
1136
1636
|
},
|
1137
1637
|
|
1638
|
+
// multi
|
1138
1639
|
initContainer: function () {
|
1139
1640
|
|
1140
1641
|
var selector = ".select2-choices", selection;
|
@@ -1179,7 +1680,7 @@
|
|
1179
1680
|
return;
|
1180
1681
|
case KEY.ESC:
|
1181
1682
|
this.cancel(e);
|
1182
|
-
e
|
1683
|
+
killEvent(e);
|
1183
1684
|
return;
|
1184
1685
|
}
|
1185
1686
|
}
|
@@ -1198,8 +1699,13 @@
|
|
1198
1699
|
|
1199
1700
|
this.search.bind("keyup", this.bind(this.resizeSearch));
|
1200
1701
|
|
1201
|
-
this.
|
1702
|
+
this.search.bind("blur", this.bind(function() {
|
1703
|
+
this.container.removeClass("select2-container-active");
|
1704
|
+
}));
|
1705
|
+
|
1706
|
+
this.container.delegate(selector, "mousedown", this.bind(function (e) {
|
1202
1707
|
if (!this.enabled) return;
|
1708
|
+
this.clearPlaceholder();
|
1203
1709
|
this.open();
|
1204
1710
|
this.focusSearch();
|
1205
1711
|
e.preventDefault();
|
@@ -1208,6 +1714,7 @@
|
|
1208
1714
|
this.container.delegate(selector, "focus", this.bind(function () {
|
1209
1715
|
if (!this.enabled) return;
|
1210
1716
|
this.container.addClass("select2-container-active");
|
1717
|
+
this.dropdown.addClass("select2-drop-active");
|
1211
1718
|
this.clearPlaceholder();
|
1212
1719
|
}));
|
1213
1720
|
|
@@ -1215,82 +1722,98 @@
|
|
1215
1722
|
this.clearSearch();
|
1216
1723
|
},
|
1217
1724
|
|
1725
|
+
// multi
|
1218
1726
|
enable: function() {
|
1219
1727
|
if (this.enabled) return;
|
1220
1728
|
|
1221
1729
|
this.parent.enable.apply(this, arguments);
|
1222
1730
|
|
1223
|
-
this.search.
|
1731
|
+
this.search.removeAttr("disabled");
|
1224
1732
|
},
|
1225
1733
|
|
1734
|
+
// multi
|
1226
1735
|
disable: function() {
|
1227
1736
|
if (!this.enabled) return;
|
1228
1737
|
|
1229
1738
|
this.parent.disable.apply(this, arguments);
|
1230
1739
|
|
1231
|
-
this.search.
|
1740
|
+
this.search.attr("disabled", true);
|
1232
1741
|
},
|
1233
1742
|
|
1743
|
+
// multi
|
1234
1744
|
initSelection: function () {
|
1235
1745
|
var data;
|
1236
1746
|
if (this.opts.element.val() === "") {
|
1237
1747
|
this.updateSelection([]);
|
1748
|
+
this.close();
|
1749
|
+
// set the placeholder if necessary
|
1750
|
+
this.clearSearch();
|
1238
1751
|
}
|
1239
1752
|
if (this.select || this.opts.element.val() !== "") {
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1753
|
+
var self = this;
|
1754
|
+
this.opts.initSelection.call(null, this.opts.element, function(data){
|
1755
|
+
if (data !== undefined && data !== null) {
|
1756
|
+
self.updateSelection(data);
|
1757
|
+
self.close();
|
1758
|
+
// set the placeholder if necessary
|
1759
|
+
self.clearSearch();
|
1760
|
+
}
|
1761
|
+
});
|
1244
1762
|
}
|
1245
|
-
|
1246
|
-
this.close();
|
1247
|
-
|
1248
|
-
// set the placeholder if necessary
|
1249
|
-
this.clearSearch();
|
1250
1763
|
},
|
1251
1764
|
|
1765
|
+
// multi
|
1252
1766
|
clearSearch: function () {
|
1253
1767
|
var placeholder = this.getPlaceholder();
|
1254
1768
|
|
1255
|
-
if (placeholder !== undefined
|
1256
|
-
&& this.getVal().length === 0
|
1257
|
-
&& this.search.hasClass("select2-focused") === false) {
|
1258
|
-
|
1769
|
+
if (placeholder !== undefined && this.getVal().length === 0 && this.search.hasClass("select2-focused") === false) {
|
1259
1770
|
this.search.val(placeholder).addClass("select2-default");
|
1260
1771
|
// stretch the search box to full width of the container so as much of the placeholder is visible as possible
|
1261
|
-
this.
|
1772
|
+
this.resizeSearch();
|
1262
1773
|
} else {
|
1263
|
-
this
|
1774
|
+
// we set this to " " instead of "" and later clear it on focus() because there is a firefox bug
|
1775
|
+
// that does not properly render the caret when the field starts out blank
|
1776
|
+
this.search.val(" ").width(10);
|
1264
1777
|
}
|
1265
1778
|
},
|
1266
1779
|
|
1780
|
+
// multi
|
1267
1781
|
clearPlaceholder: function () {
|
1268
1782
|
if (this.search.hasClass("select2-default")) {
|
1269
1783
|
this.search.val("").removeClass("select2-default");
|
1784
|
+
} else {
|
1785
|
+
// work around for the space character we set to avoid firefox caret bug
|
1786
|
+
if (this.search.val() === " ") this.search.val("");
|
1270
1787
|
}
|
1271
1788
|
},
|
1272
1789
|
|
1273
|
-
|
1274
|
-
|
1275
|
-
this.parent.
|
1276
|
-
|
1790
|
+
// multi
|
1791
|
+
opening: function () {
|
1792
|
+
this.parent.opening.apply(this, arguments);
|
1793
|
+
|
1794
|
+
this.clearPlaceholder();
|
1795
|
+
this.resizeSearch();
|
1277
1796
|
this.focusSearch();
|
1278
1797
|
},
|
1279
1798
|
|
1799
|
+
// multi
|
1280
1800
|
close: function () {
|
1281
1801
|
if (!this.opened()) return;
|
1282
1802
|
this.parent.close.apply(this, arguments);
|
1283
1803
|
},
|
1284
1804
|
|
1805
|
+
// multi
|
1285
1806
|
focus: function () {
|
1286
1807
|
this.close();
|
1287
1808
|
this.search.focus();
|
1288
1809
|
},
|
1289
1810
|
|
1811
|
+
// multi
|
1290
1812
|
isFocused: function () {
|
1291
1813
|
return this.search.hasClass("select2-focused");
|
1292
1814
|
},
|
1293
1815
|
|
1816
|
+
// multi
|
1294
1817
|
updateSelection: function (data) {
|
1295
1818
|
var ids = [], filtered = [], self = this;
|
1296
1819
|
|
@@ -1310,6 +1833,7 @@
|
|
1310
1833
|
self.postprocessResults();
|
1311
1834
|
},
|
1312
1835
|
|
1836
|
+
// multi
|
1313
1837
|
onSelect: function (data) {
|
1314
1838
|
this.addSelectedChoice(data);
|
1315
1839
|
if (this.select) { this.postprocessResults(); }
|
@@ -1320,45 +1844,56 @@
|
|
1320
1844
|
} else {
|
1321
1845
|
this.search.width(10);
|
1322
1846
|
this.resizeSearch();
|
1847
|
+
|
1848
|
+
if (this.countSelectableResults()>0) {
|
1849
|
+
this.positionDropdown();
|
1850
|
+
} else {
|
1851
|
+
// if nothing left to select close
|
1852
|
+
this.close();
|
1853
|
+
}
|
1323
1854
|
}
|
1324
1855
|
|
1325
1856
|
// since its not possible to select an element that has already been
|
1326
1857
|
// added we do not need to check if this is a new element before firing change
|
1327
|
-
this.triggerChange();
|
1858
|
+
this.triggerChange({ added: data });
|
1328
1859
|
|
1329
1860
|
this.focusSearch();
|
1330
1861
|
},
|
1331
1862
|
|
1863
|
+
// multi
|
1332
1864
|
cancel: function () {
|
1333
1865
|
this.close();
|
1334
1866
|
this.focusSearch();
|
1335
1867
|
},
|
1336
1868
|
|
1869
|
+
// multi
|
1337
1870
|
addSelectedChoice: function (data) {
|
1338
|
-
var choice
|
1871
|
+
var choice=$(
|
1872
|
+
"<li class='select2-search-choice'>" +
|
1873
|
+
" <div></div>" +
|
1874
|
+
" <a href='javascript:void(0)' class='select2-search-choice-close' tabindex='-1'></a>" +
|
1875
|
+
"</li>"),
|
1339
1876
|
id = this.id(data),
|
1340
|
-
|
1341
|
-
|
1342
|
-
|
1343
|
-
parts = ["<li class='select2-search-choice'>",
|
1344
|
-
this.opts.formatSelection(data),
|
1345
|
-
"<a href='javascript:void(0)' class='select2-search-choice-close' tabindex='-1'></a>",
|
1346
|
-
"</li>"
|
1347
|
-
];
|
1877
|
+
val = this.getVal(),
|
1878
|
+
formatted;
|
1348
1879
|
|
1349
|
-
|
1350
|
-
choice.find("
|
1880
|
+
formatted=this.opts.formatSelection(data, choice);
|
1881
|
+
choice.find("div").replaceWith("<div>"+escapeMarkup(formatted)+"</div>");
|
1882
|
+
choice.find(".select2-search-choice-close")
|
1351
1883
|
.bind("click dblclick", this.bind(function (e) {
|
1352
1884
|
if (!this.enabled) return;
|
1353
1885
|
|
1354
|
-
|
1355
|
-
|
1886
|
+
$(e.target).closest(".select2-search-choice").fadeOut('fast').animate({width: "hide"}, 50, this.bind(function(){
|
1887
|
+
this.unselect($(e.target));
|
1888
|
+
this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
|
1889
|
+
this.close();
|
1890
|
+
this.focusSearch();
|
1891
|
+
})).dequeue();
|
1356
1892
|
killEvent(e);
|
1357
|
-
this.close();
|
1358
|
-
this.focusSearch();
|
1359
1893
|
})).bind("focus", this.bind(function () {
|
1360
1894
|
if (!this.enabled) return;
|
1361
1895
|
this.container.addClass("select2-container-active");
|
1896
|
+
this.dropdown.addClass("select2-drop-active");
|
1362
1897
|
}));
|
1363
1898
|
|
1364
1899
|
choice.data("select2-data", data);
|
@@ -1368,8 +1903,10 @@
|
|
1368
1903
|
this.setVal(val);
|
1369
1904
|
},
|
1370
1905
|
|
1906
|
+
// multi
|
1371
1907
|
unselect: function (selected) {
|
1372
1908
|
var val = this.getVal(),
|
1909
|
+
data,
|
1373
1910
|
index;
|
1374
1911
|
|
1375
1912
|
selected = selected.closest(".select2-search-choice");
|
@@ -1378,7 +1915,9 @@
|
|
1378
1915
|
throw "Invalid argument: " + selected + ". Must be .select2-search-choice";
|
1379
1916
|
}
|
1380
1917
|
|
1381
|
-
|
1918
|
+
data = selected.data("select2-data");
|
1919
|
+
|
1920
|
+
index = indexOf(this.id(data), val);
|
1382
1921
|
|
1383
1922
|
if (index >= 0) {
|
1384
1923
|
val.splice(index, 1);
|
@@ -1386,35 +1925,47 @@
|
|
1386
1925
|
if (this.select) this.postprocessResults();
|
1387
1926
|
}
|
1388
1927
|
selected.remove();
|
1389
|
-
this.triggerChange();
|
1928
|
+
this.triggerChange({ removed: data });
|
1390
1929
|
},
|
1391
1930
|
|
1931
|
+
// multi
|
1392
1932
|
postprocessResults: function () {
|
1393
1933
|
var val = this.getVal(),
|
1394
|
-
choices = this.results.find(".select2-result"),
|
1934
|
+
choices = this.results.find(".select2-result-selectable"),
|
1935
|
+
compound = this.results.find(".select2-result-with-children"),
|
1395
1936
|
self = this;
|
1396
1937
|
|
1397
|
-
choices.
|
1398
|
-
var
|
1938
|
+
choices.each2(function (i, choice) {
|
1939
|
+
var id = self.id(choice.data("select2-data"));
|
1399
1940
|
if (indexOf(id, val) >= 0) {
|
1400
|
-
choice.addClass("select2-disabled");
|
1941
|
+
choice.addClass("select2-disabled").removeClass("select2-result-selectable");
|
1401
1942
|
} else {
|
1402
|
-
choice.removeClass("select2-disabled");
|
1943
|
+
choice.removeClass("select2-disabled").addClass("select2-result-selectable");
|
1403
1944
|
}
|
1404
1945
|
});
|
1405
1946
|
|
1406
|
-
|
1407
|
-
if (
|
1408
|
-
|
1947
|
+
compound.each2(function(i, e) {
|
1948
|
+
if (e.find(".select2-result-selectable").length==0) {
|
1949
|
+
e.addClass("select2-disabled");
|
1950
|
+
} else {
|
1951
|
+
e.removeClass("select2-disabled");
|
1952
|
+
}
|
1953
|
+
});
|
1954
|
+
|
1955
|
+
choices.each2(function (i, choice) {
|
1956
|
+
if (!choice.hasClass("select2-disabled") && choice.hasClass("select2-result-selectable")) {
|
1957
|
+
self.highlight(0);
|
1409
1958
|
return false;
|
1410
1959
|
}
|
1411
1960
|
});
|
1412
1961
|
|
1413
1962
|
},
|
1414
1963
|
|
1964
|
+
// multi
|
1415
1965
|
resizeSearch: function () {
|
1416
1966
|
|
1417
|
-
var minimumWidth, left, maxWidth, containerLeft, searchWidth
|
1967
|
+
var minimumWidth, left, maxWidth, containerLeft, searchWidth,
|
1968
|
+
sideBorderPadding = getSideBorderPadding(this.search);
|
1418
1969
|
|
1419
1970
|
minimumWidth = measureTextWidth(this.search) + 10;
|
1420
1971
|
|
@@ -1423,18 +1974,19 @@
|
|
1423
1974
|
maxWidth = this.selection.width();
|
1424
1975
|
containerLeft = this.selection.offset().left;
|
1425
1976
|
|
1426
|
-
searchWidth = maxWidth - (left - containerLeft) -
|
1977
|
+
searchWidth = maxWidth - (left - containerLeft) - sideBorderPadding;
|
1427
1978
|
|
1428
1979
|
if (searchWidth < minimumWidth) {
|
1429
|
-
searchWidth = maxWidth -
|
1980
|
+
searchWidth = maxWidth - sideBorderPadding;
|
1430
1981
|
}
|
1431
1982
|
|
1432
1983
|
if (searchWidth < 40) {
|
1433
|
-
searchWidth = maxWidth -
|
1984
|
+
searchWidth = maxWidth - sideBorderPadding;
|
1434
1985
|
}
|
1435
1986
|
this.search.width(searchWidth);
|
1436
1987
|
},
|
1437
1988
|
|
1989
|
+
// multi
|
1438
1990
|
getVal: function () {
|
1439
1991
|
var val;
|
1440
1992
|
if (this.select) {
|
@@ -1442,10 +1994,11 @@
|
|
1442
1994
|
return val === null ? [] : val;
|
1443
1995
|
} else {
|
1444
1996
|
val = this.opts.element.val();
|
1445
|
-
return splitVal(val,
|
1997
|
+
return splitVal(val, this.opts.separator);
|
1446
1998
|
}
|
1447
1999
|
},
|
1448
2000
|
|
2001
|
+
// multi
|
1449
2002
|
setVal: function (val) {
|
1450
2003
|
var unique;
|
1451
2004
|
if (this.select) {
|
@@ -1456,10 +2009,11 @@
|
|
1456
2009
|
$(val).each(function () {
|
1457
2010
|
if (indexOf(this, unique) < 0) unique.push(this);
|
1458
2011
|
});
|
1459
|
-
this.opts.element.val(unique.length === 0 ? "" : unique.join(
|
2012
|
+
this.opts.element.val(unique.length === 0 ? "" : unique.join(this.opts.separator));
|
1460
2013
|
}
|
1461
2014
|
},
|
1462
2015
|
|
2016
|
+
// multi
|
1463
2017
|
val: function () {
|
1464
2018
|
var val, data = [], self=this;
|
1465
2019
|
|
@@ -1469,24 +2023,37 @@
|
|
1469
2023
|
|
1470
2024
|
val = arguments[0];
|
1471
2025
|
|
2026
|
+
if (!val) {
|
2027
|
+
this.opts.element.val("");
|
2028
|
+
this.updateSelection([]);
|
2029
|
+
this.clearSearch();
|
2030
|
+
return;
|
2031
|
+
}
|
2032
|
+
|
2033
|
+
// val is a list of ids
|
2034
|
+
this.setVal(val);
|
2035
|
+
|
1472
2036
|
if (this.select) {
|
1473
|
-
// val is a list of ids
|
1474
|
-
this.setVal(val);
|
1475
2037
|
this.select.find(":selected").each(function () {
|
1476
2038
|
data.push({id: $(this).attr("value"), text: $(this).text()});
|
1477
2039
|
});
|
1478
2040
|
this.updateSelection(data);
|
1479
2041
|
} else {
|
1480
|
-
|
1481
|
-
|
1482
|
-
|
1483
|
-
$(val).each(function () { data.push(self.id(this)); });
|
1484
|
-
this.setVal(data);
|
1485
|
-
this.updateSelection(val);
|
1486
|
-
}
|
2042
|
+
if (this.opts.initSelection === undefined) {
|
2043
|
+
throw new Error("val() cannot be called if initSelection() is not defined")
|
2044
|
+
}
|
1487
2045
|
|
2046
|
+
this.opts.initSelection(this.opts.element, function(data){
|
2047
|
+
var ids=$(data).map(self.id);
|
2048
|
+
self.setVal(ids);
|
2049
|
+
self.updateSelection(data);
|
2050
|
+
self.clearSearch();
|
2051
|
+
});
|
2052
|
+
}
|
1488
2053
|
this.clearSearch();
|
1489
2054
|
},
|
2055
|
+
|
2056
|
+
// multi
|
1490
2057
|
onSortStart: function() {
|
1491
2058
|
if (this.select) {
|
1492
2059
|
throw new Error("Sorting of elements is not supported when attached to <select>. Attach to <input type='hidden'/> instead.");
|
@@ -1497,6 +2064,8 @@
|
|
1497
2064
|
// hide the container
|
1498
2065
|
this.searchContainer.hide();
|
1499
2066
|
},
|
2067
|
+
|
2068
|
+
// multi
|
1500
2069
|
onSortEnd:function() {
|
1501
2070
|
|
1502
2071
|
var val=[], self=this;
|
@@ -1505,7 +2074,7 @@
|
|
1505
2074
|
this.searchContainer.show();
|
1506
2075
|
// make sure the search container is the last item in the list
|
1507
2076
|
this.searchContainer.appendTo(this.searchContainer.parent());
|
1508
|
-
// since we collapsed the width in
|
2077
|
+
// since we collapsed the width in dragStarted, we resize it here
|
1509
2078
|
this.resizeSearch();
|
1510
2079
|
|
1511
2080
|
// update selection
|
@@ -1515,6 +2084,23 @@
|
|
1515
2084
|
});
|
1516
2085
|
this.setVal(val);
|
1517
2086
|
this.triggerChange();
|
2087
|
+
},
|
2088
|
+
|
2089
|
+
// multi
|
2090
|
+
data: function(values) {
|
2091
|
+
var self=this, ids;
|
2092
|
+
if (arguments.length === 0) {
|
2093
|
+
return this.selection
|
2094
|
+
.find(".select2-search-choice")
|
2095
|
+
.map(function() { return $(this).data("select2-data"); })
|
2096
|
+
.get();
|
2097
|
+
} else {
|
2098
|
+
if (!values) { values = []; }
|
2099
|
+
ids = $.map(values, function(e) { return self.opts.id(e)});
|
2100
|
+
this.setVal(ids);
|
2101
|
+
this.updateSelection(values);
|
2102
|
+
this.clearSearch();
|
2103
|
+
}
|
1518
2104
|
}
|
1519
2105
|
});
|
1520
2106
|
|
@@ -1523,7 +2109,7 @@
|
|
1523
2109
|
var args = Array.prototype.slice.call(arguments, 0),
|
1524
2110
|
opts,
|
1525
2111
|
select2,
|
1526
|
-
value, multiple, allowedMethods = ["val", "destroy", "open", "close", "focus", "isFocused", "container", "onSortStart", "onSortEnd", "enable", "disable"];
|
2112
|
+
value, multiple, allowedMethods = ["val", "destroy", "open", "close", "focus", "isFocused", "container", "onSortStart", "onSortEnd", "enable", "disable", "positionDropdown", "data"];
|
1527
2113
|
|
1528
2114
|
this.each(function () {
|
1529
2115
|
if (args.length === 0 || typeof(args[0]) === "object") {
|
@@ -1561,6 +2147,35 @@
|
|
1561
2147
|
return (value === undefined) ? this : value;
|
1562
2148
|
};
|
1563
2149
|
|
2150
|
+
// plugin defaults, accessible to users
|
2151
|
+
$.fn.select2.defaults = {
|
2152
|
+
width: "copy",
|
2153
|
+
closeOnSelect: true,
|
2154
|
+
containerCss: {},
|
2155
|
+
dropdownCss: {},
|
2156
|
+
containerCssClass: "",
|
2157
|
+
dropdownCssClass: "",
|
2158
|
+
formatResult: function(result, container, query) {
|
2159
|
+
var markup=[];
|
2160
|
+
markMatch(result.text, query.term, markup);
|
2161
|
+
return markup.join("");
|
2162
|
+
},
|
2163
|
+
formatSelection: function (data, container) {
|
2164
|
+
return data.text;
|
2165
|
+
},
|
2166
|
+
formatNoMatches: function () { return "No matches found"; },
|
2167
|
+
formatInputTooShort: function (input, min) { return "Please enter " + (min - input.length) + " more characters"; },
|
2168
|
+
formatSelectionTooBig: function (limit) { return "You can only select " + limit + " items"; },
|
2169
|
+
formatLoadMore: function (pageNumber) { return "Loading more results..."; },
|
2170
|
+
minimumResultsForSearch: 0,
|
2171
|
+
minimumInputLength: 0,
|
2172
|
+
maximumSelectionSize: 0,
|
2173
|
+
id: function (e) { return e.id; },
|
2174
|
+
matcher: function(term, text) {
|
2175
|
+
return text.toUpperCase().indexOf(term.toUpperCase()) >= 0;
|
2176
|
+
}
|
2177
|
+
};
|
2178
|
+
|
1564
2179
|
// exports
|
1565
2180
|
window.Select2 = {
|
1566
2181
|
query: {
|
@@ -1568,7 +2183,8 @@
|
|
1568
2183
|
local: local,
|
1569
2184
|
tags: tags
|
1570
2185
|
}, util: {
|
1571
|
-
debounce: debounce
|
2186
|
+
debounce: debounce,
|
2187
|
+
markMatch: markMatch
|
1572
2188
|
}, "class": {
|
1573
2189
|
"abstract": AbstractSelect2,
|
1574
2190
|
"single": SingleSelect2,
|