biola_wcms_components 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1195a6e67a614a384bbeb179bdbb6868d863548f
4
- data.tar.gz: 8c3f41ba9de9df99a666e742bf327fd6b9564cde
3
+ metadata.gz: 25605e62a0cc1cf6df0558fad0fb1fbae5728718
4
+ data.tar.gz: 65541e40acd9e730da2af47110b05b11a8485d8d
5
5
  SHA512:
6
- metadata.gz: 7de9ef311642b4265168c39bd52ba6be052cadaa5c29db69277d66ad781221ea157ff9dcba2435376acdb437035f850a24802b84cc3e5e08f3519f476d374b00
7
- data.tar.gz: 4564840dce404099f92163f11c250103c250bc17a76456462dcdbc4e53f93a3e79da936f836ff3f1c4e7b28740473d9c76cec4da57bbcc16440d318fc81dd0cc
6
+ metadata.gz: d7d07653a8cf6d4de761fb054ec16b850af481eb0d43546a89623bf9d2a2ea4aa840729b3dd45085b3412a54bf5085dac81301f7486ddf373f41202a466b93cc
7
+ data.tar.gz: 09a6a6c5ee3e14ffdc137804cb1c95d2443474ce21a29ab343b439b2c2f022d8362c60c096822f2a12c30a0ad6bd91cea58c5e1980ed01d606cb5f765b8a9803
@@ -7,6 +7,7 @@
7
7
  #= require handlebars
8
8
  #= require typeahead
9
9
  #
10
+ #= require bootstrap-multiselect
10
11
  #= require bootstrap-tagsinput
11
12
  #
12
13
  #= require redactor
@@ -0,0 +1,10 @@
1
+ $(document).ready ->
2
+ $('.bs-multiselect').each ->
3
+ $(this).multiselect
4
+ enableFiltering: true,
5
+ filterBehavior: 'value'
6
+
7
+ # Replace glyphicons with fonticons
8
+ if $('.bs-multiselect').length > 0
9
+ $('i.glyphicon.glyphicon-search').removeClass('glyphicon glyphicon-search').addClass('fa fa-search');
10
+ $('i.glyphicon.glyphicon-remove-circle').removeClass('glyphicon glyphicon-remove-circle').addClass('fa fa-times-circle');
@@ -1,5 +1,6 @@
1
1
  // External files
2
2
  @import "../../../vendor/assets/stylesheets/redactor";
3
+ @import "../../../vendor/assets/stylesheets/bootstrap-multiselect";
3
4
  @import "../../../vendor/assets/stylesheets/bootstrap-tagsinput";
4
5
  @import "../../../vendor/assets/stylesheets/typeahead";
5
6
 
@@ -0,0 +1,13 @@
1
+ ruby:
2
+ raise 'Form parameter is required' if form.blank?
3
+
4
+ attribute ||= nil # attribute name that the editor should be attached to
5
+ collection ||= nil
6
+ value_method ||= nil
7
+ text_method ||= nil
8
+
9
+ options = {}
10
+ html_options = {class: 'form-control bs-multiselect', multiple: "multiple"}
11
+
12
+ div
13
+ = form.collection_select attribute, collection, value_method, text_method, options, html_options
@@ -1,3 +1,3 @@
1
1
  module BiolaWcmsComponents
2
- VERSION = "0.7.0"
2
+ VERSION = "0.8.0"
3
3
  end
@@ -0,0 +1,1401 @@
1
+ /**
2
+ * Bootstrap Multiselect (https://github.com/davidstutz/bootstrap-multiselect)
3
+ *
4
+ * Apache License, Version 2.0:
5
+ * Copyright (c) 2012 - 2015 David Stutz
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
8
+ * use this file except in compliance with the License. You may obtain a
9
+ * copy of the License at http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ * License for the specific language governing permissions and limitations
15
+ * under the License.
16
+ *
17
+ * BSD 3-Clause License:
18
+ * Copyright (c) 2012 - 2015 David Stutz
19
+ * All rights reserved.
20
+ *
21
+ * Redistribution and use in source and binary forms, with or without
22
+ * modification, are permitted provided that the following conditions are met:
23
+ * - Redistributions of source code must retain the above copyright notice,
24
+ * this list of conditions and the following disclaimer.
25
+ * - Redistributions in binary form must reproduce the above copyright notice,
26
+ * this list of conditions and the following disclaimer in the documentation
27
+ * and/or other materials provided with the distribution.
28
+ * - Neither the name of David Stutz nor the names of its contributors may be
29
+ * used to endorse or promote products derived from this software without
30
+ * specific prior written permission.
31
+ *
32
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
33
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
34
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
35
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
36
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
37
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
38
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
39
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
40
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
41
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
42
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43
+ */
44
+ !function ($) {
45
+ "use strict";// jshint ;_;
46
+
47
+ if (typeof ko !== 'undefined' && ko.bindingHandlers && !ko.bindingHandlers.multiselect) {
48
+ ko.bindingHandlers.multiselect = {
49
+ after: ['options', 'value', 'selectedOptions'],
50
+
51
+ init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
52
+ var $element = $(element);
53
+ var config = ko.toJS(valueAccessor());
54
+
55
+ $element.multiselect(config);
56
+
57
+ if (allBindings.has('options')) {
58
+ var options = allBindings.get('options');
59
+ if (ko.isObservable(options)) {
60
+ ko.computed({
61
+ read: function() {
62
+ options();
63
+ setTimeout(function() {
64
+ var ms = $element.data('multiselect');
65
+ if (ms)
66
+ ms.updateOriginalOptions();//Not sure how beneficial this is.
67
+ $element.multiselect('rebuild');
68
+ }, 1);
69
+ },
70
+ disposeWhenNodeIsRemoved: element
71
+ });
72
+ }
73
+ }
74
+
75
+ //value and selectedOptions are two-way, so these will be triggered even by our own actions.
76
+ //It needs some way to tell if they are triggered because of us or because of outside change.
77
+ //It doesn't loop but it's a waste of processing.
78
+ if (allBindings.has('value')) {
79
+ var value = allBindings.get('value');
80
+ if (ko.isObservable(value)) {
81
+ ko.computed({
82
+ read: function() {
83
+ value();
84
+ setTimeout(function() {
85
+ $element.multiselect('refresh');
86
+ }, 1);
87
+ },
88
+ disposeWhenNodeIsRemoved: element
89
+ }).extend({ rateLimit: 100, notifyWhenChangesStop: true });
90
+ }
91
+ }
92
+
93
+ //Switched from arrayChange subscription to general subscription using 'refresh'.
94
+ //Not sure performance is any better using 'select' and 'deselect'.
95
+ if (allBindings.has('selectedOptions')) {
96
+ var selectedOptions = allBindings.get('selectedOptions');
97
+ if (ko.isObservable(selectedOptions)) {
98
+ ko.computed({
99
+ read: function() {
100
+ selectedOptions();
101
+ setTimeout(function() {
102
+ $element.multiselect('refresh');
103
+ }, 1);
104
+ },
105
+ disposeWhenNodeIsRemoved: element
106
+ }).extend({ rateLimit: 100, notifyWhenChangesStop: true });
107
+ }
108
+ }
109
+
110
+ ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
111
+ $element.multiselect('destroy');
112
+ });
113
+ },
114
+
115
+ update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
116
+ var $element = $(element);
117
+ var config = ko.toJS(valueAccessor());
118
+
119
+ $element.multiselect('setOptions', config);
120
+ $element.multiselect('rebuild');
121
+ }
122
+ };
123
+ }
124
+
125
+ function forEach(array, callback) {
126
+ for (var index = 0; index < array.length; ++index) {
127
+ callback(array[index], index);
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Constructor to create a new multiselect using the given select.
133
+ *
134
+ * @param {jQuery} select
135
+ * @param {Object} options
136
+ * @returns {Multiselect}
137
+ */
138
+ function Multiselect(select, options) {
139
+
140
+ this.$select = $(select);
141
+ this.options = this.mergeOptions($.extend({}, options, this.$select.data()));
142
+
143
+ // Initialization.
144
+ // We have to clone to create a new reference.
145
+ this.originalOptions = this.$select.clone()[0].options;
146
+ this.query = '';
147
+ this.searchTimeout = null;
148
+ this.lastToggledInput = null
149
+
150
+ this.options.multiple = this.$select.attr('multiple') === "multiple";
151
+ this.options.onChange = $.proxy(this.options.onChange, this);
152
+ this.options.onDropdownShow = $.proxy(this.options.onDropdownShow, this);
153
+ this.options.onDropdownHide = $.proxy(this.options.onDropdownHide, this);
154
+ this.options.onDropdownShown = $.proxy(this.options.onDropdownShown, this);
155
+ this.options.onDropdownHidden = $.proxy(this.options.onDropdownHidden, this);
156
+
157
+ // Build select all if enabled.
158
+ this.buildContainer();
159
+ this.buildButton();
160
+ this.buildDropdown();
161
+ this.buildSelectAll();
162
+ this.buildDropdownOptions();
163
+ this.buildFilter();
164
+
165
+ this.updateButtonText();
166
+ this.updateSelectAll();
167
+
168
+ if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) {
169
+ this.disable();
170
+ }
171
+
172
+ this.$select.hide().after(this.$container);
173
+ };
174
+
175
+ Multiselect.prototype = {
176
+
177
+ defaults: {
178
+ /**
179
+ * Default text function will either print 'None selected' in case no
180
+ * option is selected or a list of the selected options up to a length
181
+ * of 3 selected options.
182
+ *
183
+ * @param {jQuery} options
184
+ * @param {jQuery} select
185
+ * @returns {String}
186
+ */
187
+ buttonText: function(options, select) {
188
+ if (options.length === 0) {
189
+ return this.nonSelectedText;
190
+ }
191
+ else if (this.allSelectedText && options.length == $('option', $(select)).length) {
192
+ if (this.selectAllNumber) {
193
+ return this.allSelectedText + ' (' + options.length + ')';
194
+ }
195
+ else {
196
+ return this.allSelectedText;
197
+ }
198
+ }
199
+ else if (options.length > this.numberDisplayed) {
200
+ return options.length + ' ' + this.nSelectedText;
201
+ }
202
+ else {
203
+ var selected = '';
204
+ options.each(function() {
205
+ var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text();
206
+
207
+ selected += label + ', ';
208
+ });
209
+
210
+ return selected.substr(0, selected.length - 2);
211
+ }
212
+ },
213
+ /**
214
+ * Updates the title of the button similar to the buttonText function.
215
+ *
216
+ * @param {jQuery} options
217
+ * @param {jQuery} select
218
+ * @returns {@exp;selected@call;substr}
219
+ */
220
+ buttonTitle: function(options, select) {
221
+ if (options.length === 0) {
222
+ return this.nonSelectedText;
223
+ }
224
+ else {
225
+ var selected = '';
226
+ options.each(function () {
227
+ selected += $(this).text() + ', ';
228
+ });
229
+ return selected.substr(0, selected.length - 2);
230
+ }
231
+ },
232
+ /**
233
+ * Create a label.
234
+ *
235
+ * @param {jQuery} element
236
+ * @returns {String}
237
+ */
238
+ optionLabel: function(element){
239
+ return $(element).attr('label') || $(element).text();
240
+ },
241
+ /**
242
+ * Triggered on change of the multiselect.
243
+ *
244
+ * Not triggered when selecting/deselecting options manually.
245
+ *
246
+ * @param {jQuery} option
247
+ * @param {Boolean} checked
248
+ */
249
+ onChange : function(option, checked) {
250
+
251
+ },
252
+ /**
253
+ * Triggered when the dropdown is shown.
254
+ *
255
+ * @param {jQuery} event
256
+ */
257
+ onDropdownShow: function(event) {
258
+
259
+ },
260
+ /**
261
+ * Triggered when the dropdown is hidden.
262
+ *
263
+ * @param {jQuery} event
264
+ */
265
+ onDropdownHide: function(event) {
266
+
267
+ },
268
+ /**
269
+ * Triggered after the dropdown is shown.
270
+ *
271
+ * @param {jQuery} event
272
+ */
273
+ onDropdownShown: function(event) {
274
+
275
+ },
276
+ /**
277
+ * Triggered after the dropdown is hidden.
278
+ *
279
+ * @param {jQuery} event
280
+ */
281
+ onDropdownHidden: function(event) {
282
+
283
+ },
284
+ /**
285
+ * Triggered on select all.
286
+ */
287
+ onSelectAll: function() {
288
+
289
+ },
290
+ enableHTML: false,
291
+ buttonClass: 'btn btn-default',
292
+ inheritClass: false,
293
+ buttonWidth: 'auto',
294
+ buttonContainer: '<div class="btn-group" />',
295
+ dropRight: false,
296
+ selectedClass: 'active',
297
+ // Maximum height of the dropdown menu.
298
+ // If maximum height is exceeded a scrollbar will be displayed.
299
+ maxHeight: false,
300
+ checkboxName: false,
301
+ includeSelectAllOption: false,
302
+ includeSelectAllIfMoreThan: 0,
303
+ selectAllText: ' Select all',
304
+ selectAllValue: 'multiselect-all',
305
+ selectAllName: false,
306
+ selectAllNumber: true,
307
+ enableFiltering: false,
308
+ enableCaseInsensitiveFiltering: false,
309
+ enableClickableOptGroups: false,
310
+ filterPlaceholder: 'Search',
311
+ // possible options: 'text', 'value', 'both'
312
+ filterBehavior: 'text',
313
+ includeFilterClearBtn: true,
314
+ preventInputChangeEvent: false,
315
+ nonSelectedText: 'None selected',
316
+ nSelectedText: 'selected',
317
+ allSelectedText: 'All selected',
318
+ numberDisplayed: 3,
319
+ disableIfEmpty: false,
320
+ templates: {
321
+ button: '<button type="button" class="multiselect dropdown-toggle" data-toggle="dropdown"><span class="multiselect-selected-text"></span> <b class="caret"></b></button>',
322
+ ul: '<ul class="multiselect-container dropdown-menu"></ul>',
323
+ filter: '<li class="multiselect-item filter"><div class="input-group"><span class="input-group-addon"><i class="glyphicon glyphicon-search"></i></span><input class="form-control multiselect-search" type="text"></div></li>',
324
+ filterClearBtn: '<span class="input-group-btn"><button class="btn btn-default multiselect-clear-filter" type="button"><i class="glyphicon glyphicon-remove-circle"></i></button></span>',
325
+ li: '<li><a tabindex="0"><label></label></a></li>',
326
+ divider: '<li class="multiselect-item divider"></li>',
327
+ liGroup: '<li class="multiselect-item multiselect-group"><label></label></li>'
328
+ }
329
+ },
330
+
331
+ constructor: Multiselect,
332
+
333
+ /**
334
+ * Builds the container of the multiselect.
335
+ */
336
+ buildContainer: function() {
337
+ this.$container = $(this.options.buttonContainer);
338
+ this.$container.on('show.bs.dropdown', this.options.onDropdownShow);
339
+ this.$container.on('hide.bs.dropdown', this.options.onDropdownHide);
340
+ this.$container.on('shown.bs.dropdown', this.options.onDropdownShown);
341
+ this.$container.on('hidden.bs.dropdown', this.options.onDropdownHidden);
342
+ },
343
+
344
+ /**
345
+ * Builds the button of the multiselect.
346
+ */
347
+ buildButton: function() {
348
+ this.$button = $(this.options.templates.button).addClass(this.options.buttonClass);
349
+ if (this.$select.attr('class') && this.options.inheritClass) {
350
+ this.$button.addClass(this.$select.attr('class'));
351
+ }
352
+ // Adopt active state.
353
+ if (this.$select.prop('disabled')) {
354
+ this.disable();
355
+ }
356
+ else {
357
+ this.enable();
358
+ }
359
+
360
+ // Manually add button width if set.
361
+ if (this.options.buttonWidth && this.options.buttonWidth !== 'auto') {
362
+ this.$button.css({
363
+ 'width' : this.options.buttonWidth,
364
+ 'overflow' : 'hidden',
365
+ 'text-overflow' : 'ellipsis'
366
+ });
367
+ this.$container.css({
368
+ 'width': this.options.buttonWidth
369
+ });
370
+ }
371
+
372
+ // Keep the tab index from the select.
373
+ var tabindex = this.$select.attr('tabindex');
374
+ if (tabindex) {
375
+ this.$button.attr('tabindex', tabindex);
376
+ }
377
+
378
+ this.$container.prepend(this.$button);
379
+ },
380
+
381
+ /**
382
+ * Builds the ul representing the dropdown menu.
383
+ */
384
+ buildDropdown: function() {
385
+
386
+ // Build ul.
387
+ this.$ul = $(this.options.templates.ul);
388
+
389
+ if (this.options.dropRight) {
390
+ this.$ul.addClass('pull-right');
391
+ }
392
+
393
+ // Set max height of dropdown menu to activate auto scrollbar.
394
+ if (this.options.maxHeight) {
395
+ // TODO: Add a class for this option to move the css declarations.
396
+ this.$ul.css({
397
+ 'max-height': this.options.maxHeight + 'px',
398
+ 'overflow-y': 'auto',
399
+ 'overflow-x': 'hidden'
400
+ });
401
+ }
402
+
403
+ this.$container.append(this.$ul);
404
+ },
405
+
406
+ /**
407
+ * Build the dropdown options and binds all nessecary events.
408
+ *
409
+ * Uses createDivider and createOptionValue to create the necessary options.
410
+ */
411
+ buildDropdownOptions: function() {
412
+
413
+ this.$select.children().each($.proxy(function(index, element) {
414
+
415
+ var $element = $(element);
416
+ // Support optgroups and options without a group simultaneously.
417
+ var tag = $element.prop('tagName')
418
+ .toLowerCase();
419
+
420
+ if ($element.prop('value') === this.options.selectAllValue) {
421
+ return;
422
+ }
423
+
424
+ if (tag === 'optgroup') {
425
+ this.createOptgroup(element);
426
+ }
427
+ else if (tag === 'option') {
428
+
429
+ if ($element.data('role') === 'divider') {
430
+ this.createDivider();
431
+ }
432
+ else {
433
+ this.createOptionValue(element);
434
+ }
435
+
436
+ }
437
+
438
+ // Other illegal tags will be ignored.
439
+ }, this));
440
+
441
+ // Bind the change event on the dropdown elements.
442
+ $('li input', this.$ul).on('change', $.proxy(function(event) {
443
+ var $target = $(event.target);
444
+
445
+ var checked = $target.prop('checked') || false;
446
+ var isSelectAllOption = $target.val() === this.options.selectAllValue;
447
+
448
+ // Apply or unapply the configured selected class.
449
+ if (this.options.selectedClass) {
450
+ if (checked) {
451
+ $target.closest('li')
452
+ .addClass(this.options.selectedClass);
453
+ }
454
+ else {
455
+ $target.closest('li')
456
+ .removeClass(this.options.selectedClass);
457
+ }
458
+ }
459
+
460
+ // Get the corresponding option.
461
+ var value = $target.val();
462
+ var $option = this.getOptionByValue(value);
463
+
464
+ var $optionsNotThis = $('option', this.$select).not($option);
465
+ var $checkboxesNotThis = $('input', this.$container).not($target);
466
+
467
+ if (isSelectAllOption) {
468
+ if (checked) {
469
+ this.selectAll();
470
+ }
471
+ else {
472
+ this.deselectAll();
473
+ }
474
+ }
475
+
476
+ if(!isSelectAllOption){
477
+ if (checked) {
478
+ $option.prop('selected', true);
479
+
480
+ if (this.options.multiple) {
481
+ // Simply select additional option.
482
+ $option.prop('selected', true);
483
+ }
484
+ else {
485
+ // Unselect all other options and corresponding checkboxes.
486
+ if (this.options.selectedClass) {
487
+ $($checkboxesNotThis).closest('li').removeClass(this.options.selectedClass);
488
+ }
489
+
490
+ $($checkboxesNotThis).prop('checked', false);
491
+ $optionsNotThis.prop('selected', false);
492
+
493
+ // It's a single selection, so close.
494
+ this.$button.click();
495
+ }
496
+
497
+ if (this.options.selectedClass === "active") {
498
+ $optionsNotThis.closest("a").css("outline", "");
499
+ }
500
+ }
501
+ else {
502
+ // Unselect option.
503
+ $option.prop('selected', false);
504
+ }
505
+ }
506
+
507
+ this.$select.change();
508
+
509
+ this.updateButtonText();
510
+ this.updateSelectAll();
511
+
512
+ this.options.onChange($option, checked);
513
+
514
+ if(this.options.preventInputChangeEvent) {
515
+ return false;
516
+ }
517
+ }, this));
518
+
519
+ $('li a', this.$ul).on('mousedown', function(e) {
520
+ if (e.shiftKey) {
521
+ // Prevent selecting text by Shift+click
522
+ return false;
523
+ }
524
+ });
525
+
526
+ $('li a', this.$ul).on('touchstart click', $.proxy(function(event) {
527
+ event.stopPropagation();
528
+
529
+ var $target = $(event.target);
530
+
531
+ if (event.shiftKey && this.options.multiple) {
532
+ if($target.is("label")){ // Handles checkbox selection manually (see https://github.com/davidstutz/bootstrap-multiselect/issues/431)
533
+ event.preventDefault();
534
+ $target = $target.find("input");
535
+ $target.prop("checked", !$target.prop("checked"));
536
+ }
537
+ var checked = $target.prop('checked') || false;
538
+
539
+ if (this.lastToggledInput !== null && this.lastToggledInput !== $target) { // Make sure we actually have a range
540
+ var from = $target.closest("li").index();
541
+ var to = this.lastToggledInput.closest("li").index();
542
+
543
+ if (from > to) { // Swap the indices
544
+ var tmp = to;
545
+ to = from;
546
+ from = tmp;
547
+ }
548
+
549
+ // Make sure we grab all elements since slice excludes the last index
550
+ ++to;
551
+
552
+ // Change the checkboxes and underlying options
553
+ var range = this.$ul.find("li").slice(from, to).find("input");
554
+
555
+ range.prop('checked', checked);
556
+
557
+ if (this.options.selectedClass) {
558
+ range.closest('li')
559
+ .toggleClass(this.options.selectedClass, checked);
560
+ }
561
+
562
+ for (var i = 0, j = range.length; i < j; i++) {
563
+ var $checkbox = $(range[i]);
564
+
565
+ var $option = this.getOptionByValue($checkbox.val());
566
+
567
+ $option.prop('selected', checked);
568
+ }
569
+ }
570
+
571
+ // Trigger the select "change" event
572
+ $target.trigger("change");
573
+ }
574
+
575
+ // Remembers last clicked option
576
+ if($target.is("input") && !$target.closest("li").is(".multiselect-item")){
577
+ this.lastToggledInput = $target;
578
+ }
579
+
580
+ $target.blur();
581
+ }, this));
582
+
583
+ // Keyboard support.
584
+ this.$container.off('keydown.multiselect').on('keydown.multiselect', $.proxy(function(event) {
585
+ if ($('input[type="text"]', this.$container).is(':focus')) {
586
+ return;
587
+ }
588
+
589
+ if (event.keyCode === 9 && this.$container.hasClass('open')) {
590
+ this.$button.click();
591
+ }
592
+ else {
593
+ var $items = $(this.$container).find("li:not(.divider):not(.disabled) a").filter(":visible");
594
+
595
+ if (!$items.length) {
596
+ return;
597
+ }
598
+
599
+ var index = $items.index($items.filter(':focus'));
600
+
601
+ // Navigation up.
602
+ if (event.keyCode === 38 && index > 0) {
603
+ index--;
604
+ }
605
+ // Navigate down.
606
+ else if (event.keyCode === 40 && index < $items.length - 1) {
607
+ index++;
608
+ }
609
+ else if (!~index) {
610
+ index = 0;
611
+ }
612
+
613
+ var $current = $items.eq(index);
614
+ $current.focus();
615
+
616
+ if (event.keyCode === 32 || event.keyCode === 13) {
617
+ var $checkbox = $current.find('input');
618
+
619
+ $checkbox.prop("checked", !$checkbox.prop("checked"));
620
+ $checkbox.change();
621
+ }
622
+
623
+ event.stopPropagation();
624
+ event.preventDefault();
625
+ }
626
+ }, this));
627
+
628
+ if(this.options.enableClickableOptGroups && this.options.multiple) {
629
+ $('li.multiselect-group', this.$ul).on('click', $.proxy(function(event) {
630
+ event.stopPropagation();
631
+
632
+ var group = $(event.target).parent();
633
+
634
+ // Search all option in optgroup
635
+ var $options = group.nextUntil('li.multiselect-group');
636
+ var $visibleOptions = $options.filter(":visible:not(.disabled)");
637
+
638
+ // check or uncheck items
639
+ var allChecked = true;
640
+ var optionInputs = $visibleOptions.find('input');
641
+ optionInputs.each(function() {
642
+ allChecked = allChecked && $(this).prop('checked');
643
+ });
644
+
645
+ optionInputs.prop('checked', !allChecked).trigger('change');
646
+ }, this));
647
+ }
648
+ },
649
+
650
+ /**
651
+ * Create an option using the given select option.
652
+ *
653
+ * @param {jQuery} element
654
+ */
655
+ createOptionValue: function(element) {
656
+ var $element = $(element);
657
+ if ($element.is(':selected')) {
658
+ $element.prop('selected', true);
659
+ }
660
+
661
+ // Support the label attribute on options.
662
+ var label = this.options.optionLabel(element);
663
+ var value = $element.val();
664
+ var inputType = this.options.multiple ? "checkbox" : "radio";
665
+
666
+ var $li = $(this.options.templates.li);
667
+ var $label = $('label', $li);
668
+ $label.addClass(inputType);
669
+
670
+ if (this.options.enableHTML) {
671
+ $label.html(" " + label);
672
+ }
673
+ else {
674
+ $label.text(" " + label);
675
+ }
676
+
677
+ var $checkbox = $('<input/>').attr('type', inputType);
678
+
679
+ if (this.options.checkboxName) {
680
+ $checkbox.attr('name', this.options.checkboxName);
681
+ }
682
+ $label.prepend($checkbox);
683
+
684
+ var selected = $element.prop('selected') || false;
685
+ $checkbox.val(value);
686
+
687
+ if (value === this.options.selectAllValue) {
688
+ $li.addClass("multiselect-item multiselect-all");
689
+ $checkbox.parent().parent()
690
+ .addClass('multiselect-all');
691
+ }
692
+
693
+ $label.attr('title', $element.attr('title'));
694
+
695
+ this.$ul.append($li);
696
+
697
+ if ($element.is(':disabled')) {
698
+ $checkbox.attr('disabled', 'disabled')
699
+ .prop('disabled', true)
700
+ .closest('a')
701
+ .attr("tabindex", "-1")
702
+ .closest('li')
703
+ .addClass('disabled');
704
+ }
705
+
706
+ $checkbox.prop('checked', selected);
707
+
708
+ if (selected && this.options.selectedClass) {
709
+ $checkbox.closest('li')
710
+ .addClass(this.options.selectedClass);
711
+ }
712
+ },
713
+
714
+ /**
715
+ * Creates a divider using the given select option.
716
+ *
717
+ * @param {jQuery} element
718
+ */
719
+ createDivider: function(element) {
720
+ var $divider = $(this.options.templates.divider);
721
+ this.$ul.append($divider);
722
+ },
723
+
724
+ /**
725
+ * Creates an optgroup.
726
+ *
727
+ * @param {jQuery} group
728
+ */
729
+ createOptgroup: function(group) {
730
+ var groupName = $(group).prop('label');
731
+
732
+ // Add a header for the group.
733
+ var $li = $(this.options.templates.liGroup);
734
+
735
+ if (this.options.enableHTML) {
736
+ $('label', $li).html(groupName);
737
+ }
738
+ else {
739
+ $('label', $li).text(groupName);
740
+ }
741
+
742
+ if (this.options.enableClickableOptGroups) {
743
+ $li.addClass('multiselect-group-clickable');
744
+ }
745
+
746
+ this.$ul.append($li);
747
+
748
+ if ($(group).is(':disabled')) {
749
+ $li.addClass('disabled');
750
+ }
751
+
752
+ // Add the options of the group.
753
+ $('option', group).each($.proxy(function(index, element) {
754
+ this.createOptionValue(element);
755
+ }, this));
756
+ },
757
+
758
+ /**
759
+ * Build the selct all.
760
+ *
761
+ * Checks if a select all has already been created.
762
+ */
763
+ buildSelectAll: function() {
764
+ if (typeof this.options.selectAllValue === 'number') {
765
+ this.options.selectAllValue = this.options.selectAllValue.toString();
766
+ }
767
+
768
+ var alreadyHasSelectAll = this.hasSelectAll();
769
+
770
+ if (!alreadyHasSelectAll && this.options.includeSelectAllOption && this.options.multiple
771
+ && $('option', this.$select).length > this.options.includeSelectAllIfMoreThan) {
772
+
773
+ // Check whether to add a divider after the select all.
774
+ if (this.options.includeSelectAllDivider) {
775
+ this.$ul.prepend($(this.options.templates.divider));
776
+ }
777
+
778
+ var $li = $(this.options.templates.li);
779
+ $('label', $li).addClass("checkbox");
780
+
781
+ if (this.options.enableHTML) {
782
+ $('label', $li).html(" " + this.options.selectAllText);
783
+ }
784
+ else {
785
+ $('label', $li).text(" " + this.options.selectAllText);
786
+ }
787
+
788
+ if (this.options.selectAllName) {
789
+ $('label', $li).prepend('<input type="checkbox" name="' + this.options.selectAllName + '" />');
790
+ }
791
+ else {
792
+ $('label', $li).prepend('<input type="checkbox" />');
793
+ }
794
+
795
+ var $checkbox = $('input', $li);
796
+ $checkbox.val(this.options.selectAllValue);
797
+
798
+ $li.addClass("multiselect-item multiselect-all");
799
+ $checkbox.parent().parent()
800
+ .addClass('multiselect-all');
801
+
802
+ this.$ul.prepend($li);
803
+
804
+ $checkbox.prop('checked', false);
805
+ }
806
+ },
807
+
808
+ /**
809
+ * Builds the filter.
810
+ */
811
+ buildFilter: function() {
812
+
813
+ // Build filter if filtering OR case insensitive filtering is enabled and the number of options exceeds (or equals) enableFilterLength.
814
+ if (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering) {
815
+ var enableFilterLength = Math.max(this.options.enableFiltering, this.options.enableCaseInsensitiveFiltering);
816
+
817
+ if (this.$select.find('option').length >= enableFilterLength) {
818
+
819
+ this.$filter = $(this.options.templates.filter);
820
+ $('input', this.$filter).attr('placeholder', this.options.filterPlaceholder);
821
+
822
+ // Adds optional filter clear button
823
+ if(this.options.includeFilterClearBtn){
824
+ var clearBtn = $(this.options.templates.filterClearBtn);
825
+ clearBtn.on('click', $.proxy(function(event){
826
+ clearTimeout(this.searchTimeout);
827
+ this.$filter.find('.multiselect-search').val('');
828
+ $('li', this.$ul).show().removeClass("filter-hidden");
829
+ this.updateSelectAll();
830
+ }, this));
831
+ this.$filter.find('.input-group').append(clearBtn);
832
+ }
833
+
834
+ this.$ul.prepend(this.$filter);
835
+
836
+ this.$filter.val(this.query).on('click', function(event) {
837
+ event.stopPropagation();
838
+ }).on('input keydown', $.proxy(function(event) {
839
+ // Cancel enter key default behaviour
840
+ if (event.which === 13) {
841
+ event.preventDefault();
842
+ }
843
+
844
+ // This is useful to catch "keydown" events after the browser has updated the control.
845
+ clearTimeout(this.searchTimeout);
846
+
847
+ this.searchTimeout = this.asyncFunction($.proxy(function() {
848
+
849
+ if (this.query !== event.target.value) {
850
+ this.query = event.target.value;
851
+
852
+ var currentGroup, currentGroupVisible;
853
+ $.each($('li', this.$ul), $.proxy(function(index, element) {
854
+ var value = $('input', element).length > 0 ? $('input', element).val() : "";
855
+ var text = $('label', element).text();
856
+
857
+ var filterCandidate = '';
858
+ if ((this.options.filterBehavior === 'text')) {
859
+ filterCandidate = text;
860
+ }
861
+ else if ((this.options.filterBehavior === 'value')) {
862
+ filterCandidate = value;
863
+ }
864
+ else if (this.options.filterBehavior === 'both') {
865
+ filterCandidate = text + '\n' + value;
866
+ }
867
+
868
+ if (value !== this.options.selectAllValue && text) {
869
+ // By default lets assume that element is not
870
+ // interesting for this search.
871
+ var showElement = false;
872
+
873
+ if (this.options.enableCaseInsensitiveFiltering && filterCandidate.toLowerCase().indexOf(this.query.toLowerCase()) > -1) {
874
+ showElement = true;
875
+ }
876
+ else if (filterCandidate.indexOf(this.query) > -1) {
877
+ showElement = true;
878
+ }
879
+
880
+ // Toggle current element (group or group item) according to showElement boolean.
881
+ $(element).toggle(showElement).toggleClass('filter-hidden', !showElement);
882
+
883
+ // Differentiate groups and group items.
884
+ if ($(element).hasClass('multiselect-group')) {
885
+ // Remember group status.
886
+ currentGroup = element;
887
+ currentGroupVisible = showElement;
888
+ }
889
+ else {
890
+ // Show group name when at least one of its items is visible.
891
+ if (showElement) {
892
+ $(currentGroup).show().removeClass('filter-hidden');
893
+ }
894
+
895
+ // Show all group items when group name satisfies filter.
896
+ if (!showElement && currentGroupVisible) {
897
+ $(element).show().removeClass('filter-hidden');
898
+ }
899
+ }
900
+ }
901
+ }, this));
902
+ }
903
+
904
+ this.updateSelectAll();
905
+ }, this), 300, this);
906
+ }, this));
907
+ }
908
+ }
909
+ },
910
+
911
+ /**
912
+ * Unbinds the whole plugin.
913
+ */
914
+ destroy: function() {
915
+ this.$container.remove();
916
+ this.$select.show();
917
+ this.$select.data('multiselect', null);
918
+ },
919
+
920
+ /**
921
+ * Refreshs the multiselect based on the selected options of the select.
922
+ */
923
+ refresh: function() {
924
+ $('option', this.$select).each($.proxy(function(index, element) {
925
+ var $input = $('li input', this.$ul).filter(function() {
926
+ return $(this).val() === $(element).val();
927
+ });
928
+
929
+ if ($(element).is(':selected')) {
930
+ $input.prop('checked', true);
931
+
932
+ if (this.options.selectedClass) {
933
+ $input.closest('li')
934
+ .addClass(this.options.selectedClass);
935
+ }
936
+ }
937
+ else {
938
+ $input.prop('checked', false);
939
+
940
+ if (this.options.selectedClass) {
941
+ $input.closest('li')
942
+ .removeClass(this.options.selectedClass);
943
+ }
944
+ }
945
+
946
+ if ($(element).is(":disabled")) {
947
+ $input.attr('disabled', 'disabled')
948
+ .prop('disabled', true)
949
+ .closest('li')
950
+ .addClass('disabled');
951
+ }
952
+ else {
953
+ $input.prop('disabled', false)
954
+ .closest('li')
955
+ .removeClass('disabled');
956
+ }
957
+ }, this));
958
+
959
+ this.updateButtonText();
960
+ this.updateSelectAll();
961
+ },
962
+
963
+ /**
964
+ * Select all options of the given values.
965
+ *
966
+ * If triggerOnChange is set to true, the on change event is triggered if
967
+ * and only if one value is passed.
968
+ *
969
+ * @param {Array} selectValues
970
+ * @param {Boolean} triggerOnChange
971
+ */
972
+ select: function(selectValues, triggerOnChange) {
973
+ if(!$.isArray(selectValues)) {
974
+ selectValues = [selectValues];
975
+ }
976
+
977
+ for (var i = 0; i < selectValues.length; i++) {
978
+ var value = selectValues[i];
979
+
980
+ if (value === null || value === undefined) {
981
+ continue;
982
+ }
983
+
984
+ var $option = this.getOptionByValue(value);
985
+ var $checkbox = this.getInputByValue(value);
986
+
987
+ if($option === undefined || $checkbox === undefined) {
988
+ continue;
989
+ }
990
+
991
+ if (!this.options.multiple) {
992
+ this.deselectAll(false);
993
+ }
994
+
995
+ if (this.options.selectedClass) {
996
+ $checkbox.closest('li')
997
+ .addClass(this.options.selectedClass);
998
+ }
999
+
1000
+ $checkbox.prop('checked', true);
1001
+ $option.prop('selected', true);
1002
+ }
1003
+
1004
+ this.updateButtonText();
1005
+ this.updateSelectAll();
1006
+
1007
+ if (triggerOnChange && selectValues.length === 1) {
1008
+ this.options.onChange($option, true);
1009
+ }
1010
+ },
1011
+
1012
+ /**
1013
+ * Clears all selected items.
1014
+ */
1015
+ clearSelection: function () {
1016
+ this.deselectAll(false);
1017
+ this.updateButtonText();
1018
+ this.updateSelectAll();
1019
+ },
1020
+
1021
+ /**
1022
+ * Deselects all options of the given values.
1023
+ *
1024
+ * If triggerOnChange is set to true, the on change event is triggered, if
1025
+ * and only if one value is passed.
1026
+ *
1027
+ * @param {Array} deselectValues
1028
+ * @param {Boolean} triggerOnChange
1029
+ */
1030
+ deselect: function(deselectValues, triggerOnChange) {
1031
+ if(!$.isArray(deselectValues)) {
1032
+ deselectValues = [deselectValues];
1033
+ }
1034
+
1035
+ for (var i = 0; i < deselectValues.length; i++) {
1036
+ var value = deselectValues[i];
1037
+
1038
+ if (value === null || value === undefined) {
1039
+ continue;
1040
+ }
1041
+
1042
+ var $option = this.getOptionByValue(value);
1043
+ var $checkbox = this.getInputByValue(value);
1044
+
1045
+ if($option === undefined || $checkbox === undefined) {
1046
+ continue;
1047
+ }
1048
+
1049
+ if (this.options.selectedClass) {
1050
+ $checkbox.closest('li')
1051
+ .removeClass(this.options.selectedClass);
1052
+ }
1053
+
1054
+ $checkbox.prop('checked', false);
1055
+ $option.prop('selected', false);
1056
+ }
1057
+
1058
+ this.updateButtonText();
1059
+ this.updateSelectAll();
1060
+
1061
+ if (triggerOnChange && deselectValues.length === 1) {
1062
+ this.options.onChange($option, false);
1063
+ }
1064
+ },
1065
+
1066
+ /**
1067
+ * Selects all enabled & visible options.
1068
+ *
1069
+ * If justVisible is true or not specified, only visible options are selected.
1070
+ *
1071
+ * @param {Boolean} justVisible
1072
+ * @param {Boolean} triggerOnSelectAll
1073
+ */
1074
+ selectAll: function (justVisible, triggerOnSelectAll) {
1075
+ var justVisible = typeof justVisible === 'undefined' ? true : justVisible;
1076
+ var allCheckboxes = $("li input[type='checkbox']:enabled", this.$ul);
1077
+ var visibleCheckboxes = allCheckboxes.filter(":visible");
1078
+ var allCheckboxesCount = allCheckboxes.length;
1079
+ var visibleCheckboxesCount = visibleCheckboxes.length;
1080
+
1081
+ if(justVisible) {
1082
+ visibleCheckboxes.prop('checked', true);
1083
+ $("li:not(.divider):not(.disabled)", this.$ul).filter(":visible").addClass(this.options.selectedClass);
1084
+ }
1085
+ else {
1086
+ allCheckboxes.prop('checked', true);
1087
+ $("li:not(.divider):not(.disabled)", this.$ul).addClass(this.options.selectedClass);
1088
+ }
1089
+
1090
+ if (allCheckboxesCount === visibleCheckboxesCount || justVisible === false) {
1091
+ $("option:enabled", this.$select).prop('selected', true);
1092
+ }
1093
+ else {
1094
+ var values = visibleCheckboxes.map(function() {
1095
+ return $(this).val();
1096
+ }).get();
1097
+
1098
+ $("option:enabled", this.$select).filter(function(index) {
1099
+ return $.inArray($(this).val(), values) !== -1;
1100
+ }).prop('selected', true);
1101
+ }
1102
+
1103
+ if (triggerOnSelectAll) {
1104
+ this.options.onSelectAll();
1105
+ }
1106
+ },
1107
+
1108
+ /**
1109
+ * Deselects all options.
1110
+ *
1111
+ * If justVisible is true or not specified, only visible options are deselected.
1112
+ *
1113
+ * @param {Boolean} justVisible
1114
+ */
1115
+ deselectAll: function (justVisible) {
1116
+ var justVisible = typeof justVisible === 'undefined' ? true : justVisible;
1117
+
1118
+ if(justVisible) {
1119
+ var visibleCheckboxes = $("li input[type='checkbox']:not(:disabled)", this.$ul).filter(":visible");
1120
+ visibleCheckboxes.prop('checked', false);
1121
+
1122
+ var values = visibleCheckboxes.map(function() {
1123
+ return $(this).val();
1124
+ }).get();
1125
+
1126
+ $("option:enabled", this.$select).filter(function(index) {
1127
+ return $.inArray($(this).val(), values) !== -1;
1128
+ }).prop('selected', false);
1129
+
1130
+ if (this.options.selectedClass) {
1131
+ $("li:not(.divider):not(.disabled)", this.$ul).filter(":visible").removeClass(this.options.selectedClass);
1132
+ }
1133
+ }
1134
+ else {
1135
+ $("li input[type='checkbox']:enabled", this.$ul).prop('checked', false);
1136
+ $("option:enabled", this.$select).prop('selected', false);
1137
+
1138
+ if (this.options.selectedClass) {
1139
+ $("li:not(.divider):not(.disabled)", this.$ul).removeClass(this.options.selectedClass);
1140
+ }
1141
+ }
1142
+ },
1143
+
1144
+ /**
1145
+ * Rebuild the plugin.
1146
+ *
1147
+ * Rebuilds the dropdown, the filter and the select all option.
1148
+ */
1149
+ rebuild: function() {
1150
+ this.$ul.html('');
1151
+
1152
+ // Important to distinguish between radios and checkboxes.
1153
+ this.options.multiple = this.$select.attr('multiple') === "multiple";
1154
+
1155
+ this.buildSelectAll();
1156
+ this.buildDropdownOptions();
1157
+ this.buildFilter();
1158
+
1159
+ this.updateButtonText();
1160
+ this.updateSelectAll();
1161
+
1162
+ if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) {
1163
+ this.disable();
1164
+ }
1165
+ else {
1166
+ this.enable();
1167
+ }
1168
+
1169
+ if (this.options.dropRight) {
1170
+ this.$ul.addClass('pull-right');
1171
+ }
1172
+ },
1173
+
1174
+ /**
1175
+ * The provided data will be used to build the dropdown.
1176
+ */
1177
+ dataprovider: function(dataprovider) {
1178
+
1179
+ var groupCounter = 0;
1180
+ var $select = this.$select.empty();
1181
+
1182
+ $.each(dataprovider, function (index, option) {
1183
+ var $tag;
1184
+
1185
+ if ($.isArray(option.children)) { // create optiongroup tag
1186
+ groupCounter++;
1187
+
1188
+ $tag = $('<optgroup/>').attr({
1189
+ label: option.label || 'Group ' + groupCounter,
1190
+ disabled: !!option.disabled
1191
+ });
1192
+
1193
+ forEach(option.children, function(subOption) { // add children option tags
1194
+ $tag.append($('<option/>').attr({
1195
+ value: subOption.value,
1196
+ label: subOption.label || subOption.value,
1197
+ title: subOption.title,
1198
+ selected: !!subOption.selected,
1199
+ disabled: !!subOption.disabled
1200
+ }));
1201
+ });
1202
+ }
1203
+ else {
1204
+ $tag = $('<option/>').attr({
1205
+ value: option.value,
1206
+ label: option.label || option.value,
1207
+ title: option.title,
1208
+ selected: !!option.selected,
1209
+ disabled: !!option.disabled
1210
+ });
1211
+ }
1212
+
1213
+ $select.append($tag);
1214
+ });
1215
+
1216
+ this.rebuild();
1217
+ },
1218
+
1219
+ /**
1220
+ * Enable the multiselect.
1221
+ */
1222
+ enable: function() {
1223
+ this.$select.prop('disabled', false);
1224
+ this.$button.prop('disabled', false)
1225
+ .removeClass('disabled');
1226
+ },
1227
+
1228
+ /**
1229
+ * Disable the multiselect.
1230
+ */
1231
+ disable: function() {
1232
+ this.$select.prop('disabled', true);
1233
+ this.$button.prop('disabled', true)
1234
+ .addClass('disabled');
1235
+ },
1236
+
1237
+ /**
1238
+ * Set the options.
1239
+ *
1240
+ * @param {Array} options
1241
+ */
1242
+ setOptions: function(options) {
1243
+ this.options = this.mergeOptions(options);
1244
+ },
1245
+
1246
+ /**
1247
+ * Merges the given options with the default options.
1248
+ *
1249
+ * @param {Array} options
1250
+ * @returns {Array}
1251
+ */
1252
+ mergeOptions: function(options) {
1253
+ return $.extend(true, {}, this.defaults, this.options, options);
1254
+ },
1255
+
1256
+ /**
1257
+ * Checks whether a select all checkbox is present.
1258
+ *
1259
+ * @returns {Boolean}
1260
+ */
1261
+ hasSelectAll: function() {
1262
+ return $('li.multiselect-all', this.$ul).length > 0;
1263
+ },
1264
+
1265
+ /**
1266
+ * Updates the select all checkbox based on the currently displayed and selected checkboxes.
1267
+ */
1268
+ updateSelectAll: function() {
1269
+ if (this.hasSelectAll()) {
1270
+ var allBoxes = $("li:not(.multiselect-item):not(.filter-hidden) input:enabled", this.$ul);
1271
+ var allBoxesLength = allBoxes.length;
1272
+ var checkedBoxesLength = allBoxes.filter(":checked").length;
1273
+ var selectAllLi = $("li.multiselect-all", this.$ul);
1274
+ var selectAllInput = selectAllLi.find("input");
1275
+
1276
+ if (checkedBoxesLength > 0 && checkedBoxesLength === allBoxesLength) {
1277
+ selectAllInput.prop("checked", true);
1278
+ selectAllLi.addClass(this.options.selectedClass);
1279
+ this.options.onSelectAll();
1280
+ }
1281
+ else {
1282
+ selectAllInput.prop("checked", false);
1283
+ selectAllLi.removeClass(this.options.selectedClass);
1284
+ }
1285
+ }
1286
+ },
1287
+
1288
+ /**
1289
+ * Update the button text and its title based on the currently selected options.
1290
+ */
1291
+ updateButtonText: function() {
1292
+ var options = this.getSelected();
1293
+
1294
+ // First update the displayed button text.
1295
+ if (this.options.enableHTML) {
1296
+ $('.multiselect .multiselect-selected-text', this.$container).html(this.options.buttonText(options, this.$select));
1297
+ }
1298
+ else {
1299
+ $('.multiselect .multiselect-selected-text', this.$container).text(this.options.buttonText(options, this.$select));
1300
+ }
1301
+
1302
+ // Now update the title attribute of the button.
1303
+ $('.multiselect', this.$container).attr('title', this.options.buttonTitle(options, this.$select));
1304
+ },
1305
+
1306
+ /**
1307
+ * Get all selected options.
1308
+ *
1309
+ * @returns {jQUery}
1310
+ */
1311
+ getSelected: function() {
1312
+ return $('option', this.$select).filter(":selected");
1313
+ },
1314
+
1315
+ /**
1316
+ * Gets a select option by its value.
1317
+ *
1318
+ * @param {String} value
1319
+ * @returns {jQuery}
1320
+ */
1321
+ getOptionByValue: function (value) {
1322
+
1323
+ var options = $('option', this.$select);
1324
+ var valueToCompare = value.toString();
1325
+
1326
+ for (var i = 0; i < options.length; i = i + 1) {
1327
+ var option = options[i];
1328
+ if (option.value === valueToCompare) {
1329
+ return $(option);
1330
+ }
1331
+ }
1332
+ },
1333
+
1334
+ /**
1335
+ * Get the input (radio/checkbox) by its value.
1336
+ *
1337
+ * @param {String} value
1338
+ * @returns {jQuery}
1339
+ */
1340
+ getInputByValue: function (value) {
1341
+
1342
+ var checkboxes = $('li input', this.$ul);
1343
+ var valueToCompare = value.toString();
1344
+
1345
+ for (var i = 0; i < checkboxes.length; i = i + 1) {
1346
+ var checkbox = checkboxes[i];
1347
+ if (checkbox.value === valueToCompare) {
1348
+ return $(checkbox);
1349
+ }
1350
+ }
1351
+ },
1352
+
1353
+ /**
1354
+ * Used for knockout integration.
1355
+ */
1356
+ updateOriginalOptions: function() {
1357
+ this.originalOptions = this.$select.clone()[0].options;
1358
+ },
1359
+
1360
+ asyncFunction: function(callback, timeout, self) {
1361
+ var args = Array.prototype.slice.call(arguments, 3);
1362
+ return setTimeout(function() {
1363
+ callback.apply(self || window, args);
1364
+ }, timeout);
1365
+ },
1366
+
1367
+ setAllSelectedText: function(allSelectedText) {
1368
+ this.options.allSelectedText = allSelectedText;
1369
+ this.updateButtonText();
1370
+ }
1371
+ };
1372
+
1373
+ $.fn.multiselect = function(option, parameter, extraOptions) {
1374
+ return this.each(function() {
1375
+ var data = $(this).data('multiselect');
1376
+ var options = typeof option === 'object' && option;
1377
+
1378
+ // Initialize the multiselect.
1379
+ if (!data) {
1380
+ data = new Multiselect(this, options);
1381
+ $(this).data('multiselect', data);
1382
+ }
1383
+
1384
+ // Call multiselect method.
1385
+ if (typeof option === 'string') {
1386
+ data[option](parameter, extraOptions);
1387
+
1388
+ if (option === 'destroy') {
1389
+ $(this).data('multiselect', false);
1390
+ }
1391
+ }
1392
+ });
1393
+ };
1394
+
1395
+ $.fn.multiselect.Constructor = Multiselect;
1396
+
1397
+ $(function() {
1398
+ $("select[data-role=multiselect]").multiselect();
1399
+ });
1400
+
1401
+ }(window.jQuery);