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