1/* 2 Selectator jQuery Plugin 3 A plugin for select elements 4 version 3.2, Apr 9th, 2020 5 by Ingi á Steinamørk 6 7 The MIT License (MIT) 8 9 Copyright (c) 2013 QODIO 10 11 Permission is hereby granted, free of charge, to any person obtaining a copy of 12 this software and associated documentation files (the "Software"), to deal in 13 the Software without restriction, including without limitation the rights to 14 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 15 the Software, and to permit persons to whom the Software is furnished to do so, 16 subject to the following conditions: 17 18 The above copyright notice and this permission notice shall be included in all 19 copies or substantial portions of the Software. 20 21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 23 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 24 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 25 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27*/ 28 29(function($) { 30 'use strict'; 31 $.selectator = function (_element, _options) { 32 var defaults = { 33 prefix: 'selectator_', 34 height: 'auto', 35 useDimmer: false, 36 useSearch: true, 37 showAllOptionsOnFocus: false, 38 selectFirstOptionOnSearch: true, 39 keepOpen: false, 40 submitCallback: function () {}, 41 load: null, 42 delay: 0, 43 minSearchLength: 0, 44 valueField: 'value', 45 textField: 'text', 46 searchFields: ['value', 'text'], 47 placeholder: '', 48 render: { 49 selected_item: function (_item, escape) { 50 var html = ''; 51 if (typeof _item.left !== 'undefined') 52 html += '<div class="' + self.options.prefix + 'selected_item_left"><img src="' + escape(_item.left) + '" alt=""></div>'; 53 if (typeof _item.right !== 'undefined') 54 html += '<div class="' + self.options.prefix + 'selected_item_right">' + escape(_item.right) + '</div>'; 55 html += '<div class="' + self.options.prefix + 'selected_item_title">' + ((typeof _item.text !== 'undefined') ? escape(_item.text) : '') + '</div>'; 56 if (typeof _item.subtitle !== 'undefined') 57 html += '<div class="' + self.options.prefix + 'selected_item_subtitle">' + escape(_item.subtitle) + '</div>'; 58 html += '<div class="' + self.options.prefix + 'selected_item_remove">X</div>'; 59 return html; 60 }, 61 option: function (_item, escape) { 62 var html = ''; 63 if (typeof _item.left !== 'undefined') 64 html += '<div class="' + self.options.prefix + 'option_left"><img src="' + escape(_item.left) + '" alt=""></div>'; 65 if (typeof _item.right !== 'undefined') 66 html += '<div class="' + self.options.prefix + 'option_right">' + escape(_item.right) + '</div>'; 67 html += '<div class="' + self.options.prefix + 'option_title">' + ((typeof _item.text !== 'undefined') ? escape(_item.text) : '') + '</div>'; 68 if (typeof _item.subtitle !== 'undefined') 69 html += '<div class="' + self.options.prefix + 'option_subtitle">' + escape(_item.subtitle) + '</div>'; 70 return html; 71 } 72 }, 73 labels: { 74 search: 'Search...' 75 } 76 }; 77 78 var self = this; 79 self.options = {}; 80 self.$source_element = $(_element); 81 self.$container_element = null; 82 self.$selecteditems_element = null; 83 self.$input_element = null; 84 self.$textlength_element = null; 85 self.$options_element = null; 86 self.$mask_element = null; 87 self.usefilterResults = true; 88 var is_single = self.$source_element.attr('multiple') === undefined; 89 var is_multiple = !is_single; 90 var using_remote_data = false; 91 var has_visible_options = true; 92 var delayTimer = null; 93 var key = { 94 backspace: 8, 95 tab: 9, 96 enter: 13, 97 shift: 16, 98 ctrl: 17, 99 alt: 18, 100 capslock: 20, 101 escape: 27, 102 pageup: 33, 103 pagedown: 34, 104 end: 35, 105 home: 36, 106 left: 37, 107 up: 38, 108 right: 39, 109 down: 40 110 }; 111 112 113 114 // INITIALIZE PLUGIN 115 self.init = function () { 116 self.options = $.extend(true, {}, defaults, _options); 117 $.each(self.$source_element.data(), function (_key, _value) { 118 if (_key.substring(0, 10) === 'selectator') { 119 self.options[_key.substring(10, 11).toLowerCase() + _key.substring(11)] = _value; 120 } 121 }); 122 self.options.searchFields = typeof self.options.searchFields === 'string' ? self.options.searchFields.split(' ') : self.options.searchFields; 123 self.$source_element.find('option').each(function () { 124 $(this).data('value', this.value); 125 $(this).data('text', this.text); 126 }); 127 using_remote_data = self.options.load !== null; 128 129 //// ================== CREATE ELEMENTS ================== //// 130 // mask 131 self.$mask_element = $('#' + self.options.prefix + 'mask'); 132 if (self.$mask_element.length === 0) { 133 self.$mask_element = $(document.createElement('div')); 134 self.$mask_element.attr('id', self.options.prefix + 'mask'); 135 self.$mask_element.attr('onclick', 'void(0)'); 136 self.$mask_element.on('click', function () { 137 $(':focus').blur(); 138 }); 139 self.$mask_element.hide(); 140 $(document.body).prepend(self.$mask_element); 141 } 142 // source element 143 self.$source_element.addClass('selectator'); 144 if (self.$source_element.attr('placeholder')) { 145 self.options.placeholder = self.$source_element.attr('placeholder'); 146 } 147 // container element 148 self.$container_element = $(document.createElement('div')); 149 if (self.$source_element.attr('id') !== undefined) { 150 self.$container_element.attr('id', self.options.prefix + self.$source_element.attr('id')); 151 } 152 self.$container_element.addClass(self.options.prefix + 'element ' + (is_multiple ? 'multiple ' : 'single ') + 'options-hidden'); 153 if (!self.options.useSearch) { 154 self.$container_element.addClass('disable_search'); 155 } 156 self.$container_element.css({ 157 width: self.$source_element.css('width'), 158 minHeight: self.$source_element.css('height'), 159 padding: self.$source_element.css('padding'), 160 'flex-grow': self.$source_element.css('flex-grow'), 161 position: 'relative' 162 }); 163 if (self.options.height === 'element') { 164 self.$container_element.css({ 165 height: self.$source_element.outerHeight() + 'px' 166 }); 167 } 168 // textlength element 169 self.$textlength_element = $(document.createElement('span')); 170 self.$textlength_element.addClass(self.options.prefix + 'textlength'); 171 self.$textlength_element.css({ 172 position: 'absolute', 173 visibility: 'hidden' 174 }); 175 self.$container_element.append(self.$textlength_element); 176 // selected items element 177 self.$selecteditems_element = $(document.createElement('div')); 178 self.$selecteditems_element.addClass(self.options.prefix + 'selected_items'); 179 self.$container_element.append(self.$selecteditems_element); 180 // input element 181 self.$input_element = $(document.createElement('input')); 182 self.$input_element.addClass(self.options.prefix + 'input'); 183 self.$input_element.attr('tabindex', self.$source_element.attr('tabindex')); 184 if (!self.options.useSearch) { 185 self.$input_element.attr('readonly', true); 186 self.$input_element.css({ 187 'width': '0px', 188 'height': '0px', 189 'overflow': 'hidden', 190 'border': 0, 191 'padding': 0, 192 'position': 'absolute' 193 }); 194 } else { 195 if (is_single) { 196 self.$input_element.attr('placeholder', self.options.labels.search); 197 } else { 198 if (self.options.placeholder !== '') { 199 self.$input_element.attr('placeholder', self.options.placeholder); 200 } 201 self.$input_element.width(20); 202 } 203 } 204 self.$input_element.attr('autocomplete', 'false'); 205 self.$container_element.append(self.$input_element); 206 // options element 207 self.$options_element = $(document.createElement('ul'));
208 self.$options_element.addClass(self.options.prefix + 'options'); 209 210 self.$container_element.append(self.$options_element); 211 self.$source_element.after(self.$container_element); 212 self.$source_element.hide(); 213 214 215 //// ================== BIND ELEMENTS EVENTS ================== //// 216 217 // source element 218 self.$source_element.change(function () { 219 renderSelectedItems(); 220 }); 221 222 // container element 223 self.$container_element.on('focus', function () { 224 self.$input_element.focus(); 225 self.$input_element.trigger('focus'); 226 }); 227 self.$container_element.on('mousedown', function (_e) { 228 _e.preventDefault(); 229 self.$input_element.focus(); 230 self.$input_element.trigger('focus'); 231 // put text caret to end of search field 232 if (self.$input_element[0].setSelectionRange) { 233 self.$input_element[0].setSelectionRange(self.$input_element.val().length, self.$input_element.val().length); 234 } else if (self.$input_element[0].createTextRange) { 235 var range = self.$input_element[0].createTextRange(); 236 range.collapse(true); 237 range.moveEnd('character', self.$input_element.val().length); 238 range.moveStart('character', self.$input_element.val().length); 239 range.select(); 240 } 241 }); 242 self.$container_element.on('click', function () { 243 self.$input_element.focus(); 244 self.$input_element.trigger('focus'); 245 }); 246 self.$container_element.on('dblclick', function () { 247 self.$input_element.select(); 248 self.$input_element.trigger('focus'); 249 }); 250 251 // input element 252 self.$input_element.on('keydown', function (_e) { 253 var keyCode = _e.keyCode || _e.which; 254 var $active = null; 255 var $newActive = null; 256 switch (keyCode) { 257 case key.up: 258 _e.preventDefault(); 259 showDropdown(); 260 $active = self.$options_element.find('.active'); 261 if ($active.length !== 0) { 262 $newActive = $active.prevUntil('.' + self.options.prefix + 'option:visible').add($active).first().prev('.' + self.options.prefix + 'option:visible'); 263 $active.removeClass('active'); 264 $newActive.addClass('active'); 265 } else { 266 self.$options_element.find('.' + self.options.prefix + 'option').filter(':visible').last().addClass('active'); 267 } 268 scrollToActiveOption(); 269 break; 270 case key.down: 271 _e.preventDefault(); 272 showDropdown(); 273 $active = self.$options_element.find('.active'); 274 if ($active.length !== 0) { 275 $newActive = $active.nextUntil('.' + self.options.prefix + 'option:visible').add($active).last().next('.' + self.options.prefix + 'option:visible'); 276 $active.removeClass('active'); 277 $newActive.addClass('active'); 278 } else { 279 self.$options_element.find('.' + self.options.prefix + 'option').filter(':visible').first().addClass('active'); 280 } 281 scrollToActiveOption(); 282 break; 283 case key.escape: 284 _e.preventDefault(); 285 break; 286 case key.enter: 287 _e.preventDefault(); 288 $active = self.$options_element.find('.active'); 289 if ($active.length !== 0) { 290 selectOption(); 291 } else { 292 if (self.$input_element.val() !== '') { 293 self.options.submitCallback(self.$input_element.val()); 294 } 295 } 296 resizeSearchInput(); 297 break; 298 case key.backspace: 299 if (self.options.useSearch) { 300 if (self.$input_element.val() === '' && is_multiple && self.$source_element.find('option:selected').length) { 301 var lastSelectedItem = self.$source_element.find('option:selected').last()[0]; 302 lastSelectedItem.removeAttribute('selected'); 303 lastSelectedItem.selected = false; 304 self.$source_element.trigger('change'); 305 renderSelectedItems(); 306 } 307 resizeSearchInput(); 308 } else { 309 _e.preventDefault(); 310 } 311 break; 312 default: 313 resizeSearchInput(); 314 break; 315 } 316 }); 317 self.$input_element.on('keyup', function (_e) { 318 _e.preventDefault(); 319 _e.stopPropagation(); 320 var keyCode = _e.which; 321 switch (keyCode) { 322 case key.escape: 323 hideDropdown(); 324 break; 325 case key.enter: 326 if (!self.options.keepOpen) { 327 hideDropdown(); 328 } 329 break; 330 case key.left: 331 case key.right: 332 case key.up: 333 case key.down: 334 case key.tab: 335 case key.shift: 336 // Prevent any action 337 break; 338 default: 339 load(); 340 break; 341 } 342 if (self.$container_element.hasClass('options-hidden') && (keyCode === key.left || keyCode === key.right ||
342keyCode === key.up || keyCode === key.down)) { 343 showDropdown(); 344 } 345 resizeSearchInput(); 346 }); 347 self.$input_element.on('focus', function () { 348 self.$container_element.addClass('focused'); 349 if (is_single || self.options.showAllOptionsOnFocus || !self.options.useSearch) { 350 if (!self.options.useSearch && using_remote_data) { 351 load() 352 } 353 showDropdown(); 354 } 355 }); 356 self.$input_element.on('blur', function () { 357 self.$container_element.removeClass('focused'); 358 hideDropdown(); 359 }); 360 361 // bind selected item events 362 self.$container_element.on('mousedown', '.' + self.options.prefix + 'selected_item_remove', function () { 363 var source_item_element = $(this).closest('.' + self.options.prefix + 'selected_item').data('source_item_element'); 364 source_item_element.removeAttribute('selected'); 365 source_item_element.selected = false; 366 if (is_single && self.$source_element.find('[value=""]').length) { 367 var noValueOption = self.$source_element.find('[value=""]')[0]; 368 noValueOption.setAttribute('selected', ''); 369 noValueOption.selected = true; 370 } 371 self.$source_element.trigger('change'); 372 filterResults(self.usefilterResults); 373 renderOptions(); 374 renderSelectedItems(); 375 }); 376 377 // bind option events 378 self.$container_element.on('mouseover', '.' + self.options.prefix + 'option', function () { 379 var $active = self.$options_element.find('.active'); 380 $active.removeClass('active'); 381 var $this = $(this); 382 $this.addClass('active'); 383 }); 384 self.$container_element.on('mousedown', '.' + self.options.prefix + 'option', function (_e) { 385 _e.preventDefault(); 386 _e.stopPropagation(); 387 }); 388 self.$container_element.on('mouseup', '.' + self.options.prefix + 'option', function () { 389 selectOption(); 390 }); 391 self.$container_element.on('click', '.' + self.options.prefix + 'option', function (_e) { 392 _e.stopPropagation(); 393 var $active = self.$options_element.find('.active'); 394 $active.removeClass('active'); 395 var $this = $(this); 396 $this.addClass('active'); 397 }); 398 399 // Make elements accesible from options 400 self.options.$source_element = self.$source_element; 401 self.options.$container_element = self.$container_element; 402 self.options.$selecteditems_element = self.$selecteditems_element; 403 self.options.$input_element = self.$input_element; 404 self.options.$textlength_element = self.$textlength_element; 405 self.options.$options_element = self.$options_element; 406 407 // Render 408 renderOptions(); 409 renderSelectedItems(); 410 resizeSearchInput(); 411 }; 412 413 414 // RESIZE INPUT 415 var resizeSearchInput = function () { 416 if (is_multiple) { 417 self.$textlength_element.text(self.$input_element.val() === '' && self.options.placeholder !== '' ? self.options.placeholder : self.$input_element.val()); 418 var width = self.$textlength_element.width() > (self.$container_element.width() - 30) ? (self.$container_element.width() - 30) : (self.$textlength_element.width() + 30); 419 self.$input_element.css({ width: width + 'px' }); 420 } 421 }; 422 423 424 // RENDER SELECTED ITEMS 425 var renderSelectedItems = function () { 426 self.$selecteditems_element.empty(); 427 self.$source_element.find('option').each(function () { 428 var $option = $(this); 429 if (this.selected) { 430 var $item_element = $(document.createElement('div')); 431 $item_element.data('source_item_element', this); 432 $item_element.addClass(self.options.prefix + 'selected_item'); 433 $item_element.addClass(self.options.prefix + 'value_' + $option.val().replace(/\W/g, '')); 434 if ($option.attr('class') !== undefined) { 435 $item_element.addClass($option.attr('class')); 436 } 437 438 // fetch data 439 var data = { 440 value: this.value, 441 text: this.text 442 }; 443 $.each(this.attributes, function() { 444 if(this.specified) { 445 data[this.name.replace('data-', '')] = this.value; 446 } 447 }); 448 $.extend(data, $(this).data('item_data')); 449 $item_element.append(self.options.render.selected_item(data, escape)); 450 if (is_single && (data[self.options.valueField] === '' || typeof data[self.options.valueField] === 'undefined' || self.$source_element.find('[value=""]').length === 0)) { 451 $item_element.find('.' + self.options.prefix + 'selected_item_remove').remove(); 452 } 453 self.$selecteditems_element.append($item_element); 454 } 455 }); 456 if (is_single) { 457 if (self.options.placeholder !== '' && (self.$source_element.val() === '' || self.$source_element.val() === null)) { 458 self.$selecteditems_element.empty(); 459 self.$selecteditems_element.append('<div class="' + self.options.prefix + 'placeholder">' + self.options.placeholder + '</div>'); 460 } else { 461 self.$selecteditems_element.find('.' + self.options.prefix + 'placeholder').remove(); 462 } 463 } 464 }; 465 466 467 // RENDER OPTIONS 468 var renderOptions = function () { 469 self.$options_element.empty(); 470 var optionsArray = []; 471 self.$source_element.children().each(function () { 472 if ($(this).prop('tagName').toLowerCase() === 'optgroup') { 473 var $group = $(this); 474 if ($group.children('option').length !== 0) { 475 var groupOptionsArray = []; 476 $group.children('option').each(function () { 477 groupOptionsArray.push({ 478 type: 'option', 479 text: $(this).html(), 480 element: this 481 });
482 }); 483 optionsArray.push({ 484 type: 'group', 485 text: $group.attr('label'), 486 options: groupOptionsArray, 487 element: $group 488 }); 489 } 490 } else { 491 optionsArray.push({ 492 type: 'option', 493 text: $(this).html(), 494 element: this 495 }); 496 } 497 }); 498 499 $(optionsArray).each(function () { 500 if (this.type === 'group') { 501 var $group_element = $(document.createElement('li')); 502 $group_element.addClass(self.options.prefix + 'group'); 503 if ($(this.element).attr('class') !== undefined) { 504 $group_element.addClass($(this.element).attr('class')); 505 } 506 $group_element.html($(this.element).attr('label')); 507 self.$options_element.append($group_element); 508 509 $(this.options).each(function () { 510 var option = createOption.call(this.element, true); 511 self.$options_element.append(option); 512 }); 513 514 } else { 515 var option = createOption.call(this.element, false); 516 self.$options_element.append(option); 517 } 518 }); 519 filterResults(self.usefilterResults); 520 }; 521 522 523 // CREATE RESULT OPTION 524 var createOption = function (_isGroupOption) { 525 // holder li 526 var $option = $(document.createElement('li')); 527 $option.data('source_option_element', this); 528 $option.attr('onclick', 'void(0)'); 529 $option.addClass(self.options.prefix + 'option'); 530 $option.addClass(self.options.prefix + 'value_' + $(this).val().replace(/\W/g, '')); 531 if (_isGroupOption) { 532 $option.addClass(self.options.prefix + 'group_option'); 533 } 534 if (this.selected) { 535 $option.addClass('active'); 536 } 537 // class 538 if ($(this).attr('class') !== undefined) { 539 $option.addClass($(this).attr('class')); 540 } 541 // fetch data 542 var data = { 543 value: this.value, 544 text : this.text 545 }; 546 $.each(this.attributes, function() { 547 if(this.specified) { 548 data[this.name.replace('data-', '')] = this.value; 549 } 550 }); 551 $.extend(data, $(this).data('item_data')); 552 if (is_multiple && this.selected) { 553 $option.hide(); 554 } 555 556 $option.append(self.options.render.option(data, escape)); 557 558 return $option; 559 }; 560 561 562 // LOAD SEARCH RESULTS (IF NEEDED) 563 var load = function () { 564 clearTimeout(delayTimer); 565 delayTimer = setTimeout(function () { 566 self.$container_element.addClass('loading'); 567 if (using_remote_data) { 568 self.options.load(self.$input_element.val(), function (results, usefilterResults) { 569 self.usefilterResults = typeof usefilterResults !== 'undefined' ? usefilterResults : false; 570 self.$source_element.children('option').not(':selected').not('[value=""]').remove(); 571 if (typeof results !== 'undefined') { 572 var selectedOptions = []; 573 $.each(self.$source_element.children('option:selected'), function (_key, _option) { 574 selectedOptions.push(_option.value); 575 }); 576 if (is_single && self.$source_element.find('[value=""]').length === 0) { 577 self.$source_element.prepend($('<option value=""> </option>')); 578 } 579 if (self.$input_element.val().replace(/\s/g, '').length >= self.options.minSearchLength) { 580 for (var i = 0; i < results.length; i++) { 581 var result = results[i]; 582 if ($.inArray(result[self.options.valueField] + "", selectedOptions) === -1) { 583 var $option = $('<option value="' + result[self.options.valueField] + '">' + result[self.options.textField] + '</option>'); 584 self.$source_element.append($option); 585 $option.data('item_data', result); 586 } 587 } 588 } 589 } 590 renderOptions(); 591 self.$container_element.removeClass('loading'); 592 filterResults(self.usefilterResults); 593 }); 594 } else { 595 self.$container_element.removeClass('loading'); 596 filterResults(self.usefilterResults); 597 } 598 }, self.options.delay); 599 }; 600 601 602 // FILTER SEARCH RESULTS 603 var filterResults = function (usefilterResults) { 604 usefilterResults = typeof usefilterResults !== 'undefined' ? usefilterResults : false;
605 // bool true if search field is below required length 606 var searchIsBelowRequired = self.$input_element.val().replace(/\s/g, '').length < self.options.minSearchLength; 607 // bool true if any options are visible 608 has_visible_options = false; 609 // get sanitized search text 610 var searchFor = self.$input_element.val().toLowerCase(); 611 // iterate through the options 612 self.$options_element.find('.' + self.options.prefix + 'option').each(function () { 613 var $this = $(this); 614 var source_option_element = $this.data('source_option_element'); 615 var $source_option_element = $(source_option_element); 616 617 // SHOW IF: 618 // -------------------------------------------- 619 // NOT using filter results 620 // AND option is NOT selected 621 // OR 622 // using filter results 623 // AND 624 // option is NOT selected 625 // OR single select 626 // AND 627 // use search 628 // AND 629 // searchtext is below required 630 // OR option text matches searchtext 631 // OR option value is empty 632 // OR NOT using search 633 var match_found = false; 634 $.each(self.options.searchFields, function (key, value) { 635 if (typeof $source_option_element.data(value) !== 'undefined' && $source_option_element.data(value).toString().toLowerCase().indexOf(searchFor) !== -1) { 636 match_found = true; 637 return false; 638 } 639 }); 640 if ( 641 (!usefilterResults 642 && !source_option_element.selected 643 ) 644 || (usefilterResults 645 && ( 646 !source_option_element.selected 647 || is_single 648 ) 649 && ( 650 self.options.useSearch 651 && ( 652 searchIsBelowRequired 653 || match_found 654 || $source_option_element.val() === '' 655 ) 656 || !self.options.useSearch 657 ) 658 ) 659 ) { 660 $this.show(); 661 has_visible_options = !has_visible_options ? true : has_visible_options; 662 } else { 663 $this.hide(); 664 } 665 }); 666 // iterate through the groups 667 self.$options_element.find('.' + self.options.prefix + 'group').each(function () { 668 var $this = $(this); 669 var has_visible_options = false; 670 $this.nextUntil('.' + self.options.prefix + 'group').each(function () { 671 var $option = $(this); 672 if ($option.css('display') !== 'none') { 673 has_visible_options = true; 674 return false; 675 } 676 }); 677 // show if the group has any visible children 678 if (has_visible_options) { 679 $this.show(); 680 } else { 681 $this.hide(); 682 } 683 }); 684 showDropdown(); 685 if (is_multiple) { 686 self.$options_element.find('.active').removeClass('active'); 687 if (!searchIsBelowRequired) { 688 self.$options_element.find('.' + self.options.prefix + 'option').filter(':visible').first().addClass('active'); 689 } 690 } 691 }; 692 693 694 // SHOW OPTIONS AND MASK 695 var showDropdown = function () { 696 if (self.$input_element.is(':focus') && (has_visible_options || is_single ) && !(self.$options_element.is(':empty') && !self.options.useSearch)) { 697 self.$container_element.removeClass('options-hidden').addClass('options-visible'); 698 self.$mask_element.show(); 699 if (self.options.useDimmer) { 700 self.$mask_element.addClass(self.options.prefix + 'mask_dimmed'); 701 } else { 702 self.$mask_element.removeClass(self.options.prefix + 'mask_dimmed'); 703 } 704 setTimeout(function () { 705 self.$options_element.css('top', (self.$container_element.outerHeight() + (is_multiple ? 0 : self.$input_element.outerHeight()) - 1) + 'px'); 706 }, 1); 707 scrollToActiveOption(); 708 } else { 709 hideDropdown(); 710 } 711 }; 712 713 714 // HIDE OPTIONS AND MASK 715 var hideDropdown = function () { 716 self.$container_element.removeClass('options-visible').addClass('options-hidden'); 717 self.$mask_element.removeClass(self.options.prefix + 'mask_dimmed'); 718 self.$mask_element.hide(); 719 }; 720 721 722 // SCROLL TO ACTIVE OPTION IN OPTIONS LIST 723 var scrollToActiveOption = function () { 724 var $active_element = self.$options_element.find('.' + self.options.prefix + 'option.active'); 725 if ($active_element.length > 0) { 726 self.$options_element.scrollTop(self.$options_element.scrollTop() + $active_element.position().top - self.$options_element.height()/2 + $active_element.height()/2); 727 } 728 }; 729 730 731 // SELECT ACTIVE OPTION 732 var selectOption = function () { 733 // select option 734 if (is_single) { 735 var lastSelectedItem = self.$source_element.find('option:selected').last()[0]; 736 lastSelectedItem.removeAttribute('selected'); 737 lastSelectedItem.selected = false; 738 } 739 var $active = self.$options_element.find('.active'); 740 $active.data('source_option_element').setAttribute('selected', ''); 741 $active.data('source_option_element').selected = true; 742 self.$source_element.trigger('change'); 743 if (!self.options.keepOpen) { 744 self.$input_element.val(''); 745 } 746 filterResults(self.usefilterResults); 747 renderSelectedItems();
748 resizeSearchInput(); 749 if (using_remote_data && !self.options.keepOpen) { 750 self.$source_element.children('option').not(':selected').not('[value=""]').remove(); 751 renderOptions(); 752 hideDropdown(); 753 } 754 if (!self.options.keepOpen) { 755 hideDropdown(); 756 } 757 }; 758 759 760 // ESCAPE FUNCTION 761 var escape = function(str) { 762 return (str + '') 763 .replace(/&/g, '&') 764 .replace(/</g, '<') 765 .replace(/>/g, '>') 766 .replace(/"/g, '"'); 767 }; 768 769 770 // REFRESH PLUGIN 771 self.refresh = function () { 772 renderSelectedItems(); 773 }; 774 775 776 // REMOVE PLUGIN AND REVERT SELECT ELEMENT TO ORIGINAL STATE 777 self.destroy = function () { 778 self.$container_element.remove(); 779 $.removeData(_element, 'selectator'); 780 self.$source_element.show(); 781 if ($('.' + self.options.prefix + 'element').length === 0) { 782 self.$mask_element.remove(); 783 } 784 }; 785 786 787 // Initialize plugin 788 self.init(); 789 }; 790 791 // Initializer 792 $.fn.selectator = function(_options) { 793 var options = _options !== undefined ? _options : {}; 794 return this.each(function () { 795 var $this = $(this); 796 if (typeof(options) === 'object') { 797 if ($this.data('selectator') === undefined) { 798 var self = new $.selectator(this, options); 799 $this.data('selectator', self); 800 } 801 } else if ($this.data('selectator')[options]) { 802 $this.data('selectator')[options].apply(this, Array.prototype.slice.call(arguments, 1)); 803 } else { 804 $.error('Method ' + options + ' does not exist in $.selectator'); 805 } 806 }); 807 }; 808}(jQuery)); 809 810// Markup initializer 811$(function () { 812 'use strict'; 813 $('.selectator').each(function () { 814 var $this = $(this); 815 var options = {}; 816 $.each($this.data(), function (_key, _value) { 817 if (_key.substring(0, 10) === 'selectator') { 818 options[_key.substring(10, 11).toLowerCase() + _key.substring(11)] = _value; 819 } 820 }); 821 $this.selectator(options); 822 }); 823});
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.