aa-bulletin-board 2.2.2__py3-none-any.whl → 2.2.4__py3-none-any.whl

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.
@@ -1,969 +1,980 @@
1
- /*global jQuery, define, module, require*/
2
- /*!
3
- * jquery.sumoselect
4
- * http://hemantnegi.github.io/jquery.sumoselect
5
- * 2016-12-12
6
- *
7
- * Copyright 2015 Hemant Negi
8
- * Email : hemant.frnz@gmail.com
9
- * Compressor http://refresh-sf.com/
10
- */
11
-
12
- (function (factory) {
13
- 'use strict';
14
- if (typeof define === 'function' && define.amd) {
15
- define(['jquery'], factory);
16
- } else if (typeof exports !== 'undefined') {
17
- module.exports = factory(require('jquery'));
18
- } else {
19
- factory(jQuery);
20
- }
21
-
22
- })(($) => {
23
-
24
- 'namespace sumo';
25
- $.fn.SumoSelect = function (options) {
26
- // Extra check for IE compatibility
27
- const dispatchEvent = (target, eventName) => {
28
- let event = null;
29
- if (typeof (Event) === 'function') {
30
- event = new Event(eventName, {
31
- bubbles: true
32
- });
33
- } else {
34
- event = document.createEvent('Event');
35
- event.initEvent(eventName, true, true);
36
- }
37
-
38
- target.dispatchEvent(event);
39
- };
40
-
41
- // missing forEach on NodeList for IE11
42
- if (window.NodeList && !NodeList.prototype.forEach) {
43
- NodeList.prototype.forEach = Array.prototype.forEach;
44
- }
45
-
46
- // This is the easiest way to have default options.
47
- const defaultOptions = {
48
- placeholder: 'Select Here', // Dont change it here.
49
- csvDispCount: 3, // display no. of items in multiselect. 0 to display all.
50
- captionFormat: '{0} Selected', // format of caption text. you can set your locale.
51
- captionFormatAllSelected: '{0} all selected!', // format of caption text when all elements are selected. set null to use captionFormat. It will not work if there are disabled elements in select.
52
- floatWidth: 400, // Screen width of device at which the list is rendered in floating popup fashion.
53
- forceCustomRendering: false, // force the custom modal on all devices below floatWidth resolution.
54
- nativeOnDevice: ['Android', 'BlackBerry', 'iPhone', 'iPad', 'iPod', 'Opera Mini', 'IEMobile', 'Silk'], //
55
- outputAsCSV: false, // true to POST data as csv ( false for Html control array ie. default select )
56
- csvSepChar: ',', // separation char in csv mode
57
- okCancelInMulti: false, // display ok cancel buttons in desktop mode multiselect also.
58
- isClickAwayOk: false, // for okCancelInMulti=true. sets whether click outside will trigger Ok or Cancel (default is cancel).
59
- triggerChangeCombined: true, // im multi select mode whether to trigger change event on individual selection or combined selection.
60
- selectAll: false, // to display select all button in multiselect mode.|| also select all will not be available on mobile devices.
61
- selectAllPartialCheck: true, // Display a disabled checkbox in multiselect mode when all the items are not selected.
62
- search: false, // to display input for filtering content. selectAlltext will be input text placeholder
63
- searchText: 'Search...', // placeholder for search input
64
- searchFn(haystack, needle) { // search function
65
- return haystack.toLowerCase().indexOf(needle.toLowerCase()) < 0;
66
- },
67
- noMatch: 'No matches for "{0}"',
68
- prefix: '', // some prefix usually the field name. eg. '<b>Hello</b>'
69
- locale: ['OK', 'Cancel', 'Select All', 'Clear all'], // all text that is used. don't change the index.
70
- up: false, // set true to open upside.
71
- showTitle: true, // set to false to prevent title (tooltip) from appearing
72
- clearAll: false, // im multi select - clear all checked options
73
- closeAfterClearAll: false, // im multi select - close select after clear
74
- max: null, // Maximum number of options selected (when multiple)
75
- // eslint-disable-next-line no-unused-vars
76
- renderLi: (li, _originalOption) => li // Custom <li> item renderer
77
- };
78
-
79
- const ret = this.each(function () {
80
- const selObj = this; // the original select object.
81
- if (this.sumo || !$(this).is('select')) return; //already initialized
82
-
83
- const settings = $.extend({}, defaultOptions, options, $(this).data());
84
-
85
- this.sumo = {
86
- E: $(selObj), //the jquery object of original select element.
87
- is_multi: $(selObj).attr('multiple'), //if its a multiple select
88
- select: '',
89
- caption: '',
90
- placeholder: '',
91
- optDiv: '',
92
- CaptionCont: '',
93
- ul: '',
94
- is_floating: false,
95
- is_opened: false,
96
- //backdrop: '',
97
- mob: false, // if to open device default select
98
- Pstate: [],
99
- lastUnselected: null,
100
- selectedCount: 0,
101
-
102
- createElems() {
103
- const O = this;
104
- const selectedOptions = O.E.find('option:checked');
105
- O.E.wrap('<div class="SumoSelect" tabindex="0" role="button" aria-expanded="false">');
106
- selectedOptions.each((_, selectedOption) => { // Fix for IE resetting index to 0 when -1
107
- selectedOption.selected = true;
108
- });
109
- O.select = O.E.parent();
110
- O.caption = $('<span>');
111
- O.CaptionCont = $(`<p class="CaptionCont SelectBox ${O.E.attr('class')}" ><label><i></i></label></p>`)
112
- .attr('style', O.E.attr('style'))
113
- .prepend(O.caption);
114
- O.select.append(O.CaptionCont);
115
-
116
- // default turn off if no multiselect
117
- if (!O.is_multi) settings.okCancelInMulti = false;
118
-
119
- if (O.E.attr('disabled'))
120
- O.select.addClass('disabled').removeAttr('tabindex');
121
-
122
- //if output as csv and is a multiselect.
123
- if (settings.outputAsCSV && O.is_multi && O.E.attr('name')) {
124
- //create a hidden field to store csv value.
125
- O.select.append($('<input class="HEMANT123" type="hidden" />').attr('name', O.E.attr('name')).val(O.getSelStr()));
126
-
127
- // so it can not post the original select.
128
- O.E.removeAttr('name');
129
- }
130
-
131
- //break for mobile rendring.. if forceCustomRendering is false
132
- if (O.isMobile() && !settings.forceCustomRendering) {
133
- O.setNativeMobile();
134
- return;
135
- }
136
-
137
- // if there is a name attr in select add a class to container div
138
- if (O.E.attr('name')) O.select.addClass(`sumo_${O.E.attr('name').replace(/\[\]/, '')}`);
139
-
140
- //hide original select
141
- O.E.addClass('SumoUnder').attr('tabindex', '-1');
142
-
143
- //## Creating the list...
144
- O.optDiv = $(`<div class="optWrapper ${settings.up ? 'up' : ''}">`);
145
-
146
- //branch for floating list in low res devices.
147
- O.floatingList();
148
-
149
- //Creating the markup for the available options
150
- O.ul = $('<ul class="options">');
151
- O.optDiv.append(O.ul);
152
-
153
- // Select all functionality
154
- if (settings.clearAll && O.is_multi) O.ClearAll();
155
- if (settings.selectAll && O.is_multi && !settings.max) O.SelAll();
156
-
157
- // search functionality
158
- if (settings.search) O.Search();
159
-
160
- O.ul.append(O.prepItems(O.E.children()));
161
-
162
- //if multiple then add the class multiple and add OK / CANCEL button
163
- if (O.is_multi) O.multiSelelect();
164
-
165
- O.select.append(O.optDiv);
166
- O._handleMax();
167
- O.basicEvents();
168
- O.selAllState();
169
- },
170
-
171
- prepItems(opts, d) {
172
- const lis = [], O = this;
173
- $(opts).each((i, opt) => { // parsing options to li
174
- const $opt = $(opt);
175
- lis.push($opt.is('optgroup') ?
176
- $(`<li class="group ${opt.disabled ? 'disabled' : ''}"><label></label><ul></ul></li>`)
177
- .find('label')
178
- .text($opt.attr('label'))
179
- .end()
180
- .find('ul')
181
- .append(O.prepItems($opt.children(), opt.disabled))
182
- .end()
183
- :
184
- O.createLi($opt, d)
185
- );
186
- });
187
- return lis;
188
- },
189
-
190
- //## Creates a LI element from a given option and binds events to it
191
- //## returns the jquery instance of li (not inserted in dom)
192
- createLi(opt, d) {
193
- const O = this;
194
-
195
- if (!opt.attr('value')) opt.attr('value', opt.val());
196
- const li = $(`<li class="opt"><label>${opt.html()}</label></li>`);
197
-
198
- li.data('opt', opt); // store a direct reference to option.
199
- opt.data('li', li); // store a direct reference to list item.
200
- if (O.is_multi) li.prepend('<span><i></i></span>');
201
-
202
- if (opt[0].disabled || d)
203
- li.addClass('disabled');
204
-
205
- O.onOptClick(li);
206
-
207
- if (opt[0].selected) {
208
- li.addClass('selected');
209
- O.selectedCount++;
210
- }
211
-
212
- if (opt.attr('class'))
213
- li.addClass(opt.attr('class'));
214
-
215
- if (opt.attr('title'))
216
- li.attr('title', opt.attr('title'));
217
-
218
- return settings.renderLi(li, opt);
219
- },
220
-
221
- //## Returns the selected items as string in a Multiselect.
222
- getSelStr() {
223
- // get the pre selected items.
224
- const sopt = [];
225
- this.E.find('option:checked').each(function () { sopt.push($(this).val()); });
226
- return sopt.join(settings.csvSepChar);
227
- },
228
-
229
- //## THOSE OK/CANCEL BUTTONS ON MULTIPLE SELECT.
230
- multiSelelect() {
231
- const O = this;
232
- O.optDiv.addClass('multiple');
233
- O.okbtn = $('<p tabindex="0" class="btnOk"></p>').click(() => {
234
- //if combined change event is set.
235
- O._okbtn();
236
- O.hideOpts();
237
- });
238
- [O.okbtn[0].innerText] = settings.locale;
239
-
240
- O.cancelBtn = $('<p tabindex="0" class="btnCancel"></p>').click(() => {
241
- O._cnbtn();
242
- O.hideOpts();
243
- });
244
- [, O.cancelBtn[0].innerText] = settings.locale;
245
-
246
- const btns = O.okbtn.add(O.cancelBtn);
247
- O.optDiv.append($('<div class="MultiControls">').append(btns));
248
-
249
- // handling keyboard navigation on ok cancel buttons.
250
- btns.on('keydown.sumo', function (e) {
251
- const el = $(this);
252
- switch (e.which) {
253
- case 32: // space
254
- case 13: // enter
255
- el.trigger('click');
256
- break;
257
-
258
- case 9: //tab
259
- if (el.hasClass('btnOk')) return;
260
- break;
261
- case 27: // esc
262
- O._cnbtn();
263
- O.hideOpts();
264
- return;
265
- default:
266
- break;
267
- }
268
- e.stopPropagation();
269
- e.preventDefault();
270
- });
271
- },
272
-
273
- _okbtn() {
274
- const O = this;
275
- let cg = 0;
276
- //if combined change event is set.
277
- if (settings.triggerChangeCombined) {
278
- //check for a change in the selection.
279
- if (O.E.find('option:checked').length !== O.Pstate.length) {
280
- cg = 1;
281
- }
282
- else {
283
- O.E.find('option').each((i, e) => {
284
- if (e.selected && O.Pstate.indexOf(i) < 0) cg = 1;
285
- });
286
- }
287
-
288
- if (cg) {
289
- O.callChange();
290
- O.setText();
291
- }
292
- }
293
- },
294
- _cnbtn() {
295
- const O = this;
296
- //remove all selections
297
- O.E.find('option:checked').each(function () { this.selected = false; });
298
- O.optDiv.find('li.selected').removeClass('selected');
299
-
300
- //restore selections from saved state.
301
- for (let i = 0; i < O.Pstate.length; i++) {
302
- O.E.find('option')[O.Pstate[i]].selected = true;
303
- O.ul.find('li.opt').eq(O.Pstate[i]).addClass('selected');
304
- }
305
- O.setText();
306
- O.selAllState();
307
- },
308
-
309
- _handleMax() {
310
- // Disable options if max reached
311
- if (settings.max) {
312
- if (this.selectedCount >= +settings.max) {
313
- this.optDiv.find('li.opt').not('.hidden').each((ix, e) => {
314
- if (!$(e).hasClass('selected')) {
315
- $(e).addClass('temporary-disabled disabled');
316
- }
317
- });
318
- } else {
319
- // Enable options back
320
- this.optDiv.find('li.opt').not('.hidden').each((ix, e) => {
321
- if ($(e).hasClass('temporary-disabled')) {
322
- $(e).removeClass('temporary-disabled disabled');
323
- }
324
- });
325
- }
326
- }
327
- },
328
-
329
- ClearAll () {
330
- const O = this;
331
- if (!O.is_multi) return;
332
- O.selAll = $('<p class="reset-all"><span><i></i></span><label></label></p>');
333
- [, , , O.selAll.find('label')[0].innerText] = settings.locale;
334
- O.optDiv.addClass('resetAll');
335
- O.selAll.on('click', () => {
336
- O.selAll.removeClass('selected');
337
- O.toggSelAll(false, 1);
338
- if (settings.closeAfterClearAll) {
339
- O.hideOpts();
340
- }
341
- });
342
-
343
- O.optDiv.prepend(O.selAll);
344
- },
345
-
346
- SelAll() {
347
- const O = this;
348
- if (!O.is_multi) return;
349
- O.selAll = $('<p class="select-all"><span><i></i></span><label></label></p>');
350
- [, , O.selAll.find('label')[0].innerText] = settings.locale;
351
- O.optDiv.addClass('selall');
352
- O.selAll.on('click', () => {
353
- O.selAll.toggleClass('selected');
354
- O.toggSelAll(O.selAll.hasClass('selected'), 1);
355
- O.selAllState();
356
- });
357
-
358
- O.optDiv.prepend(O.selAll);
359
- },
360
-
361
- // search module (can be removed if not required.)
362
- Search() {
363
- const O = this,
364
- cc = O.CaptionCont.addClass('search'),
365
- P = $('<p class="no-match">'),
366
- fn = (options.searchFn && typeof options.searchFn === 'function') ? options.searchFn : settings.searchFn;
367
-
368
- O.ftxt = $('<input type="text" class="search-txt" value="" autocomplete="off">')
369
- .on('click', (e) => {
370
- e.stopPropagation();
371
- });
372
- O.ftxt[0].placeholder = settings.searchText;
373
- cc.append(O.ftxt);
374
- O.optDiv.children('ul').after(P);
375
-
376
- O.ftxt.on('keyup.sumo', () => {
377
- const hid = O.optDiv.find('ul.options li.opt').each((ix, e) => {
378
- const el = $(e),
379
- { 0: opt } = el.data('opt');
380
- opt.hidden = fn(el.text(), O.ftxt.val(), el);
381
- el.toggleClass('hidden', opt.hidden);
382
- }).not('.hidden');
383
-
384
- // Hide opt-groups with no options matched
385
- O.optDiv[0].querySelectorAll('li.group').forEach(optGroup => {
386
- if (optGroup.querySelector('li:not(.hidden)')) {
387
- optGroup.classList.remove('hidden');
388
- } else {
389
- optGroup.classList.add('hidden');
390
- }
391
- });
392
-
393
- P.html(settings.noMatch.replace(/\{0\}/g, '<em></em>')).toggle(!hid.length);
394
- P.find('em').text(O.ftxt.val());
395
- O.selAllState();
396
- });
397
- },
398
-
399
- selAllState() {
400
- const O = this;
401
- if (settings.selectAll && O.is_multi) {
402
- let sc = 0, vc = 0;
403
- O.optDiv.find('li.opt:not(.disabled):not(.hidden)').each((ix, e) => {
404
- if ($(e).hasClass('selected')) sc++;
405
- vc++;
406
- });
407
- //select all checkbox state change.
408
- if (sc === vc) O.selAll.removeClass('partial').addClass('selected');
409
- else if (sc === 0) O.selAll.removeClass('selected partial');
410
- else {
411
- if(settings.selectAllPartialCheck) {
412
- O.selAll.addClass('partial')
413
- }
414
- O.selAll.removeClass('selected');
415
- }
416
- }
417
- },
418
-
419
- showOpts() {
420
- const O = this;
421
- if (O.E.attr('disabled')) return; // if select is disabled then retrun
422
- O.E.trigger('sumo:opening', O);
423
- O.is_opened = true;
424
- O.select.addClass('open').attr('aria-expanded', 'true');
425
- O.E.trigger('sumo:opened', O);
426
-
427
- if (O.ftxt) O.ftxt.focus();
428
- else O.select.focus();
429
-
430
- // hide options on click outside.
431
- $(document).on('click.sumo', (e) => {
432
- if (!O.select.is(e.target) // if the target of the click isn't the container...
433
- && O.select.has(e.target).length === 0) { // ... nor a descendant of the container
434
- if (!O.is_opened) return;
435
- O.hideOpts();
436
- if (settings.okCancelInMulti) {
437
- if (settings.isClickAwayOk)
438
- O._okbtn();
439
- else
440
- O._cnbtn();
441
- }
442
- }
443
- });
444
-
445
- if (O.is_floating) {
446
- let H = O.optDiv.children('ul').outerHeight() + 2; // +2 is clear fix
447
- if (O.is_multi) H = H + +O.optDiv.css('padding-bottom');
448
- O.optDiv.css('height', H);
449
- $('body').addClass('sumoStopScroll');
450
- }
451
-
452
- O.setPstate();
453
- },
454
-
455
- //maintain state when ok/cancel buttons are available storing the indexes.
456
- setPstate() {
457
- const O = this;
458
- if (O.is_multi && (O.is_floating || settings.okCancelInMulti)) {
459
- O.Pstate = [];
460
- // assuming that find returns elements in tree order
461
- O.E.find('option').each((i, e) => { if (e.selected) O.Pstate.push(i); });
462
- }
463
- },
464
-
465
- callChange() {
466
- this.E.get().forEach(e => {
467
- dispatchEvent(e, 'change');
468
- dispatchEvent(e, 'click');
469
- });
470
- },
471
-
472
- hideOpts() {
473
- const O = this;
474
- if (O.is_opened) {
475
- O.E.trigger('sumo:closing', O);
476
- O.is_opened = false;
477
- O.select.removeClass('open').attr('aria-expanded', 'false').find('ul li.sel').removeClass('sel');
478
- O.E.trigger('sumo:closed', O);
479
- $(document).off('click.sumo');
480
- $('body').removeClass('sumoStopScroll');
481
-
482
- // clear the search
483
- if (settings.search) {
484
- O.ftxt.val('');
485
- O.ftxt.trigger('keyup.sumo');
486
- }
487
- }
488
- },
489
- setOnOpen() {
490
- const O = this;
491
- let li = O.optDiv.find('li.opt:not(.hidden)').eq(settings.search ? 0 : O.E[0].selectedIndex);
492
- if (li.hasClass('disabled')) {
493
- li = li.next(':not(disabled)');
494
- if (!li.length) return;
495
- }
496
- O.optDiv.find('li.sel').removeClass('sel');
497
- li.addClass('sel');
498
- O.showOpts();
499
- },
500
- nav(up) {
501
- const O = this;
502
- let c = null, sel = O.ul.find('li.opt.sel:not(.hidden)');
503
- const
504
- s = O.ul.find('li.opt:not(.disabled):not(.hidden)'),
505
- idx = s.index(sel);
506
- if (O.is_opened && sel.length) {
507
- if (up && idx > 0)
508
- c = s.eq(idx - 1);
509
- else if (!up && idx < s.length - 1 && idx > -1)
510
- c = s.eq(idx + 1);
511
- else return; // if no items before or after
512
-
513
- sel.removeClass('sel');
514
- sel = c.addClass('sel');
515
-
516
- // setting sel item to visible view.
517
- const { ul } = O,
518
- st = ul.scrollTop(),
519
- t = sel.position().top + st;
520
- if (t >= st + ul.height() - sel.outerHeight())
521
- ul.scrollTop(t - ul.height() + sel.outerHeight());
522
- if (t < st)
523
- ul.scrollTop(t);
524
-
525
- }
526
- else
527
- O.setOnOpen();
528
- },
529
-
530
- basicEvents() {
531
- const O = this;
532
- O.CaptionCont.click((evt) => {
533
- O.E.trigger('click');
534
- if (O.is_opened) O.hideOpts(); else O.showOpts();
535
- evt.stopPropagation();
536
- });
537
-
538
- O.select.on('keydown.sumo', (e) => {
539
- switch (e.which) {
540
- case 38: // up
541
- O.nav(true);
542
- break;
543
-
544
- case 40: // down
545
- O.nav(false);
546
- break;
547
-
548
- case 65: // shortcut ctrl + a to select all and ctrl + shift + a to unselect all.
549
- if (O.is_multi && !settings.max && e.ctrlKey) {
550
- O.toggSelAll(!e.shiftKey, 1);
551
- break;
552
- }
553
- else
554
- return;
555
-
556
- case 32: // space
557
- if (settings.search && O.ftxt.is(e.target)) return;
558
- break;
559
- case 13: // enter
560
- if (O.is_opened)
561
- O.optDiv.find('ul li.sel').trigger('click');
562
- else
563
- O.setOnOpen();
564
- break;
565
- case 9: //tab
566
- if (!settings.okCancelInMulti)
567
- O.hideOpts();
568
- return;
569
- case 27: // esc
570
- if (settings.okCancelInMulti) O._cnbtn();
571
- O.hideOpts();
572
- return;
573
-
574
- default:
575
- return; // exit this handler for other keys
576
- }
577
- e.preventDefault(); // prevent the default action (scroll / move caret)
578
- });
579
-
580
- $(window).on('resize.sumo', () => {
581
- O.floatingList();
582
- });
583
- },
584
-
585
- onOptClick(li) {
586
- const O = this;
587
- li.click(function () {
588
- const $li = $(this);
589
- if ($li.hasClass('disabled')) return;
590
- if (O.is_multi) {
591
- $li.toggleClass('selected');
592
- $li.data('opt')[0].selected = $li.hasClass('selected');
593
- if ($li.data('opt')[0].selected === false) {
594
- O.lastUnselected = $li.data('opt')[0].textContent;
595
- O.selectedCount--;
596
- } else {
597
- O.selectedCount++;
598
- }
599
-
600
- if (settings.max) {
601
- O._handleMax();
602
- }
603
-
604
- O.selAllState();
605
- }
606
- else {
607
- $li.parent().find('li.selected').removeClass('selected'); //if not multiselect then remove all selections from this list
608
- $li.toggleClass('selected');
609
- $li.data('opt')[0].selected = true;
610
- O.selectedCount = 1;
611
- }
612
-
613
- //branch for combined change event.
614
- if (!(O.is_multi && settings.triggerChangeCombined && (O.is_floating || settings.okCancelInMulti))) {
615
- O.setText();
616
- O.callChange();
617
- }
618
-
619
- if (!O.is_multi) O.hideOpts(); //if its not a multiselect then hide on single select.
620
- });
621
- },
622
-
623
- // fixed some variables that were not explicitly typed (michc)
624
- setText() {
625
- const O = this;
626
- let lengthSelected = 0;
627
- O.placeholder = "";
628
- if (O.is_multi) {
629
- const sels = O.E.find(':checked').not(':disabled'); //selected options.
630
-
631
- lengthSelected = sels.length;
632
-
633
- if (settings.csvDispCount && sels.length > settings.csvDispCount) {
634
- if (sels.length === O.E.find('option').length && settings.captionFormatAllSelected) {
635
- O.placeholder = settings.captionFormatAllSelected.replace(/\{0\}/g, sels.length);
636
- }
637
- else {
638
- O.placeholder = settings.captionFormat.replace(/\{0\}/g, sels.length);
639
- }
640
- }
641
- else {
642
- O.placeholder = sels.toArray().map(selected => selected.innerText).join(', ');
643
- }
644
- }
645
- else {
646
- const option = O.E.find(':checked').not(':disabled');
647
- O.placeholder = option.text();
648
- lengthSelected = option.length;
649
- }
650
-
651
- let is_placeholder = false;
652
-
653
- if (!O.placeholder) {
654
-
655
- is_placeholder = true;
656
-
657
- O.placeholder = O.E.attr('placeholder');
658
- if (!O.placeholder) //if placeholder is there then set it
659
- O.placeholder = O.E.find('option:disabled:checked').text();
660
- }
661
-
662
- O.select.attr('selected-count', lengthSelected);
663
- O.select.attr('is-selected', lengthSelected ? 'true' : 'false');
664
-
665
- O.placeholder = O.placeholder ? (`${settings.prefix} ${O.placeholder}`) : settings.placeholder;
666
-
667
- //set display text
668
- O.caption.text(O.placeholder);
669
- if (settings.showTitle) O.CaptionCont.attr('title', O.placeholder);
670
-
671
- //set the hidden field if post as csv is true.
672
- const csvField = O.select.find('input.HEMANT123');
673
- if (csvField.length) csvField.val(O.getSelStr());
674
-
675
- //add class placeholder if its a placeholder text.
676
- if (is_placeholder) O.caption.addClass('placeholder'); else O.caption.removeClass('placeholder');
677
- return O.placeholder;
678
- },
679
-
680
- isMobile() {
681
-
682
- // Adapted from http://www.detectmobilebrowsers.com
683
- const ua = navigator.userAgent || navigator.vendor || window.opera;
684
-
685
- // Checks for iOs, Android, Blackberry, Opera Mini, and Windows mobile devices
686
- for (let i = 0; i < settings.nativeOnDevice.length; i++) if (ua.toString().toLowerCase().indexOf(settings.nativeOnDevice[i].toLowerCase()) > 0) return settings.nativeOnDevice[i];
687
- return false;
688
- },
689
-
690
- setNativeMobile() {
691
- const O = this;
692
- O.E.addClass('SelectClass');//.css('height', O.select.outerHeight());
693
- O.mob = true;
694
- O.E.change(() => {
695
- O.setText();
696
- });
697
- },
698
-
699
- floatingList() {
700
- const O = this;
701
- //called on init and also on resize.
702
- //O.is_floating = true if window width is < specified float width
703
- O.is_floating = $(window).width() <= settings.floatWidth;
704
-
705
- //set class isFloating
706
- O.optDiv.toggleClass('isFloating', O.is_floating);
707
-
708
- //remove height if not floating
709
- if (!O.is_floating) O.optDiv.css('height', '');
710
-
711
- //toggle class according to okCancelInMulti flag only when it is not floating
712
- O.optDiv.toggleClass('okCancelInMulti', settings.okCancelInMulti && !O.is_floating);
713
- },
714
-
715
- //HELPERS FOR OUTSIDERS
716
- // validates range of given item operations
717
- vRange(i) {
718
- const O = this;
719
- const opts = O.E.find('option');
720
- if (opts.length <= i || i < 0) throw new Error("index out of bounds");
721
- return O;
722
- },
723
-
724
- //toggles selection on c as boolean.
725
- toggSel(c, i) {
726
- const O = this;
727
- let opt = null;
728
- if (typeof (i) === "number") {
729
- O.vRange(i);
730
- opt = O.E.find('option')[i];
731
- }
732
- else {
733
- opt = O.E.find(`option[value="${i}"]`)[0] || 0;
734
- }
735
- if (!opt || opt.disabled)
736
- return;
737
-
738
- if (opt.selected !== c) {
739
- if ((settings.max && !opt.selected && O.selectedCount < settings.max) || opt.selected || (!settings.max && !opt.selected)) {
740
- opt.selected = c;
741
- if (!O.mob) $(opt).data('li').toggleClass('selected', c);
742
-
743
- O.callChange();
744
- O.setPstate();
745
- O.setText();
746
- O.selAllState();
747
- }
748
- }
749
- },
750
-
751
- //toggles disabled on c as boolean.
752
- toggDis(c, i) {
753
- const O = this.vRange(i);
754
- O.E.find('option')[i].disabled = c;
755
- if (c) O.E.find('option')[i].selected = false;
756
- if (!O.mob) O.optDiv.find('ul.options li.opt').eq(i).toggleClass('disabled', c).removeClass('selected');
757
- O.setText();
758
- },
759
-
760
- // toggle disable/enable on complete select control
761
- toggSumo(val) {
762
- const O = this;
763
- O.enabled = val;
764
- O.select.toggleClass('disabled', val);
765
-
766
- if (val) {
767
- O.E.attr('disabled', 'disabled');
768
- O.select.removeAttr('tabindex');
769
- }
770
- else {
771
- O.E.removeAttr('disabled');
772
- O.select.attr('tabindex', '0');
773
- }
774
-
775
- return O;
776
- },
777
-
778
- // toggles all option on c as boolean.
779
- // set direct=false/0 bypasses okCancelInMulti behaviour.
780
- toggSelAll(c, direct) {
781
- const O = this;
782
- const cloneOriginalEvents = $.extend(true, {}, $._data(O.E.get(0), "events")); // clone original select elements events
783
- O.E.off(); // unbind original select elements events because we do not want the following clicks to trigger change on it
784
-
785
- if (O.is_multi) {
786
- // Select all
787
- if (c) {
788
- O.E.find('option').toArray().filter((option) => !option.selected && !option.disabled && option.style.display !== 'none').forEach(option => {
789
- if (!$(option).data('li').hasClass('hidden')) {
790
- option.selected = true;
791
- $(option).data('li').toggleClass('selected', true);
792
- }
793
- });
794
- } else {
795
- // Unselect all
796
- O.E.find('option').toArray().filter((option) => option.selected && !option.disabled && option.style.display !== 'none').forEach(option => {
797
- if (!$(option).data('li').hasClass('hidden')) {
798
- option.selected = false;
799
- $(option).data('li').toggleClass('selected', false);
800
- }
801
- });
802
- }
803
- } else {
804
- if (!c) O.E[0].selectedIndex = -1;
805
- else console.warn('You called `SelectAll` on a non-multiple select');
806
- }
807
-
808
- // rebind original select elements events
809
- $.each(cloneOriginalEvents, (_, e) => {
810
- $.each(e, (__, ev) => {
811
- O.E.on(ev.type, ev.handler);
812
- });
813
- });
814
-
815
- if ((O.is_multi && !settings.okCancelInMulti) || !O.is_multi) {
816
- O.callChange(); // call change on original select element
817
- O.setText();
818
- }
819
-
820
- if (!direct) {
821
- if (!O.mob && O.selAll) O.selAll.removeClass('partial').toggleClass('selected', !!c);
822
- O.setText();
823
- O.setPstate();
824
- }
825
- },
826
-
827
- /* outside accessibility options
828
- which can be accessed from the element instance.
829
- */
830
- reload() {
831
- const elm = this.unload();
832
- return $(elm).SumoSelect(settings);
833
- },
834
-
835
- unload() {
836
- const O = this;
837
- O.select.before(O.E);
838
- O.E.show();
839
- O.E[0].classList.remove('SumoUnder', 'SelectClass');
840
-
841
- if (settings.outputAsCSV && O.is_multi && O.select.find('input.HEMANT123').length) {
842
- O.E.attr('name', O.select.find('input.HEMANT123').attr('name')); // restore the name;
843
- }
844
- O.select.remove();
845
- delete selObj.sumo;
846
- O.E.trigger('sumo:unloaded', O);
847
- return selObj;
848
- },
849
-
850
- //## add a new option to select at a given index.
851
- add(val, txt, i, attr) {
852
- if (typeof val === "undefined") throw new Error("No value to add");
853
-
854
- const O = this;
855
- const opts = O.E.find('option');
856
- const value = val;
857
- let
858
- text = txt,
859
- index = i;
860
- if (typeof txt === "number") { // .add('xxx', 2) shorthand
861
- index = txt;
862
- text = val;
863
- } else if (typeof txt === "undefined") { // .add('xxx') shorthand
864
- text = val;
865
- }
866
-
867
- const opt = $("<option></option>").val(value).html(text);
868
-
869
- if (attr && typeof attr === "object") {
870
- $.each(attr, (j, v) => {
871
- opt.attr(j, v);
872
- });
873
- }
874
-
875
- if (opts.length < index) throw new Error("index out of bounds");
876
-
877
- if (typeof index === "undefined" || opts.length === index) { // add it to the last if given index is last no or no index provides.
878
- O.E.append(opt);
879
- if (!O.mob) O.ul.append(O.createLi(opt));
880
- }
881
- else {
882
- opts.eq(index).before(opt);
883
- if (!O.mob) O.ul.find('li.opt').eq(index).before(O.createLi(opt));
884
- }
885
-
886
- return selObj;
887
- },
888
-
889
- //## removes an item at a given index.
890
- remove(i) {
891
- const O = this.vRange(i);
892
- O.E.find('option').eq(i).remove();
893
- if (!O.mob) O.optDiv.find('ul.options li.opt').eq(i).remove();
894
- O.setText();
895
- },
896
-
897
- // removes all but the selected one
898
- removeAll() {
899
- const O = this;
900
- const optionList = O.E.find('option');
901
-
902
- for (let x = (optionList.length - 1); x >= 0; x--) {
903
- if (optionList[x].selected !== true) {
904
- O.remove(x);
905
- }
906
- }
907
-
908
- },
909
-
910
-
911
- find(val) {
912
- const O = this;
913
- const optionList = O.E.find('option');
914
- for (const x in optionList) {
915
- if (optionList[x].value === val) {
916
- return +x;
917
- }
918
- }
919
-
920
- return -1;
921
-
922
- },
923
-
924
- //## Select an item at a given index.
925
- selectItem(i) { this.toggSel(true, i); },
926
-
927
- //## UnSelect an iten at a given index.
928
- unSelectItem(i) { this.toggSel(false, i); },
929
-
930
- //## Select all items of the select.
931
- selectAll() { this.toggSelAll(true); },
932
-
933
- //## UnSelect all items of the select.
934
- unSelectAll() { this.toggSelAll(false); },
935
-
936
- //## Disable an iten at a given index.
937
- disableItem(i) { this.toggDis(true, i); },
938
-
939
- //## Removes disabled an iten at a given index.
940
- enableItem(i) { this.toggDis(false, i); },
941
-
942
- //## New simple methods as getter and setter are not working fine in ie8-
943
- //## variable to check state of control if enabled or disabled.
944
- enabled: true,
945
- //## Enables the control
946
- enable() { return this.toggSumo(false); },
947
-
948
- //## Disables the control
949
- disable() { return this.toggSumo(true); },
950
-
951
-
952
- init() {
953
- const O = this;
954
- O.createElems();
955
- O.setText();
956
- O.E.trigger('sumo:initialized', O);
957
- return O;
958
- }
959
-
960
- };
961
-
962
- selObj.sumo.init();
963
- });
964
-
965
- return ret.length === 1 ? ret[0] : ret;
966
- };
967
-
968
-
969
- });
1
+ /*global jQuery, define, module, require*/
2
+ /*!
3
+ * jquery.sumoselect
4
+ * http://hemantnegi.github.io/jquery.sumoselect
5
+ * 2016-12-12
6
+ *
7
+ * Copyright 2015 Hemant Negi
8
+ * Email : hemant.frnz@gmail.com
9
+ * Compressor http://refresh-sf.com/
10
+ */
11
+
12
+ (function (factory) {
13
+ 'use strict';
14
+ if (typeof define === 'function' && define.amd) {
15
+ define(['jquery'], factory);
16
+ } else if (typeof exports !== 'undefined') {
17
+ module.exports = factory(require('jquery'));
18
+ } else {
19
+ factory(jQuery);
20
+ }
21
+
22
+ })(($) => {
23
+
24
+ 'namespace sumo';
25
+ $.fn.SumoSelect = function (options) {
26
+ // Extra check for IE compatibility
27
+ const dispatchEvent = (target, eventName) => {
28
+ let event = null;
29
+ if (typeof (Event) === 'function') {
30
+ event = new Event(eventName, {
31
+ bubbles: true
32
+ });
33
+ } else {
34
+ event = document.createEvent('Event');
35
+ event.initEvent(eventName, true, true);
36
+ }
37
+
38
+ target.dispatchEvent(event);
39
+ };
40
+
41
+ // missing forEach on NodeList for IE11
42
+ if (window.NodeList && !NodeList.prototype.forEach) {
43
+ NodeList.prototype.forEach = Array.prototype.forEach;
44
+ }
45
+
46
+ // This is the easiest way to have default options.
47
+ const defaultOptions = {
48
+ placeholder: 'Select Here', // Dont change it here.
49
+ csvDispCount: 3, // display no. of items in multiselect. 0 to display all.
50
+ captionFormat: '{0} Selected', // format of caption text. you can set your locale.
51
+ captionFormatAllSelected: '{0} all selected!', // format of caption text when all elements are selected. set null to use captionFormat. It will not work if there are disabled elements in select.
52
+ floatWidth: 400, // Screen width of device at which the list is rendered in floating popup fashion.
53
+ forceCustomRendering: false, // force the custom modal on all devices below floatWidth resolution.
54
+ nativeOnDevice: ['Android', 'BlackBerry', 'iPhone', 'iPad', 'iPod', 'Opera Mini', 'IEMobile', 'Silk'], //
55
+ outputAsCSV: false, // true to POST data as csv ( false for Html control array ie. default select )
56
+ csvSepChar: ',', // separation char in csv mode
57
+ okCancelInMulti: false, // display ok cancel buttons in desktop mode multiselect also.
58
+ isClickAwayOk: false, // for okCancelInMulti=true. sets whether click outside will trigger Ok or Cancel (default is cancel).
59
+ triggerChangeCombined: true, // im multi select mode whether to trigger change event on individual selection or combined selection.
60
+ selectAll: false, // to display select all button in multiselect mode.|| also select all will not be available on mobile devices.
61
+ selectAllPartialCheck: true, // Display a disabled checkbox in multiselect mode when all the items are not selected.
62
+ search: false, // to display input for filtering content. selectAlltext will be input text placeholder
63
+ searchText: 'Search...', // placeholder for search input
64
+ searchFn(haystack, needle) { // search function
65
+ return haystack.toLowerCase().indexOf(needle.toLowerCase()) < 0;
66
+ },
67
+ noMatch: 'No matches for "{0}"',
68
+ prefix: '', // some prefix usually the field name. eg. '<b>Hello</b>'
69
+ locale: ['OK', 'Cancel', 'Select All', 'Clear all'], // all text that is used. don't change the index.
70
+ up: false, // set true to open upside.
71
+ showTitle: true, // set to false to prevent title (tooltip) from appearing
72
+ clearAll: false, // im multi select - clear all checked options
73
+ closeAfterClearAll: false, // im multi select - close select after clear
74
+ max: null, // Maximum number of options selected (when multiple)
75
+ // eslint-disable-next-line no-unused-vars
76
+ renderLi: (li, _originalOption) => li // Custom <li> item renderer
77
+ };
78
+
79
+ const ret = this.each(function () {
80
+ const selObj = this; // the original select object.
81
+ if (this.sumo || !$(this).is('select')) return; //already initialized
82
+
83
+ const settings = $.extend({}, defaultOptions, options, $(this).data());
84
+
85
+ this.sumo = {
86
+ E: $(selObj), //the jquery object of original select element.
87
+ is_multi: $(selObj).attr('multiple'), //if its a multiple select
88
+ select: '',
89
+ caption: '',
90
+ placeholder: '',
91
+ optDiv: '',
92
+ CaptionCont: '',
93
+ ul: '',
94
+ is_floating: false,
95
+ is_opened: false,
96
+ //backdrop: '',
97
+ mob: false, // if to open device default select
98
+ Pstate: [],
99
+ lastUnselected: null,
100
+ selectedCount: 0,
101
+
102
+ createElems() {
103
+ const O = this;
104
+ const selectedOptions = O.E.find('option:checked');
105
+ O.E.wrap('<div class="SumoSelect" tabindex="0" role="button" aria-expanded="false">');
106
+ selectedOptions.each((_, selectedOption) => { // Fix for IE resetting index to 0 when -1
107
+ selectedOption.selected = true;
108
+ });
109
+ O.select = O.E.parent();
110
+ O.caption = $('<span>');
111
+ O.CaptionCont = $(`<p class="CaptionCont SelectBox ${O.E.attr('class')}" ><label><i></i></label></p>`)
112
+ .attr('style', O.E.attr('style'))
113
+ .prepend(O.caption);
114
+ O.select.append(O.CaptionCont);
115
+
116
+ // default turn off if no multiselect
117
+ if (!O.is_multi) settings.okCancelInMulti = false;
118
+
119
+ if (O.E.attr('disabled'))
120
+ O.select.addClass('disabled').removeAttr('tabindex');
121
+
122
+ //if output as csv and is a multiselect.
123
+ if (settings.outputAsCSV && O.is_multi && O.E.attr('name')) {
124
+ //create a hidden field to store csv value.
125
+ O.select.append($('<input class="HEMANT123" type="hidden" />').attr('name', O.E.attr('name')).val(O.getSelStr()));
126
+
127
+ // so it can not post the original select.
128
+ O.E.removeAttr('name');
129
+ }
130
+
131
+ //break for mobile rendring.. if forceCustomRendering is false
132
+ if (O.isMobile() && !settings.forceCustomRendering) {
133
+ O.setNativeMobile();
134
+ return;
135
+ }
136
+
137
+ // if there is a name attr in select add a class to container div
138
+ if (O.E.attr('name')) O.select.addClass(`sumo_${O.E.attr('name').replace(/\[\]/, '')}`);
139
+
140
+ //hide original select
141
+ O.E.addClass('SumoUnder').attr('tabindex', '-1');
142
+
143
+ //## Creating the list...
144
+ O.optDiv = $(`<div class="optWrapper ${settings.up ? 'up' : ''}">`);
145
+
146
+ //branch for floating list in low res devices.
147
+ O.floatingList();
148
+
149
+ //Creating the markup for the available options
150
+ O.ul = $('<ul class="options">');
151
+ O.optDiv.append(O.ul);
152
+
153
+ // Select all functionality
154
+ if (settings.clearAll && O.is_multi) O.ClearAll();
155
+ if (settings.selectAll && O.is_multi && !settings.max) O.SelAll();
156
+
157
+ // search functionality
158
+ if (settings.search) O.Search();
159
+
160
+ O.ul.append(O.prepItems(O.E.children()));
161
+
162
+ //if multiple then add the class multiple and add OK / CANCEL button
163
+ if (O.is_multi) O.multiSelelect();
164
+
165
+ O.select.append(O.optDiv);
166
+ O._handleMax();
167
+ O.basicEvents();
168
+ O.selAllState();
169
+ },
170
+
171
+ prepItems(opts, d) {
172
+ const lis = [], O = this;
173
+ $(opts).each((i, opt) => { // parsing options to li
174
+ const $opt = $(opt);
175
+ lis.push($opt.is('optgroup') ?
176
+ $(`<li class="group ${opt.disabled ? 'disabled' : ''}"><label></label><ul></ul></li>`)
177
+ .find('label')
178
+ .text($opt.attr('label'))
179
+ .end()
180
+ .find('ul')
181
+ .append(O.prepItems($opt.children(), opt.disabled))
182
+ .end()
183
+ :
184
+ O.createLi($opt, d)
185
+ );
186
+ });
187
+ return lis;
188
+ },
189
+
190
+ //## Creates a LI element from a given option and binds events to it
191
+ //## returns the jquery instance of li (not inserted in dom)
192
+ createLi(opt, d) {
193
+ const O = this;
194
+
195
+ if (!opt.attr('value')) opt.attr('value', opt.val());
196
+ const li = $(`<li class="opt"><label>${opt.html()}</label></li>`);
197
+
198
+ li.data('opt', opt); // store a direct reference to option.
199
+ opt.data('li', li); // store a direct reference to list item.
200
+ if (O.is_multi) li.prepend('<span><i></i></span>');
201
+
202
+ if (opt[0].disabled || d)
203
+ li.addClass('disabled');
204
+
205
+ O.onOptClick(li);
206
+
207
+ if (opt[0].selected) {
208
+ li.addClass('selected');
209
+ O.selectedCount++;
210
+ }
211
+
212
+ if (opt.attr('class'))
213
+ li.addClass(opt.attr('class'));
214
+
215
+ if (opt.attr('title'))
216
+ li.attr('title', opt.attr('title'));
217
+
218
+ return settings.renderLi(li, opt);
219
+ },
220
+
221
+ //## Returns the selected items as string in a Multiselect.
222
+ getSelStr() {
223
+ // get the pre selected items.
224
+ const sopt = [];
225
+ this.E.find('option:checked').each(function () { sopt.push($(this).val()); });
226
+ return sopt.join(settings.csvSepChar);
227
+ },
228
+
229
+ //## THOSE OK/CANCEL BUTTONS ON MULTIPLE SELECT.
230
+ multiSelelect() {
231
+ const O = this;
232
+ O.optDiv.addClass('multiple');
233
+ O.okbtn = $('<p tabindex="0" class="btnOk"></p>').on('click', () => {
234
+ //if combined change event is set.
235
+ O._okbtn();
236
+ O.hideOpts();
237
+ });
238
+ [O.okbtn[0].innerText] = settings.locale;
239
+
240
+ O.cancelBtn = $('<p tabindex="0" class="btnCancel"></p>').on('click', () => {
241
+ O._cnbtn();
242
+ O.hideOpts();
243
+ });
244
+ [, O.cancelBtn[0].innerText] = settings.locale;
245
+
246
+ const btns = O.okbtn.add(O.cancelBtn);
247
+ O.optDiv.append($('<div class="MultiControls">').append(btns));
248
+
249
+ // handling keyboard navigation on ok cancel buttons.
250
+ btns.on('keydown.sumo', function (e) {
251
+ const el = $(this);
252
+ switch (e.which) {
253
+ case 32: // space
254
+ case 13: // enter
255
+ el.trigger('click');
256
+ break;
257
+
258
+ case 9: //tab
259
+ if (el.hasClass('btnOk')) return;
260
+ break;
261
+ case 27: // esc
262
+ O._cnbtn();
263
+ O.hideOpts();
264
+ return;
265
+ default:
266
+ break;
267
+ }
268
+ e.stopPropagation();
269
+ e.preventDefault();
270
+ });
271
+ },
272
+
273
+ _okbtn() {
274
+ const O = this;
275
+ let cg = 0;
276
+ //if combined change event is set.
277
+ if (settings.triggerChangeCombined) {
278
+ //check for a change in the selection.
279
+ if (O.E.find('option:checked').length !== O.Pstate.length) {
280
+ cg = 1;
281
+ }
282
+ else {
283
+ O.E.find('option').each((i, e) => {
284
+ if (e.selected && O.Pstate.indexOf(i) < 0) cg = 1;
285
+ });
286
+ }
287
+
288
+ if (cg) {
289
+ O.callChange();
290
+ O.setText();
291
+ }
292
+ }
293
+ },
294
+ _cnbtn() {
295
+ const O = this;
296
+ //remove all selections
297
+ O.E.find('option:checked').each(function () { this.selected = false; });
298
+ O.optDiv.find('li.selected').removeClass('selected');
299
+
300
+ //restore selections from saved state.
301
+ for (let i = 0; i < O.Pstate.length; i++) {
302
+ O.E.find('option')[O.Pstate[i]].selected = true;
303
+ O.ul.find('li.opt').eq(O.Pstate[i]).addClass('selected');
304
+ }
305
+ O.setText();
306
+ O.selAllState();
307
+ },
308
+
309
+ _handleMax() {
310
+ // Disable options if max reached
311
+ if (settings.max) {
312
+ if (this.selectedCount >= +settings.max) {
313
+ this.optDiv.find('li.opt').not('.hidden').each((ix, e) => {
314
+ if (!$(e).hasClass('selected')) {
315
+ $(e).addClass('temporary-disabled disabled');
316
+ }
317
+ });
318
+ } else {
319
+ // Enable options back
320
+ this.optDiv.find('li.opt').not('.hidden').each((ix, e) => {
321
+ if ($(e).hasClass('temporary-disabled')) {
322
+ $(e).removeClass('temporary-disabled disabled');
323
+ }
324
+ });
325
+ }
326
+ }
327
+ },
328
+
329
+ ClearAll () {
330
+ const O = this;
331
+ if (!O.is_multi) return;
332
+ O.selAll = $('<p class="reset-all"><span><i></i></span><label></label></p>');
333
+ [, , , O.selAll.find('label')[0].innerText] = settings.locale;
334
+ O.optDiv.addClass('resetAll');
335
+ O.selAll.on('click', () => {
336
+ O.selAll.removeClass('selected');
337
+ O.toggSelAll(false, 1);
338
+ if (settings.closeAfterClearAll) {
339
+ O.hideOpts();
340
+ }
341
+ });
342
+
343
+ O.optDiv.prepend(O.selAll);
344
+ },
345
+
346
+ SelAll() {
347
+ const O = this;
348
+ if (!O.is_multi) return;
349
+ O.selAll = $('<p class="select-all"><span><i></i></span><label></label></p>');
350
+ [, , O.selAll.find('label')[0].innerText] = settings.locale;
351
+ O.optDiv.addClass('selall');
352
+ O.selAll.on('click', () => {
353
+ O.selAll.toggleClass('selected');
354
+ O.toggSelAll(O.selAll.hasClass('selected'), 1);
355
+ O.selAllState();
356
+ });
357
+
358
+ O.optDiv.prepend(O.selAll);
359
+ },
360
+
361
+ // search module (can be removed if not required.)
362
+ Search() {
363
+ const O = this,
364
+ cc = O.CaptionCont.addClass('search'),
365
+ P = $('<p class="no-match">'),
366
+ fn = (options.searchFn && typeof options.searchFn === 'function') ? options.searchFn : settings.searchFn;
367
+
368
+ O.ftxt = $('<input type="search" class="search-txt" value="" autocomplete="off">')
369
+ .on('click', (e) => {
370
+ e.stopPropagation();
371
+ });
372
+ O.ftxt[0].placeholder = settings.searchText;
373
+ cc.append(O.ftxt);
374
+ O.optDiv.children('ul').after(P);
375
+
376
+ O.ftxt.on('input.sumo', () => {
377
+ const hid = O.optDiv.find('ul.options li.opt').each((ix, e) => {
378
+ const el = $(e),
379
+ { 0: opt } = el.data('opt');
380
+ opt.hidden = fn(el.text(), O.ftxt.val(), el);
381
+ el.toggleClass('hidden', opt.hidden);
382
+ }).not('.hidden');
383
+
384
+ // Hide opt-groups with no options matched
385
+ O.optDiv[0].querySelectorAll('li.group').forEach(optGroup => {
386
+ if (optGroup.querySelector('li:not(.hidden)')) {
387
+ optGroup.classList.remove('hidden');
388
+ } else {
389
+ optGroup.classList.add('hidden');
390
+ }
391
+ });
392
+
393
+ P.html(settings.noMatch.replace(/\{0\}/g, '<em></em>')).toggle(!hid.length);
394
+ P.find('em').text(O.ftxt.val());
395
+ O.selAllState();
396
+ });
397
+ },
398
+
399
+ selAllState() {
400
+ const O = this;
401
+ if (settings.selectAll && O.is_multi) {
402
+ let sc = 0, vc = 0;
403
+ O.optDiv.find('li.opt:not(.disabled):not(.hidden)').each((ix, e) => {
404
+ if ($(e).hasClass('selected')) sc++;
405
+ vc++;
406
+ });
407
+ //select all checkbox state change.
408
+ if (sc === vc) O.selAll.removeClass('partial').addClass('selected');
409
+ else if (sc === 0) O.selAll.removeClass('selected partial');
410
+ else {
411
+ if(settings.selectAllPartialCheck) {
412
+ O.selAll.addClass('partial')
413
+ }
414
+ O.selAll.removeClass('selected');
415
+ }
416
+ }
417
+ },
418
+
419
+ showOpts() {
420
+ const O = this;
421
+ if (O.E.attr('disabled')) return; // if select is disabled then retrun
422
+ O.E.trigger('sumo:opening', O);
423
+ O.is_opened = true;
424
+ O.select.addClass('open').attr('aria-expanded', 'true');
425
+
426
+ // Scroll first selected option into view
427
+ const firstSelected = O.optDiv.find('li.opt.selected').first();
428
+ if (firstSelected.length) {
429
+ O.optDiv.find('.options').scrollTop(firstSelected.position().top);
430
+ } else {
431
+ O.optDiv.find('.options').scrollTop(0);
432
+ }
433
+
434
+ O.E.trigger('sumo:opened', O);
435
+
436
+ if (O.ftxt) O.ftxt.focus();
437
+ else O.select.focus();
438
+
439
+ // hide options on click outside.
440
+ $(document).on('click.sumo', (e) => {
441
+ if (!O.select.is(e.target) // if the target of the click isn't the container...
442
+ && O.select.has(e.target).length === 0) { // ... nor a descendant of the container
443
+ if (!O.is_opened) return;
444
+ O.hideOpts();
445
+ if (settings.okCancelInMulti) {
446
+ if (settings.isClickAwayOk)
447
+ O._okbtn();
448
+ else
449
+ O._cnbtn();
450
+ }
451
+ }
452
+ });
453
+
454
+ if (O.is_floating) {
455
+ let H = O.optDiv.children('ul').outerHeight() + 2; // +2 is clear fix
456
+ if (O.is_multi) H = H + +O.optDiv.css('padding-bottom');
457
+ O.optDiv.css('height', H);
458
+ $('body').addClass('sumoStopScroll');
459
+ }
460
+
461
+ O.setPstate();
462
+ },
463
+
464
+ //maintain state when ok/cancel buttons are available storing the indexes.
465
+ setPstate() {
466
+ const O = this;
467
+ if (O.is_multi && (O.is_floating || settings.okCancelInMulti)) {
468
+ O.Pstate = [];
469
+ // assuming that find returns elements in tree order
470
+ O.E.find('option').each((i, e) => { if (e.selected) O.Pstate.push(i); });
471
+ }
472
+ },
473
+
474
+ callChange() {
475
+ this.E.get().forEach(e => {
476
+ dispatchEvent(e, 'change');
477
+ dispatchEvent(e, 'click');
478
+ });
479
+ },
480
+
481
+ hideOpts() {
482
+ const O = this;
483
+ if (O.is_opened) {
484
+ O.E.trigger('sumo:closing', O);
485
+ O.is_opened = false;
486
+ O.select.removeClass('open').attr('aria-expanded', 'false').find('ul li.sel').removeClass('sel');
487
+ O.E.trigger('sumo:closed', O);
488
+ $(document).off('click.sumo');
489
+ $('body').removeClass('sumoStopScroll');
490
+
491
+ // clear the search
492
+ if (settings.search) {
493
+ O.ftxt.val('');
494
+ O.ftxt.trigger('input.sumo');
495
+ }
496
+ }
497
+ },
498
+ setOnOpen() {
499
+ const O = this;
500
+ let li = O.optDiv.find('li.opt:not(.hidden)').eq(settings.search ? 0 : O.E[0].selectedIndex);
501
+ if (li.hasClass('disabled')) {
502
+ li = li.next(':not(disabled)');
503
+ if (!li.length) return;
504
+ }
505
+ O.optDiv.find('li.sel').removeClass('sel');
506
+ li.addClass('sel');
507
+ O.showOpts();
508
+ },
509
+ nav(up) {
510
+ const O = this;
511
+ let c = null, sel = O.ul.find('li.opt.sel:not(.hidden)');
512
+ const
513
+ s = O.ul.find('li.opt:not(.disabled):not(.hidden)'),
514
+ idx = s.index(sel);
515
+ if (O.is_opened && sel.length) {
516
+ if (up && idx > 0)
517
+ c = s.eq(idx - 1);
518
+ else if (!up && idx < s.length - 1 && idx > -1)
519
+ c = s.eq(idx + 1);
520
+ else return; // if no items before or after
521
+
522
+ sel.removeClass('sel');
523
+ sel = c.addClass('sel');
524
+
525
+ // setting sel item to visible view.
526
+ const { ul } = O,
527
+ st = ul.scrollTop(),
528
+ t = sel.position().top + st;
529
+ if (t >= st + ul.height() - sel.outerHeight())
530
+ ul.scrollTop(t - ul.height() + sel.outerHeight());
531
+ if (t < st)
532
+ ul.scrollTop(t);
533
+
534
+ }
535
+ else
536
+ O.setOnOpen();
537
+ },
538
+
539
+ basicEvents() {
540
+ const O = this;
541
+ O.CaptionCont.on('click', (evt) => {
542
+ O.E.trigger('click');
543
+ if (O.is_opened) O.hideOpts(); else O.showOpts();
544
+ evt.stopPropagation();
545
+ });
546
+
547
+ O.select.on('keydown.sumo', (e) => {
548
+ switch (e.which) {
549
+ case 38: // up
550
+ O.nav(true);
551
+ break;
552
+
553
+ case 40: // down
554
+ O.nav(false);
555
+ break;
556
+
557
+ case 65: // shortcut ctrl + a to select all and ctrl + shift + a to unselect all.
558
+ if (O.is_multi && !settings.max && e.ctrlKey) {
559
+ O.toggSelAll(!e.shiftKey, 1);
560
+ break;
561
+ }
562
+ else
563
+ return;
564
+
565
+ case 32: // space
566
+ if (settings.search && O.ftxt.is(e.target)) return;
567
+ break;
568
+ case 13: // enter
569
+ if (O.is_opened)
570
+ O.optDiv.find('ul li.sel').trigger('click');
571
+ if (settings.search) O.select.focus();
572
+ else
573
+ O.setOnOpen();
574
+ break;
575
+ case 9: //tab
576
+ if (!settings.okCancelInMulti)
577
+ O.hideOpts();
578
+ return;
579
+ case 27: // esc
580
+ if (settings.okCancelInMulti) O._cnbtn();
581
+ O.hideOpts();
582
+ if (settings.search) O.select.focus();
583
+ return;
584
+
585
+ default:
586
+ return; // exit this handler for other keys
587
+ }
588
+ e.preventDefault(); // prevent the default action (scroll / move caret)
589
+ });
590
+
591
+ $(window).on('resize.sumo', () => {
592
+ O.floatingList();
593
+ });
594
+ },
595
+
596
+ onOptClick(li) {
597
+ const O = this;
598
+ li.on('click', function () {
599
+ const $li = $(this);
600
+ if ($li.hasClass('disabled')) return;
601
+ if (O.is_multi) {
602
+ $li.toggleClass('selected');
603
+ $li.data('opt')[0].selected = $li.hasClass('selected');
604
+ if ($li.data('opt')[0].selected === false) {
605
+ O.lastUnselected = $li.data('opt')[0].textContent;
606
+ O.selectedCount--;
607
+ } else {
608
+ O.selectedCount++;
609
+ }
610
+
611
+ if (settings.max) {
612
+ O._handleMax();
613
+ }
614
+
615
+ O.selAllState();
616
+ }
617
+ else {
618
+ $li.parent().find('li.selected').removeClass('selected'); //if not multiselect then remove all selections from this list
619
+ $li.toggleClass('selected');
620
+ $li.data('opt')[0].selected = true;
621
+ O.selectedCount = 1;
622
+ }
623
+
624
+ //branch for combined change event.
625
+ if (!(O.is_multi && settings.triggerChangeCombined && (O.is_floating || settings.okCancelInMulti))) {
626
+ O.setText();
627
+ O.callChange();
628
+ }
629
+
630
+ if (!O.is_multi) O.hideOpts(); //if its not a multiselect then hide on single select.
631
+ });
632
+ },
633
+
634
+ // fixed some variables that were not explicitly typed (michc)
635
+ setText() {
636
+ const O = this;
637
+ let lengthSelected = 0;
638
+ O.placeholder = "";
639
+ if (O.is_multi) {
640
+ const sels = O.E.find(':checked').not(':disabled'); //selected options.
641
+
642
+ lengthSelected = sels.length;
643
+
644
+ if (settings.csvDispCount && sels.length > settings.csvDispCount) {
645
+ if (sels.length === O.E.find('option').length && settings.captionFormatAllSelected) {
646
+ O.placeholder = settings.captionFormatAllSelected.replace(/\{0\}/g, sels.length);
647
+ }
648
+ else {
649
+ O.placeholder = settings.captionFormat.replace(/\{0\}/g, sels.length);
650
+ }
651
+ }
652
+ else {
653
+ O.placeholder = sels.toArray().map(selected => selected.innerText).join(', ');
654
+ }
655
+ }
656
+ else {
657
+ const option = O.E.find(':checked').not(':disabled');
658
+ O.placeholder = option.text();
659
+ lengthSelected = option.length;
660
+ }
661
+
662
+ let is_placeholder = false;
663
+
664
+ if (!O.placeholder) {
665
+
666
+ is_placeholder = true;
667
+
668
+ O.placeholder = O.E.attr('placeholder');
669
+ if (!O.placeholder) //if placeholder is there then set it
670
+ O.placeholder = O.E.find('option:disabled:checked').text();
671
+ }
672
+
673
+ O.select.attr('selected-count', lengthSelected);
674
+ O.select.attr('is-selected', lengthSelected ? 'true' : 'false');
675
+
676
+ O.placeholder = O.placeholder ? (`${settings.prefix} ${O.placeholder}`) : settings.placeholder;
677
+
678
+ //set display text
679
+ O.caption.text(O.placeholder);
680
+ if (settings.showTitle) O.CaptionCont.attr('title', O.placeholder);
681
+
682
+ //set the hidden field if post as csv is true.
683
+ const csvField = O.select.find('input.HEMANT123');
684
+ if (csvField.length) csvField.val(O.getSelStr());
685
+
686
+ //add class placeholder if its a placeholder text.
687
+ if (is_placeholder) O.caption.addClass('placeholder'); else O.caption.removeClass('placeholder');
688
+ return O.placeholder;
689
+ },
690
+
691
+ isMobile() {
692
+
693
+ // Adapted from http://www.detectmobilebrowsers.com
694
+ const ua = navigator.userAgent || navigator.vendor || window.opera;
695
+
696
+ // Checks for iOs, Android, Blackberry, Opera Mini, and Windows mobile devices
697
+ for (let i = 0; i < settings.nativeOnDevice.length; i++) if (ua.toString().toLowerCase().indexOf(settings.nativeOnDevice[i].toLowerCase()) > 0) return settings.nativeOnDevice[i];
698
+ return false;
699
+ },
700
+
701
+ setNativeMobile() {
702
+ const O = this;
703
+ O.E.addClass('SelectClass');//.css('height', O.select.outerHeight());
704
+ O.mob = true;
705
+ O.E.change(() => {
706
+ O.setText();
707
+ });
708
+ },
709
+
710
+ floatingList() {
711
+ const O = this;
712
+ //called on init and also on resize.
713
+ //O.is_floating = true if window width is < specified float width
714
+ O.is_floating = $(window).width() <= settings.floatWidth;
715
+
716
+ //set class isFloating
717
+ O.optDiv.toggleClass('isFloating', O.is_floating);
718
+
719
+ //remove height if not floating
720
+ if (!O.is_floating) O.optDiv.css('height', '');
721
+
722
+ //toggle class according to okCancelInMulti flag only when it is not floating
723
+ O.optDiv.toggleClass('okCancelInMulti', settings.okCancelInMulti && !O.is_floating);
724
+ },
725
+
726
+ //HELPERS FOR OUTSIDERS
727
+ // validates range of given item operations
728
+ vRange(i) {
729
+ const O = this;
730
+ const opts = O.E.find('option');
731
+ if (opts.length <= i || i < 0) throw new Error("index out of bounds");
732
+ return O;
733
+ },
734
+
735
+ //toggles selection on c as boolean.
736
+ toggSel(c, i) {
737
+ const O = this;
738
+ let opt = null;
739
+ if (typeof (i) === "number") {
740
+ O.vRange(i);
741
+ opt = O.E.find('option')[i];
742
+ }
743
+ else {
744
+ opt = O.E.find(`option[value="${i}"]`)[0] || 0;
745
+ }
746
+ if (!opt || opt.disabled)
747
+ return;
748
+
749
+ if (opt.selected !== c) {
750
+ if ((settings.max && !opt.selected && O.selectedCount < settings.max) || opt.selected || (!settings.max && !opt.selected)) {
751
+ opt.selected = c;
752
+ if (!O.mob) $(opt).data('li').toggleClass('selected', c);
753
+
754
+ O.callChange();
755
+ O.setPstate();
756
+ O.setText();
757
+ O.selAllState();
758
+ }
759
+ }
760
+ },
761
+
762
+ //toggles disabled on c as boolean.
763
+ toggDis(c, i) {
764
+ const O = this.vRange(i);
765
+ O.E.find('option')[i].disabled = c;
766
+ if (c) O.E.find('option')[i].selected = false;
767
+ if (!O.mob) O.optDiv.find('ul.options li.opt').eq(i).toggleClass('disabled', c).removeClass('selected');
768
+ O.setText();
769
+ },
770
+
771
+ // toggle disable/enable on complete select control
772
+ toggSumo(val) {
773
+ const O = this;
774
+ O.enabled = val;
775
+ O.select.toggleClass('disabled', val);
776
+
777
+ if (val) {
778
+ O.E.attr('disabled', 'disabled');
779
+ O.select.removeAttr('tabindex');
780
+ }
781
+ else {
782
+ O.E.removeAttr('disabled');
783
+ O.select.attr('tabindex', '0');
784
+ }
785
+
786
+ return O;
787
+ },
788
+
789
+ // toggles all option on c as boolean.
790
+ // set direct=false/0 bypasses okCancelInMulti behaviour.
791
+ toggSelAll(c, direct) {
792
+ const O = this;
793
+ const cloneOriginalEvents = $.extend(true, {}, $._data(O.E.get(0), "events")); // clone original select elements events
794
+ O.E.off(); // unbind original select elements events because we do not want the following clicks to trigger change on it
795
+
796
+ if (O.is_multi) {
797
+ // Select all
798
+ if (c) {
799
+ O.E.find('option').toArray().filter((option) => !option.selected && !option.disabled && option.style.display !== 'none').forEach(option => {
800
+ if (!$(option).data('li').hasClass('hidden')) {
801
+ option.selected = true;
802
+ $(option).data('li').toggleClass('selected', true);
803
+ }
804
+ });
805
+ } else {
806
+ // Unselect all
807
+ O.E.find('option').toArray().filter((option) => option.selected && !option.disabled && option.style.display !== 'none').forEach(option => {
808
+ if (!$(option).data('li').hasClass('hidden')) {
809
+ option.selected = false;
810
+ $(option).data('li').toggleClass('selected', false);
811
+ }
812
+ });
813
+ }
814
+ } else {
815
+ if (!c) O.E[0].selectedIndex = -1;
816
+ else console.warn('You called `SelectAll` on a non-multiple select');
817
+ }
818
+
819
+ // rebind original select elements events
820
+ $.each(cloneOriginalEvents, (_, e) => {
821
+ $.each(e, (__, ev) => {
822
+ O.E.on(ev.type, ev.handler);
823
+ });
824
+ });
825
+
826
+ if ((O.is_multi && !settings.okCancelInMulti) || !O.is_multi) {
827
+ O.callChange(); // call change on original select element
828
+ O.setText();
829
+ }
830
+
831
+ if (!direct) {
832
+ if (!O.mob && O.selAll) O.selAll.removeClass('partial').toggleClass('selected', !!c);
833
+ O.setText();
834
+ O.setPstate();
835
+ }
836
+ },
837
+
838
+ /* outside accessibility options
839
+ which can be accessed from the element instance.
840
+ */
841
+ reload() {
842
+ const elm = this.unload();
843
+ return $(elm).SumoSelect(settings);
844
+ },
845
+
846
+ unload() {
847
+ const O = this;
848
+ O.select.before(O.E);
849
+ O.E.show();
850
+ O.E[0].classList.remove('SumoUnder', 'SelectClass');
851
+
852
+ if (settings.outputAsCSV && O.is_multi && O.select.find('input.HEMANT123').length) {
853
+ O.E.attr('name', O.select.find('input.HEMANT123').attr('name')); // restore the name;
854
+ }
855
+ O.select.remove();
856
+ delete selObj.sumo;
857
+ O.E.trigger('sumo:unloaded', O);
858
+ return selObj;
859
+ },
860
+
861
+ //## add a new option to select at a given index.
862
+ add(val, txt, i, attr) {
863
+ if (typeof val === "undefined") throw new Error("No value to add");
864
+
865
+ const O = this;
866
+ const opts = O.E.find('option');
867
+ const value = val;
868
+ let
869
+ text = txt,
870
+ index = i;
871
+ if (typeof txt === "number") { // .add('xxx', 2) shorthand
872
+ index = txt;
873
+ text = val;
874
+ } else if (typeof txt === "undefined") { // .add('xxx') shorthand
875
+ text = val;
876
+ }
877
+
878
+ const opt = $("<option></option>").val(value).html(text);
879
+
880
+ if (attr && typeof attr === "object") {
881
+ $.each(attr, (j, v) => {
882
+ opt.attr(j, v);
883
+ });
884
+ }
885
+
886
+ if (opts.length < index) throw new Error("index out of bounds");
887
+
888
+ if (typeof index === "undefined" || opts.length === index) { // add it to the last if given index is last no or no index provides.
889
+ O.E.append(opt);
890
+ if (!O.mob) O.ul.append(O.createLi(opt));
891
+ }
892
+ else {
893
+ opts.eq(index).before(opt);
894
+ if (!O.mob) O.ul.find('li.opt').eq(index).before(O.createLi(opt));
895
+ }
896
+
897
+ return selObj;
898
+ },
899
+
900
+ //## removes an item at a given index.
901
+ remove(i) {
902
+ const O = this.vRange(i);
903
+ O.E.find('option').eq(i).remove();
904
+ if (!O.mob) O.optDiv.find('ul.options li.opt').eq(i).remove();
905
+ O.setText();
906
+ },
907
+
908
+ // removes all but the selected one
909
+ removeAll() {
910
+ const O = this;
911
+ const optionList = O.E.find('option');
912
+
913
+ for (let x = (optionList.length - 1); x >= 0; x--) {
914
+ if (optionList[x].selected !== true) {
915
+ O.remove(x);
916
+ }
917
+ }
918
+
919
+ },
920
+
921
+
922
+ find(val) {
923
+ const O = this;
924
+ const optionList = O.E.find('option');
925
+ for (const x in optionList) {
926
+ if (optionList[x].value === val) {
927
+ return +x;
928
+ }
929
+ }
930
+
931
+ return -1;
932
+
933
+ },
934
+
935
+ //## Select an item at a given index.
936
+ selectItem(i) { this.toggSel(true, i); },
937
+
938
+ //## UnSelect an iten at a given index.
939
+ unSelectItem(i) { this.toggSel(false, i); },
940
+
941
+ //## Select all items of the select.
942
+ selectAll() { this.toggSelAll(true); },
943
+
944
+ //## UnSelect all items of the select.
945
+ unSelectAll() { this.toggSelAll(false); },
946
+
947
+ //## Disable an iten at a given index.
948
+ disableItem(i) { this.toggDis(true, i); },
949
+
950
+ //## Removes disabled an iten at a given index.
951
+ enableItem(i) { this.toggDis(false, i); },
952
+
953
+ //## New simple methods as getter and setter are not working fine in ie8-
954
+ //## variable to check state of control if enabled or disabled.
955
+ enabled: true,
956
+ //## Enables the control
957
+ enable() { return this.toggSumo(false); },
958
+
959
+ //## Disables the control
960
+ disable() { return this.toggSumo(true); },
961
+
962
+
963
+ init() {
964
+ const O = this;
965
+ O.createElems();
966
+ O.setText();
967
+ O.E.trigger('sumo:initialized', O);
968
+ return O;
969
+ }
970
+
971
+ };
972
+
973
+ selObj.sumo.init();
974
+ });
975
+
976
+ return ret.length === 1 ? ret[0] : ret;
977
+ };
978
+
979
+
980
+ });