1*87e82791SAdam Hornacek/* 2*87e82791SAdam Hornacek * SOL - Searchable Option List jQuery plugin 3*87e82791SAdam Hornacek * Version 2.0.2 4*87e82791SAdam Hornacek * https://pbauerochse.github.io/searchable-option-list/ 5*87e82791SAdam Hornacek * 6*87e82791SAdam Hornacek * Copyright 2015, Patrick Bauerochse 7*87e82791SAdam Hornacek * Portions Copyright (c) 2020, Chris Fraire <cfraire@me.com>. 8*87e82791SAdam Hornacek * 9*87e82791SAdam Hornacek * Licensed under the MIT license: 10*87e82791SAdam Hornacek * http://www.opensource.org/licenses/MIT 11*87e82791SAdam Hornacek * 12*87e82791SAdam Hornacek */ 13*87e82791SAdam Hornacek 14*87e82791SAdam Hornacek/* 15*87e82791SAdam Hornacek * Original based on SOL v2.0.2 16*87e82791SAdam Hornacek * Modified by Krystof Tulinger for OpenGrok in 2016. 17*87e82791SAdam Hornacek */ 18*87e82791SAdam Hornacek 19*87e82791SAdam Hornacek/*jslint nomen: true */ 20*87e82791SAdam Hornacek; 21*87e82791SAdam Hornacek(function ($, window, document) { 22*87e82791SAdam Hornacek 'use strict'; 23*87e82791SAdam Hornacek 24*87e82791SAdam Hornacek // constructor 25*87e82791SAdam Hornacek var SearchableOptionList = function ($element, options) { 26*87e82791SAdam Hornacek this.$originalElement = $element; 27*87e82791SAdam Hornacek this.options = options; 28*87e82791SAdam Hornacek 29*87e82791SAdam Hornacek // allow setting options as data attribute 30*87e82791SAdam Hornacek // e.g. <select data-sol-options="{'allowNullSelection':true}"> 31*87e82791SAdam Hornacek this.metadata = this.$originalElement.data('sol-options'); 32*87e82791SAdam Hornacek }; 33*87e82791SAdam Hornacek 34*87e82791SAdam Hornacek // plugin prototype 35*87e82791SAdam Hornacek SearchableOptionList.prototype = { 36*87e82791SAdam Hornacek 37*87e82791SAdam Hornacek SOL_OPTION_FORMAT: { 38*87e82791SAdam Hornacek type: 'option', // fixed 39*87e82791SAdam Hornacek value: undefined, // value that will be submitted 40*87e82791SAdam Hornacek selected: false, // boolean selected state 41*87e82791SAdam Hornacek disabled: false, // boolean disabled state 42*87e82791SAdam Hornacek label: undefined, // label string 43*87e82791SAdam Hornacek tooltip: undefined, // tooltip string 44*87e82791SAdam Hornacek cssClass: '' // custom css class for container 45*87e82791SAdam Hornacek }, 46*87e82791SAdam Hornacek SOL_OPTIONGROUP_FORMAT: { 47*87e82791SAdam Hornacek type: 'optiongroup', // fixed 48*87e82791SAdam Hornacek label: undefined, // label string 49*87e82791SAdam Hornacek tooltip: undefined, // tooltip string 50*87e82791SAdam Hornacek disabled: false, // all children disabled boolean property 51*87e82791SAdam Hornacek children: undefined // array of SOL_OPTION_FORMAT objects 52*87e82791SAdam Hornacek }, 53*87e82791SAdam Hornacek 54*87e82791SAdam Hornacek DATA_KEY: 'sol-element', 55*87e82791SAdam Hornacek WINDOW_EVENTS_KEY: 'sol-window-events', 56*87e82791SAdam Hornacek 57*87e82791SAdam Hornacek // default option values 58*87e82791SAdam Hornacek defaults: { 59*87e82791SAdam Hornacek data: undefined, 60*87e82791SAdam Hornacek name: undefined, // name attribute, can also be set as name="" attribute on original element or data-sol-name="" 61*87e82791SAdam Hornacek 62*87e82791SAdam Hornacek texts: { 63*87e82791SAdam Hornacek noItemsAvailable: 'No entries found', 64*87e82791SAdam Hornacek selectAll: 'Select all', 65*87e82791SAdam Hornacek selectNone: 'Select none', 66*87e82791SAdam Hornacek quickDelete: '×', 67*87e82791SAdam Hornacek searchplaceholder: 'Click here to search', 68*87e82791SAdam Hornacek loadingData: 'Still loading data...', 69*87e82791SAdam Hornacek /* 70*87e82791SAdam Hornacek * Modified for OpenGrok in 2016. 71*87e82791SAdam Hornacek */ 72*87e82791SAdam Hornacek itemsSelected: '{$a} more items selected' 73*87e82791SAdam Hornacek }, 74*87e82791SAdam Hornacek 75*87e82791SAdam Hornacek events: { 76*87e82791SAdam Hornacek onInitialized: undefined, 77*87e82791SAdam Hornacek onRendered: undefined, 78*87e82791SAdam Hornacek onOpen: undefined, 79*87e82791SAdam Hornacek onClose: undefined, 80*87e82791SAdam Hornacek onChange: undefined, 81*87e82791SAdam Hornacek onScroll: function () { 82*87e82791SAdam Hornacek 83*87e82791SAdam Hornacek var selectionContainerYPos = this.$input.offset().top - this.config.scrollTarget.scrollTop() + this.$input.outerHeight(false), 84*87e82791SAdam Hornacek selectionContainerHeight = this.$selectionContainer.outerHeight(false), 85*87e82791SAdam Hornacek selectionContainerBottom = selectionContainerYPos + selectionContainerHeight, 86*87e82791SAdam Hornacek displayContainerAboveInput = this.config.displayContainerAboveInput || document.documentElement.clientHeight - this.config.scrollTarget.scrollTop() < selectionContainerBottom, 87*87e82791SAdam Hornacek selectionContainerWidth = this.$innerContainer.outerWidth(false) - parseInt(this.$selectionContainer.css('border-left-width'), 10) - parseInt(this.$selectionContainer.css('border-right-width'), 10); 88*87e82791SAdam Hornacek 89*87e82791SAdam Hornacek if (displayContainerAboveInput) { 90*87e82791SAdam Hornacek // position the popup above the input 91*87e82791SAdam Hornacek selectionContainerYPos = this.$input.offset().top - selectionContainerHeight - this.config.scrollTarget.scrollTop() + parseInt(this.$selectionContainer.css('border-bottom-width'), 10); 92*87e82791SAdam Hornacek this.$container 93*87e82791SAdam Hornacek .removeClass('sol-selection-bottom') 94*87e82791SAdam Hornacek .addClass('sol-selection-top'); 95*87e82791SAdam Hornacek } else { 96*87e82791SAdam Hornacek this.$container 97*87e82791SAdam Hornacek .removeClass('sol-selection-top') 98*87e82791SAdam Hornacek .addClass('sol-selection-bottom'); 99*87e82791SAdam Hornacek } 100*87e82791SAdam Hornacek 101*87e82791SAdam Hornacek if (this.$innerContainer.css('display') !== 'block') { 102*87e82791SAdam Hornacek // container has a certain width 103*87e82791SAdam Hornacek // make selection container a bit wider 104*87e82791SAdam Hornacek selectionContainerWidth = selectionContainerWidth * 1.2; 105*87e82791SAdam Hornacek } else { 106*87e82791SAdam Hornacek 107*87e82791SAdam Hornacek var borderRadiusSelector = displayContainerAboveInput ? 'border-bottom-right-radius' : 'border-top-right-radius'; 108*87e82791SAdam Hornacek 109*87e82791SAdam Hornacek // no border radius on top 110*87e82791SAdam Hornacek this.$selectionContainer 111*87e82791SAdam Hornacek .css(borderRadiusSelector, 'initial'); 112*87e82791SAdam Hornacek 113*87e82791SAdam Hornacek if (this.$actionButtons) { 114*87e82791SAdam Hornacek this.$actionButtons 115*87e82791SAdam Hornacek .css(borderRadiusSelector, 'initial'); 116*87e82791SAdam Hornacek } 117*87e82791SAdam Hornacek } 118*87e82791SAdam Hornacek 119*87e82791SAdam Hornacek this.$selectionContainer 120*87e82791SAdam Hornacek .css('top', Math.floor(selectionContainerYPos)) 121*87e82791SAdam Hornacek .css('left', Math.floor(this.$container.offset().left)) 122*87e82791SAdam Hornacek .css('width', selectionContainerWidth); 123*87e82791SAdam Hornacek 124*87e82791SAdam Hornacek // remember the position 125*87e82791SAdam Hornacek this.config.displayContainerAboveInput = displayContainerAboveInput; 126*87e82791SAdam Hornacek } 127*87e82791SAdam Hornacek }, 128*87e82791SAdam Hornacek 129*87e82791SAdam Hornacek selectAllMaxItemsThreshold: 30, 130*87e82791SAdam Hornacek showSelectAll: function () { 131*87e82791SAdam Hornacek return this.config.multiple && this.config.selectAllMaxItemsThreshold && this.items && this.items.length <= this.config.selectAllMaxItemsThreshold; 132*87e82791SAdam Hornacek }, 133*87e82791SAdam Hornacek 134*87e82791SAdam Hornacek useBracketParameters: false, 135*87e82791SAdam Hornacek multiple: undefined, 136*87e82791SAdam Hornacek /* 137*87e82791SAdam Hornacek * Modified for OpenGrok in 2016. 138*87e82791SAdam Hornacek */ 139*87e82791SAdam Hornacek resultsContainer: undefined, 140*87e82791SAdam Hornacek closeOnClick: false, 141*87e82791SAdam Hornacek showSelectionBelowList: false, 142*87e82791SAdam Hornacek allowNullSelection: false, 143*87e82791SAdam Hornacek scrollTarget: undefined, 144*87e82791SAdam Hornacek maxHeight: undefined, 145*87e82791SAdam Hornacek converter: undefined, 146*87e82791SAdam Hornacek asyncBatchSize: 300, 147*87e82791SAdam Hornacek searchTimeout: 300, 148*87e82791SAdam Hornacek maxShow: 0 149*87e82791SAdam Hornacek }, 150*87e82791SAdam Hornacek 151*87e82791SAdam Hornacek // initialize the plugin 152*87e82791SAdam Hornacek init: function () { 153*87e82791SAdam Hornacek this.numSelected = 0; 154*87e82791SAdam Hornacek this.valMap = null; 155*87e82791SAdam Hornacek this.config = $.extend(true, {}, this.defaults, this.options, this.metadata); 156*87e82791SAdam Hornacek 157*87e82791SAdam Hornacek var originalName = this._getNameAttribute(), 158*87e82791SAdam Hornacek sol = this; 159*87e82791SAdam Hornacek 160*87e82791SAdam Hornacek if (!originalName) { 161*87e82791SAdam Hornacek this._showErrorLabel('name attribute is required'); 162*87e82791SAdam Hornacek return; 163*87e82791SAdam Hornacek } 164*87e82791SAdam Hornacek 165*87e82791SAdam Hornacek // old IE does not support trim 166*87e82791SAdam Hornacek if (typeof String.prototype.trim !== 'function') { 167*87e82791SAdam Hornacek String.prototype.trim = function () { 168*87e82791SAdam Hornacek return this.replace(/^\s+|\s+$/g, ''); 169*87e82791SAdam Hornacek } 170*87e82791SAdam Hornacek } 171*87e82791SAdam Hornacek 172*87e82791SAdam Hornacek this.config.multiple = this.config.multiple || this.$originalElement.attr('multiple'); 173*87e82791SAdam Hornacek 174*87e82791SAdam Hornacek if (!this.config.scrollTarget) { 175*87e82791SAdam Hornacek this.config.scrollTarget = $(window); 176*87e82791SAdam Hornacek } 177*87e82791SAdam Hornacek 178*87e82791SAdam Hornacek this._registerWindowEventsIfNecessary(); 179*87e82791SAdam Hornacek this._initializeUiElements(); 180*87e82791SAdam Hornacek this._initializeInputEvents(); 181*87e82791SAdam Hornacek 182*87e82791SAdam Hornacek setTimeout(function () { 183*87e82791SAdam Hornacek sol._initializeData(); 184*87e82791SAdam Hornacek 185*87e82791SAdam Hornacek // take original form element out of form submission 186*87e82791SAdam Hornacek // by removing the name attribute 187*87e82791SAdam Hornacek sol.$originalElement 188*87e82791SAdam Hornacek .data(sol.DATA_KEY, sol) 189*87e82791SAdam Hornacek .removeAttr('name') 190*87e82791SAdam Hornacek .data('sol-name', originalName); 191*87e82791SAdam Hornacek }, 0); 192*87e82791SAdam Hornacek 193*87e82791SAdam Hornacek this.$originalElement.hide(); 194*87e82791SAdam Hornacek this.$container 195*87e82791SAdam Hornacek .css('visibility', 'initial') 196*87e82791SAdam Hornacek .show(); 197*87e82791SAdam Hornacek 198*87e82791SAdam Hornacek return this; 199*87e82791SAdam Hornacek }, 200*87e82791SAdam Hornacek 201*87e82791SAdam Hornacek _getNameAttribute: function () { 202*87e82791SAdam Hornacek return this.config.name || this.$originalElement.data('sol-name') || this.$originalElement.attr('name'); 203*87e82791SAdam Hornacek }, 204*87e82791SAdam Hornacek 205*87e82791SAdam Hornacek // shows an error label 206*87e82791SAdam Hornacek _showErrorLabel: function (message) { 207*87e82791SAdam Hornacek var $errorMessage = $('<div style="color: red; font-weight: bold;" />').html(message); 208*87e82791SAdam Hornacek if (!this.$container) { 209*87e82791SAdam Hornacek $errorMessage.insertAfter(this.$originalElement); 210*87e82791SAdam Hornacek } else { 211*87e82791SAdam Hornacek this.$container.append($errorMessage); 212*87e82791SAdam Hornacek } 213*87e82791SAdam Hornacek }, 214*87e82791SAdam Hornacek 215*87e82791SAdam Hornacek // register click handler to determine when to trigger the close event 216*87e82791SAdam Hornacek _registerWindowEventsIfNecessary: function () { 217*87e82791SAdam Hornacek if (!window[this.WINDOW_EVENTS_KEY]) { 218*87e82791SAdam Hornacek $(document).click(function (event) { 219*87e82791SAdam Hornacek // if clicked inside a sol element close all others 220*87e82791SAdam Hornacek // else close all sol containers 221*87e82791SAdam Hornacek 222*87e82791SAdam Hornacek var $clickedElement = $(event.target), 223*87e82791SAdam Hornacek $closestSelectionContainer = $clickedElement.closest('.sol-selection-container'), 224*87e82791SAdam Hornacek $closestInnerContainer = $clickedElement.closest('.sol-inner-container'), 225*87e82791SAdam Hornacek $clickedWithinThisSolContainer; 226*87e82791SAdam Hornacek 227*87e82791SAdam Hornacek if ($closestInnerContainer.length) { 228*87e82791SAdam Hornacek $clickedWithinThisSolContainer = $closestInnerContainer.first().parent('.sol-container'); 229*87e82791SAdam Hornacek } else if ($closestSelectionContainer.length) { 230*87e82791SAdam Hornacek $clickedWithinThisSolContainer = $closestSelectionContainer.first().parent('.sol-container'); 231*87e82791SAdam Hornacek } 232*87e82791SAdam Hornacek 233*87e82791SAdam Hornacek $('.sol-active') 234*87e82791SAdam Hornacek .not($clickedWithinThisSolContainer) 235*87e82791SAdam Hornacek .each(function (index, item) { 236*87e82791SAdam Hornacek $(item) 237*87e82791SAdam Hornacek .data(SearchableOptionList.prototype.DATA_KEY) 238*87e82791SAdam Hornacek .close(); 239*87e82791SAdam Hornacek }); 240*87e82791SAdam Hornacek }); 241*87e82791SAdam Hornacek 242*87e82791SAdam Hornacek // remember we already registered the global events 243*87e82791SAdam Hornacek window[this.WINDOW_EVENTS_KEY] = true; 244*87e82791SAdam Hornacek } 245*87e82791SAdam Hornacek }, 246*87e82791SAdam Hornacek 247*87e82791SAdam Hornacek // add sol ui elements 248*87e82791SAdam Hornacek _initializeUiElements: function () { 249*87e82791SAdam Hornacek var self = this; 250*87e82791SAdam Hornacek 251*87e82791SAdam Hornacek this.internalScrollWrapper = function () { 252*87e82791SAdam Hornacek if ($.isFunction(self.config.events.onScroll)) { 253*87e82791SAdam Hornacek self.config.events.onScroll.call(self); 254*87e82791SAdam Hornacek } 255*87e82791SAdam Hornacek }; 256*87e82791SAdam Hornacek 257*87e82791SAdam Hornacek this.$input = $('<input type="text"/>') 258*87e82791SAdam Hornacek .attr('placeholder', this.config.texts.searchplaceholder); 259*87e82791SAdam Hornacek 260*87e82791SAdam Hornacek this.$noResultsItem = $('<div class="sol-no-results"/>').html(this.config.texts.noItemsAvailable).hide(); 261*87e82791SAdam Hornacek this.$loadingData = $('<div class="sol-loading-data"/>').html(this.config.texts.loadingData); 262*87e82791SAdam Hornacek this.$xItemsSelected = $('<div class="sol-results-count"/>'); 263*87e82791SAdam Hornacek 264*87e82791SAdam Hornacek this.$caret = $('<div class="sol-caret-container"><b class="sol-caret"/></div>').click(function (e) { 265*87e82791SAdam Hornacek self.toggle(); 266*87e82791SAdam Hornacek e.preventDefault(); 267*87e82791SAdam Hornacek return false; 268*87e82791SAdam Hornacek }); 269*87e82791SAdam Hornacek 270*87e82791SAdam Hornacek var $inputContainer = $('<div class="sol-input-container"/>').append(this.$input); 271*87e82791SAdam Hornacek this.$innerContainer = $('<div class="sol-inner-container"/>').append($inputContainer).append(this.$caret); 272*87e82791SAdam Hornacek this.$selection = $('<div class="sol-selection"/>'); 273*87e82791SAdam Hornacek this.$selectionContainer = $('<div class="sol-selection-container"/>') 274*87e82791SAdam Hornacek .append(this.$noResultsItem) 275*87e82791SAdam Hornacek .append(this.$loadingData) 276*87e82791SAdam Hornacek .append(this.$selection); 277*87e82791SAdam Hornacek 278*87e82791SAdam Hornacek this.$container = $('<div class="sol-container"/>') 279*87e82791SAdam Hornacek .hide() 280*87e82791SAdam Hornacek /* 281*87e82791SAdam Hornacek * Modified for OpenGrok in 2016. 282*87e82791SAdam Hornacek */ 283*87e82791SAdam Hornacek .keydown(function (e) { 284*87e82791SAdam Hornacek if (e.keyCode == 13) { 285*87e82791SAdam Hornacek var concat = ''; 286*87e82791SAdam Hornacek $("#sbox #qtbl input[type='text']").each(function () { 287*87e82791SAdam Hornacek concat += $.trim($(this).val()); 288*87e82791SAdam Hornacek }); 289*87e82791SAdam Hornacek if (e.keyCode == 13 && concat === '') { 290*87e82791SAdam Hornacek // follow the project user's typed (may not exist) 291*87e82791SAdam Hornacek if(self.$input.val() !== '') { 292*87e82791SAdam Hornacek window.location = document.xrefPath + '/' + self.$input.val(); 293*87e82791SAdam Hornacek return false; 294*87e82791SAdam Hornacek } 295*87e82791SAdam Hornacek var $el = $(".keyboard-selection").first().find(".sol-checkbox") 296*87e82791SAdam Hornacek // follow the actual project 297*87e82791SAdam Hornacek if($el.length && $el.data('sol-item') && 298*87e82791SAdam Hornacek $el.data('sol-item').label) { 299*87e82791SAdam Hornacek window.location = document.xrefPath + 300*87e82791SAdam Hornacek '/' + 301*87e82791SAdam Hornacek $el.data('sol-item').label; 302*87e82791SAdam Hornacek return false; 303*87e82791SAdam Hornacek } 304*87e82791SAdam Hornacek // follow first selected project 305*87e82791SAdam Hornacek $el = $(".sol-selected-display-item").first() 306*87e82791SAdam Hornacek if($el.length && $el.data('label')) { 307*87e82791SAdam Hornacek window.location = document.xrefPath + '/' + $el.data('label'); 308*87e82791SAdam Hornacek return false; 309*87e82791SAdam Hornacek } 310*87e82791SAdam Hornacek return false; 311*87e82791SAdam Hornacek } 312*87e82791SAdam Hornacek return true; 313*87e82791SAdam Hornacek } 314*87e82791SAdam Hornacek }) 315*87e82791SAdam Hornacek .data(this.DATA_KEY, this) 316*87e82791SAdam Hornacek .append(this.$selectionContainer) 317*87e82791SAdam Hornacek .append(this.$innerContainer) 318*87e82791SAdam Hornacek .insertBefore(this.$originalElement); 319*87e82791SAdam Hornacek 320*87e82791SAdam Hornacek // add selected items display container 321*87e82791SAdam Hornacek this.$showSelectionContainer = $('<div class="sol-current-selection"/>'); 322*87e82791SAdam Hornacek 323*87e82791SAdam Hornacek /* 324*87e82791SAdam Hornacek * Modified for OpenGrok in 2016. 325*87e82791SAdam Hornacek */ 326*87e82791SAdam Hornacek var $el = this.config.resultsContainer || this.$innerContainer 327*87e82791SAdam Hornacek if (this.config.resultsContainer) { 328*87e82791SAdam Hornacek this.$showSelectionContainer.appendTo($el); 329*87e82791SAdam Hornacek } else { 330*87e82791SAdam Hornacek if (this.config.showSelectionBelowList) { 331*87e82791SAdam Hornacek this.$showSelectionContainer.insertAfter($el); 332*87e82791SAdam Hornacek } else { 333*87e82791SAdam Hornacek this.$showSelectionContainer.insertBefore($el); 334*87e82791SAdam Hornacek } 335*87e82791SAdam Hornacek } 336*87e82791SAdam Hornacek 337*87e82791SAdam Hornacek // dimensions 338*87e82791SAdam Hornacek if (this.config.maxHeight) { 339*87e82791SAdam Hornacek this.$selection.css('max-height', this.config.maxHeight); 340*87e82791SAdam Hornacek } 341*87e82791SAdam Hornacek 342*87e82791SAdam Hornacek // detect inline css classes and styles 343*87e82791SAdam Hornacek var cssClassesAsString = this.$originalElement.attr('class'), 344*87e82791SAdam Hornacek cssStylesAsString = this.$originalElement.attr('style'), 345*87e82791SAdam Hornacek cssClassList = [], 346*87e82791SAdam Hornacek stylesList = []; 347*87e82791SAdam Hornacek 348*87e82791SAdam Hornacek if (cssClassesAsString && cssClassesAsString.length > 0) { 349*87e82791SAdam Hornacek cssClassList = cssClassesAsString.split(/\s+/); 350*87e82791SAdam Hornacek 351*87e82791SAdam Hornacek // apply css classes to $container 352*87e82791SAdam Hornacek for (var i = 0; i < cssClassList.length; i++) { 353*87e82791SAdam Hornacek this.$container.addClass(cssClassList[i]); 354*87e82791SAdam Hornacek } 355*87e82791SAdam Hornacek } 356*87e82791SAdam Hornacek 357*87e82791SAdam Hornacek if (cssStylesAsString && cssStylesAsString.length > 0) { 358*87e82791SAdam Hornacek stylesList = cssStylesAsString.split(/\;/); 359*87e82791SAdam Hornacek 360*87e82791SAdam Hornacek // apply css inline styles to $container 361*87e82791SAdam Hornacek for (var i = 0; i < stylesList.length; i++) { 362*87e82791SAdam Hornacek var splitted = stylesList[i].split(/\s*\:\s*/g); 363*87e82791SAdam Hornacek 364*87e82791SAdam Hornacek if (splitted.length === 2) { 365*87e82791SAdam Hornacek 366*87e82791SAdam Hornacek if (splitted[0].toLowerCase().indexOf('height') >= 0) { 367*87e82791SAdam Hornacek // height property, apply to innerContainer instead of outer 368*87e82791SAdam Hornacek this.$innerContainer.css(splitted[0].trim(), splitted[1].trim()); 369*87e82791SAdam Hornacek } else { 370*87e82791SAdam Hornacek this.$container.css(splitted[0].trim(), splitted[1].trim()); 371*87e82791SAdam Hornacek } 372*87e82791SAdam Hornacek } 373*87e82791SAdam Hornacek } 374*87e82791SAdam Hornacek } 375*87e82791SAdam Hornacek 376*87e82791SAdam Hornacek if (this.$originalElement.css('display') !== 'block') { 377*87e82791SAdam Hornacek this.$container.css('width', this._getActualCssPropertyValue(this.$originalElement, 'width')); 378*87e82791SAdam Hornacek } 379*87e82791SAdam Hornacek 380*87e82791SAdam Hornacek if ($.isFunction(this.config.events.onRendered)) { 381*87e82791SAdam Hornacek this.config.events.onRendered.call(this, this); 382*87e82791SAdam Hornacek } 383*87e82791SAdam Hornacek }, 384*87e82791SAdam Hornacek 385*87e82791SAdam Hornacek _getActualCssPropertyValue: function ($element, property) { 386*87e82791SAdam Hornacek 387*87e82791SAdam Hornacek var domElement = $element.get(0), 388*87e82791SAdam Hornacek originalDisplayProperty = $element.css('display'); 389*87e82791SAdam Hornacek 390*87e82791SAdam Hornacek // set invisible to get original width setting instead of translated to px 391*87e82791SAdam Hornacek // see https://bugzilla.mozilla.org/show_bug.cgi?id=707691#c7 392*87e82791SAdam Hornacek $element.css('display', 'none'); 393*87e82791SAdam Hornacek 394*87e82791SAdam Hornacek if (domElement.currentStyle) { 395*87e82791SAdam Hornacek return domElement.currentStyle[property]; 396*87e82791SAdam Hornacek } else if (window.getComputedStyle) { 397*87e82791SAdam Hornacek return document.defaultView.getComputedStyle(domElement, null).getPropertyValue(property); 398*87e82791SAdam Hornacek } 399*87e82791SAdam Hornacek 400*87e82791SAdam Hornacek $element.css('display', originalDisplayProperty); 401*87e82791SAdam Hornacek 402*87e82791SAdam Hornacek return $element.css(property); 403*87e82791SAdam Hornacek }, 404*87e82791SAdam Hornacek 405*87e82791SAdam Hornacek _initializeInputEvents: function () { 406*87e82791SAdam Hornacek // form event 407*87e82791SAdam Hornacek var self = this, 408*87e82791SAdam Hornacek $form = this.$input.parents('form').first(); 409*87e82791SAdam Hornacek 410*87e82791SAdam Hornacek if ($form && $form.length === 1 && !$form.data(this.WINDOW_EVENTS_KEY)) { 411*87e82791SAdam Hornacek var resetFunction = function () { 412*87e82791SAdam Hornacek var $changedItems = []; 413*87e82791SAdam Hornacek 414*87e82791SAdam Hornacek $form.find('.sol-option input').each(function (index, item) { 415*87e82791SAdam Hornacek var $item = $(item), 416*87e82791SAdam Hornacek initialState = $item.data('sol-item').selected; 417*87e82791SAdam Hornacek 418*87e82791SAdam Hornacek if ($item.prop('checked') !== initialState) { 419*87e82791SAdam Hornacek $item 420*87e82791SAdam Hornacek .prop('checked', initialState) 421*87e82791SAdam Hornacek .trigger('sol-change', true); 422*87e82791SAdam Hornacek $changedItems.push($item); 423*87e82791SAdam Hornacek } 424*87e82791SAdam Hornacek }); 425*87e82791SAdam Hornacek 426*87e82791SAdam Hornacek if ($changedItems.length > 0 && $.isFunction(self.config.events.onChange)) { 427*87e82791SAdam Hornacek self.config.events.onChange.call(self, self, $changedItems); 428*87e82791SAdam Hornacek } 429*87e82791SAdam Hornacek }; 430*87e82791SAdam Hornacek 431*87e82791SAdam Hornacek $form.on('reset', function (event) { 432*87e82791SAdam Hornacek // unfortunately the reset event gets fired _before_ 433*87e82791SAdam Hornacek // the inputs are actually reset. The only possibility 434*87e82791SAdam Hornacek // to overcome this is to set an interval to execute 435*87e82791SAdam Hornacek // own scripts some time after the actual reset event 436*87e82791SAdam Hornacek 437*87e82791SAdam Hornacek // before fields are actually reset by the browser 438*87e82791SAdam Hornacek // needed to reset newly checked fields 439*87e82791SAdam Hornacek resetFunction.call(self); 440*87e82791SAdam Hornacek 441*87e82791SAdam Hornacek // timeout for selection after form reset 442*87e82791SAdam Hornacek // needed to reset previously checked fields 443*87e82791SAdam Hornacek setTimeout(function () { 444*87e82791SAdam Hornacek resetFunction.call(self); 445*87e82791SAdam Hornacek }, 100); 446*87e82791SAdam Hornacek }); 447*87e82791SAdam Hornacek 448*87e82791SAdam Hornacek $form.data(this.WINDOW_EVENTS_KEY, true); 449*87e82791SAdam Hornacek } 450*87e82791SAdam Hornacek 451*87e82791SAdam Hornacek // text input events 452*87e82791SAdam Hornacek this.$input 453*87e82791SAdam Hornacek .focus(function () { 454*87e82791SAdam Hornacek self.open(); 455*87e82791SAdam Hornacek }) 456*87e82791SAdam Hornacek .on('propertychange input', function (e) { 457*87e82791SAdam Hornacek var valueChanged = true; 458*87e82791SAdam Hornacek if (e.type=='propertychange') { 459*87e82791SAdam Hornacek valueChanged = e.originalEvent.propertyName.toLowerCase()=='value'; 460*87e82791SAdam Hornacek } 461*87e82791SAdam Hornacek if (valueChanged) { 462*87e82791SAdam Hornacek if ($(this).data('timeout')) { 463*87e82791SAdam Hornacek clearTimeout($(this).data('timeout')); 464*87e82791SAdam Hornacek } 465*87e82791SAdam Hornacek $(this).data('timeout', setTimeout(function () { 466*87e82791SAdam Hornacek self._applySearchTermFilter(); 467*87e82791SAdam Hornacek }, self.config.searchTimeout)) 468*87e82791SAdam Hornacek 469*87e82791SAdam Hornacek } 470*87e82791SAdam Hornacek }); 471*87e82791SAdam Hornacek 472*87e82791SAdam Hornacek // keyboard navigation 473*87e82791SAdam Hornacek this.$container 474*87e82791SAdam Hornacek .on('keydown', function (e) { 475*87e82791SAdam Hornacek var keyCode = e.keyCode; 476*87e82791SAdam Hornacek 477*87e82791SAdam Hornacek // event handling for keyboard navigation 478*87e82791SAdam Hornacek // only when there are results to be shown 479*87e82791SAdam Hornacek if (!self.$noResultsItem.is(':visible')) { 480*87e82791SAdam Hornacek 481*87e82791SAdam Hornacek var $currentHighlightedOption, 482*87e82791SAdam Hornacek $nextHighlightedOption, 483*87e82791SAdam Hornacek directionValue, 484*87e82791SAdam Hornacek preventDefault = false, 485*87e82791SAdam Hornacek $allVisibleOptions = self.$selection.find('.sol-option:visible'); 486*87e82791SAdam Hornacek 487*87e82791SAdam Hornacek if (keyCode === 40 || keyCode === 38) { 488*87e82791SAdam Hornacek // arrow up or down to select an item 489*87e82791SAdam Hornacek self._setKeyBoardNavigationMode(true); 490*87e82791SAdam Hornacek /* 491*87e82791SAdam Hornacek * Modified for OpenGrok in 2016. 492*87e82791SAdam Hornacek */ 493*87e82791SAdam Hornacek $currentHighlightedOption = self.$selection.find('.sol-option.keyboard-selection') 494*87e82791SAdam Hornacek $currentHighlightedOption.find("input[type='checkbox']").blur(); 495*87e82791SAdam Hornacek directionValue = (keyCode === 38) ? -1 : 1; // negative for up, positive for down 496*87e82791SAdam Hornacek 497*87e82791SAdam Hornacek var indexOfNextHighlightedOption = $allVisibleOptions.index($currentHighlightedOption) + directionValue; 498*87e82791SAdam Hornacek if (indexOfNextHighlightedOption < 0) { 499*87e82791SAdam Hornacek indexOfNextHighlightedOption = $allVisibleOptions.length - 1; 500*87e82791SAdam Hornacek } else if (indexOfNextHighlightedOption >= $allVisibleOptions.length) { 501*87e82791SAdam Hornacek indexOfNextHighlightedOption = 0; 502*87e82791SAdam Hornacek } 503*87e82791SAdam Hornacek 504*87e82791SAdam Hornacek $currentHighlightedOption.removeClass('keyboard-selection'); 505*87e82791SAdam Hornacek $nextHighlightedOption = $($allVisibleOptions[indexOfNextHighlightedOption]) 506*87e82791SAdam Hornacek .addClass('keyboard-selection'); 507*87e82791SAdam Hornacek /* 508*87e82791SAdam Hornacek * Modified for OpenGrok in 2016. 509*87e82791SAdam Hornacek */ 510*87e82791SAdam Hornacek $nextHighlightedOption.find("input[type='checkbox']").focus() 511*87e82791SAdam Hornacek 512*87e82791SAdam Hornacek /* 513*87e82791SAdam Hornacek * Modified for OpenGrok in 2016. 514*87e82791SAdam Hornacek */ 515*87e82791SAdam Hornacek //self.$selection.scrollTop(self.$selection.scrollTop() + $nextHighlightedOption.position().top); 516*87e82791SAdam Hornacek 517*87e82791SAdam Hornacek preventDefault = true; 518*87e82791SAdam Hornacek } else if (self.keyboardNavigationMode === true && keyCode === 32) { 519*87e82791SAdam Hornacek // toggle current selected item with space bar 520*87e82791SAdam Hornacek $currentHighlightedOption = self.$selection.find('.sol-option.keyboard-selection input'); 521*87e82791SAdam Hornacek $currentHighlightedOption 522*87e82791SAdam Hornacek .prop('checked', !$currentHighlightedOption.is(':checked')) 523*87e82791SAdam Hornacek .trigger('change'); 524*87e82791SAdam Hornacek 525*87e82791SAdam Hornacek preventDefault = true; 526*87e82791SAdam Hornacek } 527*87e82791SAdam Hornacek 528*87e82791SAdam Hornacek if (preventDefault) { 529*87e82791SAdam Hornacek // dont trigger any events in the input 530*87e82791SAdam Hornacek e.preventDefault(); 531*87e82791SAdam Hornacek return false; 532*87e82791SAdam Hornacek } 533*87e82791SAdam Hornacek } 534*87e82791SAdam Hornacek }) 535*87e82791SAdam Hornacek .on('keyup', function (e) { 536*87e82791SAdam Hornacek var keyCode = e.keyCode; 537*87e82791SAdam Hornacek 538*87e82791SAdam Hornacek if (keyCode === 27) { 539*87e82791SAdam Hornacek // escape key 540*87e82791SAdam Hornacek if (self.keyboardNavigationMode === true) { 541*87e82791SAdam Hornacek self._setKeyBoardNavigationMode(false); 542*87e82791SAdam Hornacek } else if (self.$input.val() === '') { 543*87e82791SAdam Hornacek // trigger closing of container 544*87e82791SAdam Hornacek self.$caret.trigger('click'); 545*87e82791SAdam Hornacek self.$input.trigger('blur'); 546*87e82791SAdam Hornacek } else { 547*87e82791SAdam Hornacek // reset input and result filter 548*87e82791SAdam Hornacek self.$input.val('').trigger('input'); 549*87e82791SAdam Hornacek } 550*87e82791SAdam Hornacek } else if (keyCode === 16 || keyCode === 17 || keyCode === 18 || keyCode === 20) { 551*87e82791SAdam Hornacek // special events like shift and control 552*87e82791SAdam Hornacek return; 553*87e82791SAdam Hornacek } 554*87e82791SAdam Hornacek }); 555*87e82791SAdam Hornacek }, 556*87e82791SAdam Hornacek 557*87e82791SAdam Hornacek _setKeyBoardNavigationMode: function (keyboardNavigationOn) { 558*87e82791SAdam Hornacek 559*87e82791SAdam Hornacek if (keyboardNavigationOn) { 560*87e82791SAdam Hornacek // on 561*87e82791SAdam Hornacek this.keyboardNavigationMode = true; 562*87e82791SAdam Hornacek this.$selection.addClass('sol-keyboard-navigation'); 563*87e82791SAdam Hornacek } else { 564*87e82791SAdam Hornacek // off 565*87e82791SAdam Hornacek this.keyboardNavigationMode = false; 566*87e82791SAdam Hornacek this.$selection.find('.sol-option.keyboard-selection') 567*87e82791SAdam Hornacek this.$selection.removeClass('sol-keyboard-navigation'); 568*87e82791SAdam Hornacek this.$selectionContainer.find('.sol-option.keyboard-selection').removeClass('keyboard-selection'); 569*87e82791SAdam Hornacek this.$selection.scrollTop(0); 570*87e82791SAdam Hornacek } 571*87e82791SAdam Hornacek }, 572*87e82791SAdam Hornacek 573*87e82791SAdam Hornacek _applySearchTermFilter: function () { 574*87e82791SAdam Hornacek if (!this.items || this.items.length === 0) { 575*87e82791SAdam Hornacek return; 576*87e82791SAdam Hornacek } 577*87e82791SAdam Hornacek 578*87e82791SAdam Hornacek var searchTerm = this.$input.val(), 579*87e82791SAdam Hornacek lowerCased = (searchTerm || '').toLowerCase(); 580*87e82791SAdam Hornacek 581*87e82791SAdam Hornacek // show previously filtered elements again 582*87e82791SAdam Hornacek this.$selectionContainer.find('.sol-filtered-search').removeClass('sol-filtered-search'); 583*87e82791SAdam Hornacek this._setNoResultsItemVisible(false); 584*87e82791SAdam Hornacek 585*87e82791SAdam Hornacek if (lowerCased.trim().length > 0) { 586*87e82791SAdam Hornacek this._findTerms(this.items, lowerCased); 587*87e82791SAdam Hornacek } 588*87e82791SAdam Hornacek 589*87e82791SAdam Hornacek // call onScroll to position the popup again 590*87e82791SAdam Hornacek // important if showing popup above list 591*87e82791SAdam Hornacek if ($.isFunction(this.config.events.onScroll)) { 592*87e82791SAdam Hornacek this.config.events.onScroll.call(this); 593*87e82791SAdam Hornacek } 594*87e82791SAdam Hornacek }, 595*87e82791SAdam Hornacek 596*87e82791SAdam Hornacek _findTerms: function (dataArray, searchTerm) { 597*87e82791SAdam Hornacek if (!dataArray || !$.isArray(dataArray) || dataArray.length === 0) { 598*87e82791SAdam Hornacek return; 599*87e82791SAdam Hornacek } 600*87e82791SAdam Hornacek 601*87e82791SAdam Hornacek var self = this, 602*87e82791SAdam Hornacek amountOfUnfilteredItems = dataArray.length 603*87e82791SAdam Hornacek 604*87e82791SAdam Hornacek // reset keyboard navigation mode when applying new filter 605*87e82791SAdam Hornacek this._setKeyBoardNavigationMode(false); 606*87e82791SAdam Hornacek 607*87e82791SAdam Hornacek /* 608*87e82791SAdam Hornacek * Modified for OpenGrok in 2016. 609*87e82791SAdam Hornacek * recursion was very slow (however good lookin') 610*87e82791SAdam Hornacek */ 611*87e82791SAdam Hornacek for (var itemIndex = 0; itemIndex < dataArray.length; itemIndex++) { 612*87e82791SAdam Hornacek var item = dataArray[itemIndex]; 613*87e82791SAdam Hornacek if (item.type === 'option') { 614*87e82791SAdam Hornacek var $element = item.displayElement, 615*87e82791SAdam Hornacek elementSearchableTerms = (item.label + ' ' + item.tooltip).trim().toLowerCase(); 616*87e82791SAdam Hornacek 617*87e82791SAdam Hornacek if (elementSearchableTerms.indexOf(searchTerm) === -1) { 618*87e82791SAdam Hornacek $element.addClass('sol-filtered-search'); 619*87e82791SAdam Hornacek amountOfUnfilteredItems--; 620*87e82791SAdam Hornacek } 621*87e82791SAdam Hornacek } else { 622*87e82791SAdam Hornacek var amountOfUnfilteredChildren = item.children.length 623*87e82791SAdam Hornacek for (var childrenIndex = 0; childrenIndex < item.children.length; childrenIndex++) { 624*87e82791SAdam Hornacek var child = item.children[childrenIndex]; 625*87e82791SAdam Hornacek if (child.type === 'option') { 626*87e82791SAdam Hornacek var $element = child.displayElement, 627*87e82791SAdam Hornacek elementSearchableTerms = (child.label + ' ' + child.tooltip).trim().toLowerCase(); 628*87e82791SAdam Hornacek 629*87e82791SAdam Hornacek if (elementSearchableTerms.indexOf(searchTerm) === -1) { 630*87e82791SAdam Hornacek $element.addClass('sol-filtered-search'); 631*87e82791SAdam Hornacek amountOfUnfilteredChildren--; 632*87e82791SAdam Hornacek } 633*87e82791SAdam Hornacek } 634*87e82791SAdam Hornacek } 635*87e82791SAdam Hornacek 636*87e82791SAdam Hornacek if (amountOfUnfilteredChildren === 0) { 637*87e82791SAdam Hornacek item.displayElement.addClass('sol-filtered-search'); 638*87e82791SAdam Hornacek amountOfUnfilteredItems--; 639*87e82791SAdam Hornacek } 640*87e82791SAdam Hornacek } 641*87e82791SAdam Hornacek } 642*87e82791SAdam Hornacek 643*87e82791SAdam Hornacek this._setNoResultsItemVisible(amountOfUnfilteredItems === 0); 644*87e82791SAdam Hornacek }, 645*87e82791SAdam Hornacek 646*87e82791SAdam Hornacek _initializeData: function () { 647*87e82791SAdam Hornacek if (!this.config.data) { 648*87e82791SAdam Hornacek this.items = this._detectDataFromOriginalElement(); 649*87e82791SAdam Hornacek } else if ($.isFunction(this.config.data)) { 650*87e82791SAdam Hornacek this.items = this._fetchDataFromFunction(this.config.data); 651*87e82791SAdam Hornacek } else if ($.isArray(this.config.data)) { 652*87e82791SAdam Hornacek this.items = this._fetchDataFromArray(this.config.data); 653*87e82791SAdam Hornacek } else if (typeof this.config.data === (typeof 'a string')) { 654*87e82791SAdam Hornacek this._loadItemsFromUrl(this.config.data); 655*87e82791SAdam Hornacek } else { 656*87e82791SAdam Hornacek this._showErrorLabel('Unknown data type'); 657*87e82791SAdam Hornacek } 658*87e82791SAdam Hornacek 659*87e82791SAdam Hornacek if (this.items) { 660*87e82791SAdam Hornacek // done right away -> invoke postprocessing 661*87e82791SAdam Hornacek this._processDataItems(this.items); 662*87e82791SAdam Hornacek } 663*87e82791SAdam Hornacek }, 664*87e82791SAdam Hornacek 665*87e82791SAdam Hornacek _detectDataFromOriginalElement: function () { 666*87e82791SAdam Hornacek if (this.$originalElement.prop('tagName').toLowerCase() === 'select') { 667*87e82791SAdam Hornacek var self = this, 668*87e82791SAdam Hornacek solData = []; 669*87e82791SAdam Hornacek 670*87e82791SAdam Hornacek $.each(this.$originalElement.children(), function (index, item) { 671*87e82791SAdam Hornacek var $item = $(item), 672*87e82791SAdam Hornacek itemTagName = $item.prop('tagName').toLowerCase(), 673*87e82791SAdam Hornacek solDataItem; 674*87e82791SAdam Hornacek 675*87e82791SAdam Hornacek if (itemTagName === 'option') { 676*87e82791SAdam Hornacek solDataItem = self._processSelectOption($item); 677*87e82791SAdam Hornacek if (solDataItem) { 678*87e82791SAdam Hornacek solData.push(solDataItem); 679*87e82791SAdam Hornacek } 680*87e82791SAdam Hornacek } else if (itemTagName === 'optgroup') { 681*87e82791SAdam Hornacek solDataItem = self._processSelectOptgroup($item); 682*87e82791SAdam Hornacek if (solDataItem) { 683*87e82791SAdam Hornacek solData.push(solDataItem); 684*87e82791SAdam Hornacek } 685*87e82791SAdam Hornacek } else { 686*87e82791SAdam Hornacek self._showErrorLabel('Invalid element found in select: ' + itemTagName + '. Only option and optgroup are allowed'); 687*87e82791SAdam Hornacek } 688*87e82791SAdam Hornacek }); 689*87e82791SAdam Hornacek return this._invokeConverterIfNecessary(solData); 690*87e82791SAdam Hornacek } else if (this.$originalElement.data('sol-data')) { 691*87e82791SAdam Hornacek var solDataAttributeValue = this.$originalElement.data('sol-data'); 692*87e82791SAdam Hornacek return this._invokeConverterIfNecessary(solDataAttributeValue); 693*87e82791SAdam Hornacek } else { 694*87e82791SAdam Hornacek this._showErrorLabel('Could not determine data from original element. Must be a select or data must be provided as data-sol-data="" attribute'); 695*87e82791SAdam Hornacek } 696*87e82791SAdam Hornacek }, 697*87e82791SAdam Hornacek 698*87e82791SAdam Hornacek _processSelectOption: function ($option) { 699*87e82791SAdam Hornacek return $.extend({}, this.SOL_OPTION_FORMAT, { 700*87e82791SAdam Hornacek value: $option.val(), 701*87e82791SAdam Hornacek selected: $option.prop('selected'), 702*87e82791SAdam Hornacek disabled: $option.prop('disabled'), 703*87e82791SAdam Hornacek cssClass: $option.attr('class'), 704*87e82791SAdam Hornacek label: $option.html(), 705*87e82791SAdam Hornacek tooltip: $option.attr('title'), 706*87e82791SAdam Hornacek element: $option 707*87e82791SAdam Hornacek }); 708*87e82791SAdam Hornacek }, 709*87e82791SAdam Hornacek 710*87e82791SAdam Hornacek _processSelectOptgroup: function ($optgroup) { 711*87e82791SAdam Hornacek var self = this, 712*87e82791SAdam Hornacek solOptiongroup = $.extend({}, this.SOL_OPTIONGROUP_FORMAT, { 713*87e82791SAdam Hornacek label: $optgroup.attr('label'), 714*87e82791SAdam Hornacek tooltip: $optgroup.attr('title'), 715*87e82791SAdam Hornacek disabled: $optgroup.prop('disabled'), 716*87e82791SAdam Hornacek children: [] 717*87e82791SAdam Hornacek }), 718*87e82791SAdam Hornacek optgroupChildren = $optgroup.children('option'); 719*87e82791SAdam Hornacek 720*87e82791SAdam Hornacek $.each(optgroupChildren, function (index, item) { 721*87e82791SAdam Hornacek var $child = $(item), 722*87e82791SAdam Hornacek solOption = self._processSelectOption($child); 723*87e82791SAdam Hornacek 724*87e82791SAdam Hornacek // explicitly disable children when optgroup is disabled 725*87e82791SAdam Hornacek if (solOptiongroup.disabled) { 726*87e82791SAdam Hornacek solOption.disabled = true; 727*87e82791SAdam Hornacek } 728*87e82791SAdam Hornacek 729*87e82791SAdam Hornacek solOptiongroup.children.push(solOption); 730*87e82791SAdam Hornacek }); 731*87e82791SAdam Hornacek 732*87e82791SAdam Hornacek return solOptiongroup; 733*87e82791SAdam Hornacek }, 734*87e82791SAdam Hornacek 735*87e82791SAdam Hornacek _fetchDataFromFunction: function (dataFunction) { 736*87e82791SAdam Hornacek return this._invokeConverterIfNecessary(dataFunction(this)); 737*87e82791SAdam Hornacek }, 738*87e82791SAdam Hornacek 739*87e82791SAdam Hornacek _fetchDataFromArray: function (dataArray) { 740*87e82791SAdam Hornacek return this._invokeConverterIfNecessary(dataArray); 741*87e82791SAdam Hornacek }, 742*87e82791SAdam Hornacek 743*87e82791SAdam Hornacek _loadItemsFromUrl: function (url) { 744*87e82791SAdam Hornacek var self = this; 745*87e82791SAdam Hornacek $.ajax(url, { 746*87e82791SAdam Hornacek success: function (actualData) { 747*87e82791SAdam Hornacek self.items = self._invokeConverterIfNecessary(actualData); 748*87e82791SAdam Hornacek if (self.items) { 749*87e82791SAdam Hornacek self._processDataItems(self.items); 750*87e82791SAdam Hornacek } 751*87e82791SAdam Hornacek }, 752*87e82791SAdam Hornacek error: function (xhr, status, message) { 753*87e82791SAdam Hornacek self._showErrorLabel('Error loading from url ' + url + ': ' + message); 754*87e82791SAdam Hornacek }, 755*87e82791SAdam Hornacek dataType: 'json' 756*87e82791SAdam Hornacek }); 757*87e82791SAdam Hornacek }, 758*87e82791SAdam Hornacek 759*87e82791SAdam Hornacek _invokeConverterIfNecessary: function (dataItems) { 760*87e82791SAdam Hornacek if ($.isFunction(this.config.converter)) { 761*87e82791SAdam Hornacek return this.config.converter.call(this, this, dataItems); 762*87e82791SAdam Hornacek } 763*87e82791SAdam Hornacek return dataItems; 764*87e82791SAdam Hornacek }, 765*87e82791SAdam Hornacek 766*87e82791SAdam Hornacek _processDataItems: function (solItems) { 767*87e82791SAdam Hornacek if (!solItems) { 768*87e82791SAdam Hornacek this._showErrorLabel('Data items not present. Maybe the converter did not return any values'); 769*87e82791SAdam Hornacek return; 770*87e82791SAdam Hornacek } 771*87e82791SAdam Hornacek 772*87e82791SAdam Hornacek if (solItems.length === 0) { 773*87e82791SAdam Hornacek this._setNoResultsItemVisible(true); 774*87e82791SAdam Hornacek this.$loadingData.remove(); 775*87e82791SAdam Hornacek return; 776*87e82791SAdam Hornacek } 777*87e82791SAdam Hornacek 778*87e82791SAdam Hornacek var self = this, 779*87e82791SAdam Hornacek nextIndex = 0, 780*87e82791SAdam Hornacek dataProcessedFunction = function () { 781*87e82791SAdam Hornacek // hide "loading data" 782*87e82791SAdam Hornacek this.$loadingData.remove(); 783*87e82791SAdam Hornacek this._initializeSelectAll(); 784*87e82791SAdam Hornacek 785*87e82791SAdam Hornacek if ($.isFunction(this.config.events.onInitialized)) { 786*87e82791SAdam Hornacek this.config.events.onInitialized.call(this, this, solItems); 787*87e82791SAdam Hornacek } 788*87e82791SAdam Hornacek }, 789*87e82791SAdam Hornacek loopFunction = function () { 790*87e82791SAdam Hornacek 791*87e82791SAdam Hornacek var currentBatch = 0, 792*87e82791SAdam Hornacek item; 793*87e82791SAdam Hornacek 794*87e82791SAdam Hornacek while (currentBatch++ < self.config.asyncBatchSize && nextIndex < solItems.length) { 795*87e82791SAdam Hornacek item = solItems[nextIndex++]; 796*87e82791SAdam Hornacek if (item.type === self.SOL_OPTION_FORMAT.type) { 797*87e82791SAdam Hornacek self._renderOption(item); 798*87e82791SAdam Hornacek } else if (item.type === self.SOL_OPTIONGROUP_FORMAT.type) { 799*87e82791SAdam Hornacek self._renderOptiongroup(item); 800*87e82791SAdam Hornacek } else { 801*87e82791SAdam Hornacek self._showErrorLabel('Invalid item type found ' + item.type); 802*87e82791SAdam Hornacek return; 803*87e82791SAdam Hornacek } 804*87e82791SAdam Hornacek } 805*87e82791SAdam Hornacek 806*87e82791SAdam Hornacek if (nextIndex >= solItems.length) { 807*87e82791SAdam Hornacek dataProcessedFunction.call(self); 808*87e82791SAdam Hornacek } else { 809*87e82791SAdam Hornacek setTimeout(loopFunction, 0); 810*87e82791SAdam Hornacek } 811*87e82791SAdam Hornacek }; 812*87e82791SAdam Hornacek 813*87e82791SAdam Hornacek // start async rendering of html elements 814*87e82791SAdam Hornacek loopFunction.call(this); 815*87e82791SAdam Hornacek }, 816*87e82791SAdam Hornacek 817*87e82791SAdam Hornacek _renderOption: function (solOption, $optionalTargetContainer) { 818*87e82791SAdam Hornacek var self = this, 819*87e82791SAdam Hornacek $actualTargetContainer = $optionalTargetContainer || this.$selection, 820*87e82791SAdam Hornacek $inputElement, 821*87e82791SAdam Hornacek /* 822*87e82791SAdam Hornacek * Modified for OpenGrok in 2016. 823*87e82791SAdam Hornacek */ 824*87e82791SAdam Hornacek $labelText = $('<div class="sol-label-text"/>') 825*87e82791SAdam Hornacek .html(solOption.label.trim().length === 0 ? ' ' : solOption.label) 826*87e82791SAdam Hornacek .addClass(solOption.cssClass), 827*87e82791SAdam Hornacek $label, 828*87e82791SAdam Hornacek $displayElement, 829*87e82791SAdam Hornacek inputName = this._getNameAttribute(); 830*87e82791SAdam Hornacek /* 831*87e82791SAdam Hornacek * Modified for OpenGrok in 2016, 2019. 832*87e82791SAdam Hornacek */ 833*87e82791SAdam Hornacek var data = $(solOption.element).data('messages'); 834*87e82791SAdam Hornacek var messagesLevel = $(solOption.element).data('messages-level'); 835*87e82791SAdam Hornacek var messagesAvailable = data && data.length; 836*87e82791SAdam Hornacek if (messagesAvailable && messagesLevel) { 837*87e82791SAdam Hornacek var cssString = 'pull-right '; 838*87e82791SAdam Hornacek cssString += 'note-' + messagesLevel; 839*87e82791SAdam Hornacek cssString += ' important-note important-note-rounded'; 840*87e82791SAdam Hornacek 841*87e82791SAdam Hornacek $labelText.append( 842*87e82791SAdam Hornacek $('<span>') 843*87e82791SAdam Hornacek .addClass(cssString) 844*87e82791SAdam Hornacek .data("messages", data) 845*87e82791SAdam Hornacek .attr('data-messages', '') 846*87e82791SAdam Hornacek .text('!') 847*87e82791SAdam Hornacek ); 848*87e82791SAdam Hornacek } 849*87e82791SAdam Hornacek 850*87e82791SAdam Hornacek if (this.config.multiple) { 851*87e82791SAdam Hornacek // use checkboxes 852*87e82791SAdam Hornacek $inputElement = $('<input type="checkbox" class="sol-checkbox"/>'); 853*87e82791SAdam Hornacek 854*87e82791SAdam Hornacek if (this.config.useBracketParameters) { 855*87e82791SAdam Hornacek inputName += '[]'; 856*87e82791SAdam Hornacek } 857*87e82791SAdam Hornacek } else { 858*87e82791SAdam Hornacek // use radio buttons 859*87e82791SAdam Hornacek $inputElement = $('<input type="radio" class="sol-radio"/>') 860*87e82791SAdam Hornacek .on('change', function () { 861*87e82791SAdam Hornacek // when selected notify all others of being deselected 862*87e82791SAdam Hornacek self.$selectionContainer.find('input[type="radio"][name="' + inputName + '"]').not($(this)).trigger('sol-deselect'); 863*87e82791SAdam Hornacek }) 864*87e82791SAdam Hornacek .on('sol-deselect', function () { 865*87e82791SAdam Hornacek // remove display selection item 866*87e82791SAdam Hornacek // TODO also better show it inline instead of above or below to save space 867*87e82791SAdam Hornacek self._removeSelectionDisplayItem($(this)); 868*87e82791SAdam Hornacek }); 869*87e82791SAdam Hornacek } 870*87e82791SAdam Hornacek 871*87e82791SAdam Hornacek $inputElement 872*87e82791SAdam Hornacek .on('change', function (event, skipCallback) { 873*87e82791SAdam Hornacek $(this).trigger('sol-change', skipCallback); 874*87e82791SAdam Hornacek }) 875*87e82791SAdam Hornacek .on('sol-change', function (event, skipCallback) { 876*87e82791SAdam Hornacek /* 877*87e82791SAdam Hornacek * Modified for OpenGrok in 2016. 878*87e82791SAdam Hornacek */ 879*87e82791SAdam Hornacek var $closestOption = $(this).closest('.sol-option') 880*87e82791SAdam Hornacek self._setKeyBoardNavigationMode(true) 881*87e82791SAdam Hornacek self.$selection 882*87e82791SAdam Hornacek .find('.sol-option.keyboard-selection') 883*87e82791SAdam Hornacek .removeClass("keyboard-selection") 884*87e82791SAdam Hornacek 885*87e82791SAdam Hornacek $closestOption.addClass('keyboard-selection') 886*87e82791SAdam Hornacek //self.$selection.scrollTop(self.$selection.scrollTop() + $closestOption.position().top) 887*87e82791SAdam Hornacek 888*87e82791SAdam Hornacek self._selectionChange($(this), skipCallback); 889*87e82791SAdam Hornacek }) 890*87e82791SAdam Hornacek .data('sol-item', solOption) 891*87e82791SAdam Hornacek .prop('checked', solOption.selected) 892*87e82791SAdam Hornacek .prop('disabled', solOption.disabled) 893*87e82791SAdam Hornacek .attr('name', inputName) 894*87e82791SAdam Hornacek .val(solOption.value); 895*87e82791SAdam Hornacek 896*87e82791SAdam Hornacek $label = $('<label class="sol-label"/>') 897*87e82791SAdam Hornacek .attr('title', solOption.tooltip) 898*87e82791SAdam Hornacek .append($inputElement) 899*87e82791SAdam Hornacek .append($labelText); 900*87e82791SAdam Hornacek /* 901*87e82791SAdam Hornacek * Modified for OpenGrok in 2016. 902*87e82791SAdam Hornacek */ 903*87e82791SAdam Hornacek $displayElement = $('<div class="sol-option"/>').dblclick(function (e) { 904*87e82791SAdam Hornacek var $el = $(this).find('.sol-checkbox'); 905*87e82791SAdam Hornacek if ($el.length && $el.data('sol-item') && $el.data('sol-item').label) { 906*87e82791SAdam Hornacek // go first project 907*87e82791SAdam Hornacek window.location = document.xrefPath + '/' + $(this).find('.sol-checkbox').data('sol-item').label; 908*87e82791SAdam Hornacek } 909*87e82791SAdam Hornacek }).append($label); 910*87e82791SAdam Hornacek /* 911*87e82791SAdam Hornacek * Modified for OpenGrok in 2016, 2019. 912*87e82791SAdam Hornacek */ 913*87e82791SAdam Hornacek $inputElement.data('messages-available', messagesAvailable); 914*87e82791SAdam Hornacek if (messagesLevel) { 915*87e82791SAdam Hornacek $inputElement.data('messages-level', messagesLevel); 916*87e82791SAdam Hornacek } 917*87e82791SAdam Hornacek 918*87e82791SAdam Hornacek solOption.displayElement = $displayElement; 919*87e82791SAdam Hornacek 920*87e82791SAdam Hornacek $actualTargetContainer.append($displayElement); 921*87e82791SAdam Hornacek 922*87e82791SAdam Hornacek if (solOption.selected) { 923*87e82791SAdam Hornacek this._addSelectionDisplayItem($inputElement); 924*87e82791SAdam Hornacek } 925*87e82791SAdam Hornacek }, 926*87e82791SAdam Hornacek 927*87e82791SAdam Hornacek _renderOptiongroup: function (solOptiongroup) { 928*87e82791SAdam Hornacek var self = this, 929*87e82791SAdam Hornacek $groupCaption = $('<div class="sol-optiongroup-label"/>') 930*87e82791SAdam Hornacek .attr('title', solOptiongroup.tooltip) 931*87e82791SAdam Hornacek .html(solOptiongroup.label), 932*87e82791SAdam Hornacek $groupCheckbox = $('<input class="sol-checkbox" style="display: none" type="checkbox" name="group" value="' + solOptiongroup.label+ '"/>'), 933*87e82791SAdam Hornacek $groupItem = $('<div class="sol-optiongroup"/>').append($groupCaption).append($groupCheckbox); 934*87e82791SAdam Hornacek 935*87e82791SAdam Hornacek if (solOptiongroup.disabled) { 936*87e82791SAdam Hornacek $groupItem.addClass('disabled'); 937*87e82791SAdam Hornacek } 938*87e82791SAdam Hornacek /* 939*87e82791SAdam Hornacek * Modified for OpenGrok in 2016, 2017. 940*87e82791SAdam Hornacek */ 941*87e82791SAdam Hornacek $groupCaption.click(function (e) { 942*87e82791SAdam Hornacek // select all group 943*87e82791SAdam Hornacek if (self.config.multiple) { 944*87e82791SAdam Hornacek if (!e.ctrlKey) { 945*87e82791SAdam Hornacek self.deselectAll(); 946*87e82791SAdam Hornacek } 947*87e82791SAdam Hornacek self.selectAll($(this).text()) 948*87e82791SAdam Hornacek self.$selection.scrollTop(self.$selection.scrollTop() + $(this).position().top) 949*87e82791SAdam Hornacek } 950*87e82791SAdam Hornacek }); 951*87e82791SAdam Hornacek 952*87e82791SAdam Hornacek /* 953*87e82791SAdam Hornacek * Modified for OpenGrok in 2016. 954*87e82791SAdam Hornacek */ 955*87e82791SAdam Hornacek this.$selection.append($groupItem); 956*87e82791SAdam Hornacek 957*87e82791SAdam Hornacek if ($.isArray(solOptiongroup.children)) { 958*87e82791SAdam Hornacek $.each(solOptiongroup.children, function (index, item) { 959*87e82791SAdam Hornacek self._renderOption(item, $groupItem); 960*87e82791SAdam Hornacek }); 961*87e82791SAdam Hornacek } 962*87e82791SAdam Hornacek 963*87e82791SAdam Hornacek solOptiongroup.displayElement = $groupItem; 964*87e82791SAdam Hornacek }, 965*87e82791SAdam Hornacek 966*87e82791SAdam Hornacek _initializeSelectAll: function () { 967*87e82791SAdam Hornacek // multiple values selectable 968*87e82791SAdam Hornacek if (this.config.showSelectAll === true || ($.isFunction(this.config.showSelectAll) && this.config.showSelectAll.call(this))) { 969*87e82791SAdam Hornacek // buttons for (de-)select all 970*87e82791SAdam Hornacek var self = this, 971*87e82791SAdam Hornacek $deselectAllButton = $('<a href="#" class="sol-deselect-all"/>').html(this.config.texts.selectNone).click(function (e) { 972*87e82791SAdam Hornacek self.deselectAll(); 973*87e82791SAdam Hornacek e.preventDefault(); 974*87e82791SAdam Hornacek return false; 975*87e82791SAdam Hornacek }), 976*87e82791SAdam Hornacek $selectAllButton = $('<a href="#" class="sol-select-all"/>').html(this.config.texts.selectAll).click(function (e) { 977*87e82791SAdam Hornacek self.selectAll(); 978*87e82791SAdam Hornacek e.preventDefault(); 979*87e82791SAdam Hornacek return false; 980*87e82791SAdam Hornacek }); 981*87e82791SAdam Hornacek 982*87e82791SAdam Hornacek this.$actionButtons = $('<div class="sol-action-buttons"/>').append($selectAllButton).append($deselectAllButton).append('<div class="sol-clearfix"/>'); 983*87e82791SAdam Hornacek this.$selectionContainer.prepend(this.$actionButtons); 984*87e82791SAdam Hornacek } 985*87e82791SAdam Hornacek }, 986*87e82791SAdam Hornacek 987*87e82791SAdam Hornacek _selectionChange: function ($changeItem, skipCallback) { 988*87e82791SAdam Hornacek 989*87e82791SAdam Hornacek // apply state to original select if necessary 990*87e82791SAdam Hornacek // helps to keep old legacy code running which depends 991*87e82791SAdam Hornacek // on retrieving the value via jQuery option selectors 992*87e82791SAdam Hornacek // e.g. $('#myPreviousSelectWhichNowIsSol').val() 993*87e82791SAdam Hornacek if (this.$originalElement && this.$originalElement.prop('tagName').toLowerCase() === 'select') { 994*87e82791SAdam Hornacek var self = this; 995*87e82791SAdam Hornacek if (this.valMap == null) { 996*87e82791SAdam Hornacek this.$originalElement.find('option').each(function (index, item) { 997*87e82791SAdam Hornacek var $currentOriginalOption = $(item); 998*87e82791SAdam Hornacek if ($currentOriginalOption.val() === $changeItem.val()) { 999*87e82791SAdam Hornacek $currentOriginalOption.prop('selected', $changeItem.prop('checked')); 1000*87e82791SAdam Hornacek self.$originalElement.trigger('change'); 1001*87e82791SAdam Hornacek return false; // stop the loop 1002*87e82791SAdam Hornacek } 1003*87e82791SAdam Hornacek }); 1004*87e82791SAdam Hornacek } else { 1005*87e82791SAdam Hornacek var mappedVal = this.valMap.get($changeItem.val()); 1006*87e82791SAdam Hornacek if (mappedVal) { 1007*87e82791SAdam Hornacek mappedVal.prop('selected', $changeItem.prop('checked')); 1008*87e82791SAdam Hornacek self.$originalElement.trigger('change'); 1009*87e82791SAdam Hornacek } 1010*87e82791SAdam Hornacek } 1011*87e82791SAdam Hornacek } 1012*87e82791SAdam Hornacek 1013*87e82791SAdam Hornacek if ($changeItem.prop('checked')) { 1014*87e82791SAdam Hornacek this._addSelectionDisplayItem($changeItem); 1015*87e82791SAdam Hornacek } else { 1016*87e82791SAdam Hornacek this._removeSelectionDisplayItem($changeItem); 1017*87e82791SAdam Hornacek } 1018*87e82791SAdam Hornacek 1019*87e82791SAdam Hornacek if (this.config.multiple) { 1020*87e82791SAdam Hornacek // update position of selection container 1021*87e82791SAdam Hornacek // to allow selecting more entries 1022*87e82791SAdam Hornacek this.config.scrollTarget.trigger('scroll'); 1023*87e82791SAdam Hornacek } else { 1024*87e82791SAdam Hornacek // only one option selectable 1025*87e82791SAdam Hornacek // close selection container 1026*87e82791SAdam Hornacek this.close(); 1027*87e82791SAdam Hornacek } 1028*87e82791SAdam Hornacek 1029*87e82791SAdam Hornacek if (!skipCallback && $.isFunction(this.config.events.onChange)) { 1030*87e82791SAdam Hornacek this.config.events.onChange.call(this, this, $changeItem); 1031*87e82791SAdam Hornacek } 1032*87e82791SAdam Hornacek }, 1033*87e82791SAdam Hornacek 1034*87e82791SAdam Hornacek _setXItemsSelected: function() { 1035*87e82791SAdam Hornacek if (this.config.maxShow !== 0 && this.numSelected > this.config.maxShow) { 1036*87e82791SAdam Hornacek var xItemsText = this.config.texts.itemsSelected.replace('{$a}', 1037*87e82791SAdam Hornacek this.numSelected - this.config.maxShow); 1038*87e82791SAdam Hornacek this.$xItemsSelected.html('<div class="sol-selected-display-item-text">' + 1039*87e82791SAdam Hornacek xItemsText + '<div>'); 1040*87e82791SAdam Hornacek this.$showSelectionContainer.append(this.$xItemsSelected); 1041*87e82791SAdam Hornacek this.$xItemsSelected.show(); 1042*87e82791SAdam Hornacek } else { 1043*87e82791SAdam Hornacek this.$xItemsSelected.hide(); 1044*87e82791SAdam Hornacek } 1045*87e82791SAdam Hornacek }, 1046*87e82791SAdam Hornacek 1047*87e82791SAdam Hornacek _addSelectionDisplayItem: function ($changedItem) { 1048*87e82791SAdam Hornacek this.numSelected = 1 + this.numSelected; 1049*87e82791SAdam Hornacek if (this.config.numSelectedItem) { 1050*87e82791SAdam Hornacek this.config.numSelectedItem.val(this.numSelected); 1051*87e82791SAdam Hornacek } 1052*87e82791SAdam Hornacek 1053*87e82791SAdam Hornacek if (this.config.maxShow !== 0 && this.numSelected > this.config.maxShow) { 1054*87e82791SAdam Hornacek if (this.valMap == null) { 1055*87e82791SAdam Hornacek this._setXItemsSelected(); 1056*87e82791SAdam Hornacek } 1057*87e82791SAdam Hornacek } else { 1058*87e82791SAdam Hornacek this._buildSelectionDisplayItem($changedItem); 1059*87e82791SAdam Hornacek } 1060*87e82791SAdam Hornacek }, 1061*87e82791SAdam Hornacek 1062*87e82791SAdam Hornacek _buildSelectionDisplayItem: function ($changedItem) { 1063*87e82791SAdam Hornacek var solOptionItem = $changedItem.data('sol-item'), 1064*87e82791SAdam Hornacek self = this, 1065*87e82791SAdam Hornacek $existingDisplayItem, 1066*87e82791SAdam Hornacek $displayItemText; 1067*87e82791SAdam Hornacek 1068*87e82791SAdam Hornacek /* 1069*87e82791SAdam Hornacek * Modified for OpenGrok in 2016, 2019. 1070*87e82791SAdam Hornacek */ 1071*87e82791SAdam Hornacek var label = solOptionItem.label; 1072*87e82791SAdam Hornacek if ($changedItem.data('messages-available')) { 1073*87e82791SAdam Hornacek label += ' <span class="'; 1074*87e82791SAdam Hornacek label += 'note-' + $changedItem.data('messages-level'); 1075*87e82791SAdam Hornacek label += ' important-note important-note-rounded" '; 1076*87e82791SAdam Hornacek label += 'title="Some message is present for this project.'; 1077*87e82791SAdam Hornacek label += ' Find more info in the project list.">!</span>' 1078*87e82791SAdam Hornacek } 1079*87e82791SAdam Hornacek 1080*87e82791SAdam Hornacek $displayItemText = $('<span class="sol-selected-display-item-text" />').html(label); 1081*87e82791SAdam Hornacek $existingDisplayItem = $('<div class="sol-selected-display-item"/>') 1082*87e82791SAdam Hornacek .append($displayItemText) 1083*87e82791SAdam Hornacek .attr('title', solOptionItem.tooltip) 1084*87e82791SAdam Hornacek .data('label', solOptionItem.label) 1085*87e82791SAdam Hornacek .appendTo(this.$showSelectionContainer) 1086*87e82791SAdam Hornacek .dblclick(function () { // Modified for OpenGrok in 2017. 1087*87e82791SAdam Hornacek $changedItem.dblclick(); 1088*87e82791SAdam Hornacek }); 1089*87e82791SAdam Hornacek 1090*87e82791SAdam Hornacek // show remove button on display items if not disabled and null selection allowed 1091*87e82791SAdam Hornacek if ((this.config.multiple || this.config.allowNullSelection) && !$changedItem.prop('disabled')) { 1092*87e82791SAdam Hornacek $('<span class="sol-quick-delete"/>') 1093*87e82791SAdam Hornacek .html(this.config.texts.quickDelete) 1094*87e82791SAdam Hornacek .click(function () { // deselect the project and refresh the search 1095*87e82791SAdam Hornacek $changedItem 1096*87e82791SAdam Hornacek .prop('checked', false) 1097*87e82791SAdam Hornacek .trigger('change'); 1098*87e82791SAdam Hornacek /* 1099*87e82791SAdam Hornacek * Modified for OpenGrok in 2017. 1100*87e82791SAdam Hornacek */ 1101*87e82791SAdam Hornacek if (self.config.quickDeleteForm) { 1102*87e82791SAdam Hornacek if (self.config.quickDeletePermit) { 1103*87e82791SAdam Hornacek if (self.config.quickDeletePermit()) { 1104*87e82791SAdam Hornacek self.config.quickDeleteForm.submit(); 1105*87e82791SAdam Hornacek } 1106*87e82791SAdam Hornacek } else { 1107*87e82791SAdam Hornacek self.config.quickDeleteForm.submit(); 1108*87e82791SAdam Hornacek } 1109*87e82791SAdam Hornacek } 1110*87e82791SAdam Hornacek }) 1111*87e82791SAdam Hornacek .prependTo($existingDisplayItem); 1112*87e82791SAdam Hornacek } 1113*87e82791SAdam Hornacek 1114*87e82791SAdam Hornacek solOptionItem.displaySelectionItem = $existingDisplayItem; 1115*87e82791SAdam Hornacek }, 1116*87e82791SAdam Hornacek 1117*87e82791SAdam Hornacek _removeSelectionDisplayItem: function ($changedItem) { 1118*87e82791SAdam Hornacek var solOptionItem = $changedItem.data('sol-item'), 1119*87e82791SAdam Hornacek $myDisplayItem = solOptionItem.displaySelectionItem; 1120*87e82791SAdam Hornacek 1121*87e82791SAdam Hornacek var wasExceeding = this.config.maxShow !== 0 && this.numSelected > this.config.maxShow; 1122*87e82791SAdam Hornacek this.numSelected = this.numSelected - 1; 1123*87e82791SAdam Hornacek if (this.config.numSelectedItem) { 1124*87e82791SAdam Hornacek this.config.numSelectedItem.val(this.numSelected); 1125*87e82791SAdam Hornacek } 1126*87e82791SAdam Hornacek 1127*87e82791SAdam Hornacek if ($myDisplayItem) { 1128*87e82791SAdam Hornacek $myDisplayItem.remove(); 1129*87e82791SAdam Hornacek solOptionItem.displaySelectionItem = undefined; 1130*87e82791SAdam Hornacek 1131*87e82791SAdam Hornacek /* 1132*87e82791SAdam Hornacek * N.b. for bulk mode, wasExceeding handling is off since only 1133*87e82791SAdam Hornacek * Clear or Invert-Selection would cause this function to be 1134*87e82791SAdam Hornacek * called. For Clear, there won't be any selected items at the 1135*87e82791SAdam Hornacek * end, so wasExceeding is irrelevant. For Invert-Selection, 1136*87e82791SAdam Hornacek * checked options are unchecked first -- i.e. we go to zero 1137*87e82791SAdam Hornacek * this.numSelected first -- so normal _addSelectionDisplayItem 1138*87e82791SAdam Hornacek * takes care of things. 1139*87e82791SAdam Hornacek */ 1140*87e82791SAdam Hornacek 1141*87e82791SAdam Hornacek if (wasExceeding && this.valMap == null) { 1142*87e82791SAdam Hornacek var self = this; 1143*87e82791SAdam Hornacek this.$selectionContainer 1144*87e82791SAdam Hornacek .find('.sol-option input[type="checkbox"]:not([disabled]):checked') 1145*87e82791SAdam Hornacek .each(function (index, item) { 1146*87e82791SAdam Hornacek var $currentOptionItem = $(item); 1147*87e82791SAdam Hornacek if ($currentOptionItem.data('sol-item').displaySelectionItem == null) { 1148*87e82791SAdam Hornacek self._buildSelectionDisplayItem($currentOptionItem); 1149*87e82791SAdam Hornacek return false; 1150*87e82791SAdam Hornacek } 1151*87e82791SAdam Hornacek }); 1152*87e82791SAdam Hornacek } 1153*87e82791SAdam Hornacek } 1154*87e82791SAdam Hornacek if (this.valMap == null) { 1155*87e82791SAdam Hornacek this._setXItemsSelected(); 1156*87e82791SAdam Hornacek } 1157*87e82791SAdam Hornacek }, 1158*87e82791SAdam Hornacek 1159*87e82791SAdam Hornacek _setNoResultsItemVisible: function (visible) { 1160*87e82791SAdam Hornacek if (visible) { 1161*87e82791SAdam Hornacek this.$noResultsItem.show(); 1162*87e82791SAdam Hornacek this.$selection.hide(); 1163*87e82791SAdam Hornacek 1164*87e82791SAdam Hornacek if (this.$actionButtons) { 1165*87e82791SAdam Hornacek this.$actionButtons.hide(); 1166*87e82791SAdam Hornacek } 1167*87e82791SAdam Hornacek } else { 1168*87e82791SAdam Hornacek this.$noResultsItem.hide(); 1169*87e82791SAdam Hornacek this.$selection.show(); 1170*87e82791SAdam Hornacek 1171*87e82791SAdam Hornacek if (this.$actionButtons) { 1172*87e82791SAdam Hornacek this.$actionButtons.show(); 1173*87e82791SAdam Hornacek } 1174*87e82791SAdam Hornacek } 1175*87e82791SAdam Hornacek }, 1176*87e82791SAdam Hornacek 1177*87e82791SAdam Hornacek _buildValMap: function () { 1178*87e82791SAdam Hornacek if (this.$originalElement && this.$originalElement.prop('tagName').toLowerCase() === 'select') { 1179*87e82791SAdam Hornacek var self = this; 1180*87e82791SAdam Hornacek this.valMap = new Map(); 1181*87e82791SAdam Hornacek this.$originalElement.find('option').each(function (index, item) { 1182*87e82791SAdam Hornacek var $currentOriginalOption = $(item); 1183*87e82791SAdam Hornacek self.valMap.set($currentOriginalOption.val(), $currentOriginalOption); 1184*87e82791SAdam Hornacek }); 1185*87e82791SAdam Hornacek } 1186*87e82791SAdam Hornacek }, 1187*87e82791SAdam Hornacek 1188*87e82791SAdam Hornacek isOpen: function () { 1189*87e82791SAdam Hornacek return this.$container.hasClass('sol-active'); 1190*87e82791SAdam Hornacek }, 1191*87e82791SAdam Hornacek 1192*87e82791SAdam Hornacek isClosed: function () { 1193*87e82791SAdam Hornacek return !this.isOpen(); 1194*87e82791SAdam Hornacek }, 1195*87e82791SAdam Hornacek 1196*87e82791SAdam Hornacek toggle: function () { 1197*87e82791SAdam Hornacek if (this.isOpen()) { 1198*87e82791SAdam Hornacek this.close(); 1199*87e82791SAdam Hornacek } else { 1200*87e82791SAdam Hornacek this.open(); 1201*87e82791SAdam Hornacek } 1202*87e82791SAdam Hornacek }, 1203*87e82791SAdam Hornacek 1204*87e82791SAdam Hornacek open: function () { 1205*87e82791SAdam Hornacek if (this.isClosed()) { 1206*87e82791SAdam Hornacek this.$container.addClass('sol-active'); 1207*87e82791SAdam Hornacek this.config.scrollTarget.bind('scroll', this.internalScrollWrapper).trigger('scroll'); 1208*87e82791SAdam Hornacek $(window).on('resize', this.internalScrollWrapper); 1209*87e82791SAdam Hornacek 1210*87e82791SAdam Hornacek if ($.isFunction(this.config.events.onOpen)) { 1211*87e82791SAdam Hornacek this.config.events.onOpen.call(this, this); 1212*87e82791SAdam Hornacek } 1213*87e82791SAdam Hornacek } 1214*87e82791SAdam Hornacek }, 1215*87e82791SAdam Hornacek 1216*87e82791SAdam Hornacek close: function () { 1217*87e82791SAdam Hornacek if (this.isOpen()) { 1218*87e82791SAdam Hornacek this._setKeyBoardNavigationMode(false); 1219*87e82791SAdam Hornacek 1220*87e82791SAdam Hornacek 1221*87e82791SAdam Hornacek this.$container.removeClass('sol-active'); 1222*87e82791SAdam Hornacek this.config.scrollTarget.unbind('scroll', this.internalScrollWrapper); 1223*87e82791SAdam Hornacek $(window).off('resize'); 1224*87e82791SAdam Hornacek 1225*87e82791SAdam Hornacek // reset search on close 1226*87e82791SAdam Hornacek this.$input.val(''); 1227*87e82791SAdam Hornacek this._applySearchTermFilter(); 1228*87e82791SAdam Hornacek 1229*87e82791SAdam Hornacek // clear to recalculate position again the next time sol is opened 1230*87e82791SAdam Hornacek this.config.displayContainerAboveInput = undefined; 1231*87e82791SAdam Hornacek 1232*87e82791SAdam Hornacek if ($.isFunction(this.config.events.onClose)) { 1233*87e82791SAdam Hornacek this.config.events.onClose.call(this, this); 1234*87e82791SAdam Hornacek } 1235*87e82791SAdam Hornacek } 1236*87e82791SAdam Hornacek }, 1237*87e82791SAdam Hornacek /* 1238*87e82791SAdam Hornacek * Modified for OpenGrok in 2016. 1239*87e82791SAdam Hornacek */ 1240*87e82791SAdam Hornacek selectAll: function (/* string or undefined */optgroup) { 1241*87e82791SAdam Hornacek if (this.config.multiple) { 1242*87e82791SAdam Hornacek this._buildValMap(); 1243*87e82791SAdam Hornacek 1244*87e82791SAdam Hornacek var $changedInputs = !optgroup ? this.$selectionContainer 1245*87e82791SAdam Hornacek : this.$selectionContainer 1246*87e82791SAdam Hornacek .find(".sol-optiongroup-label") 1247*87e82791SAdam Hornacek .filter(function () { 1248*87e82791SAdam Hornacek return $(this).text() === optgroup; 1249*87e82791SAdam Hornacek }).closest('.sol-optiongroup') 1250*87e82791SAdam Hornacek 1251*87e82791SAdam Hornacek $changedInputs = $changedInputs.find('input[type="checkbox"]:not([disabled], :checked)') 1252*87e82791SAdam Hornacek .prop('checked', true) 1253*87e82791SAdam Hornacek .trigger('change', true); 1254*87e82791SAdam Hornacek 1255*87e82791SAdam Hornacek this.config.closeOnClick && this.close(); 1256*87e82791SAdam Hornacek 1257*87e82791SAdam Hornacek if ($.isFunction(this.config.events.onChange)) { 1258*87e82791SAdam Hornacek this.config.events.onChange.call(this, this, $changedInputs); 1259*87e82791SAdam Hornacek } 1260*87e82791SAdam Hornacek 1261*87e82791SAdam Hornacek this.valMap = null; 1262*87e82791SAdam Hornacek this._setXItemsSelected(); 1263*87e82791SAdam Hornacek } 1264*87e82791SAdam Hornacek }, 1265*87e82791SAdam Hornacek /* 1266*87e82791SAdam Hornacek * Modified for OpenGrok in 2016, 2019. 1267*87e82791SAdam Hornacek */ 1268*87e82791SAdam Hornacek invert: function () { 1269*87e82791SAdam Hornacek if (this.config.multiple) { 1270*87e82791SAdam Hornacek this._buildValMap(); 1271*87e82791SAdam Hornacek 1272*87e82791SAdam Hornacek var $closedInputs = this.$selectionContainer 1273*87e82791SAdam Hornacek .find('input[type="checkbox"][name=project]:not([disabled], :checked)') 1274*87e82791SAdam Hornacek var $openedInputs = this.$selectionContainer 1275*87e82791SAdam Hornacek .find('input[type="checkbox"][name=project]').filter('[disabled], :checked') 1276*87e82791SAdam Hornacek 1277*87e82791SAdam Hornacek $openedInputs.prop('checked', false) 1278*87e82791SAdam Hornacek .trigger('change', true); 1279*87e82791SAdam Hornacek $closedInputs.prop('checked', true) 1280*87e82791SAdam Hornacek .trigger('change', true) 1281*87e82791SAdam Hornacek 1282*87e82791SAdam Hornacek this.config.closeOnClick && this.close(); 1283*87e82791SAdam Hornacek 1284*87e82791SAdam Hornacek if ($.isFunction(this.config.events.onChange)) { 1285*87e82791SAdam Hornacek this.config.events.onChange.call(this, this, $openedInputs.add($closedInputs)); 1286*87e82791SAdam Hornacek } 1287*87e82791SAdam Hornacek 1288*87e82791SAdam Hornacek this.valMap = null; 1289*87e82791SAdam Hornacek this._setXItemsSelected(); 1290*87e82791SAdam Hornacek } 1291*87e82791SAdam Hornacek }, 1292*87e82791SAdam Hornacek /* 1293*87e82791SAdam Hornacek * Modified for OpenGrok in 2016. 1294*87e82791SAdam Hornacek */ 1295*87e82791SAdam Hornacek deselectAll: function ( /* string or undefined */ optgroup) { 1296*87e82791SAdam Hornacek if (this.config.multiple) { 1297*87e82791SAdam Hornacek this._buildValMap(); 1298*87e82791SAdam Hornacek 1299*87e82791SAdam Hornacek var $changedInputs = !optgroup ? this.$selectionContainer 1300*87e82791SAdam Hornacek : this.$selectionContainer 1301*87e82791SAdam Hornacek .find(".sol-optiongroup-label") 1302*87e82791SAdam Hornacek .filter(function () { 1303*87e82791SAdam Hornacek return $(this).text() === optgroup; 1304*87e82791SAdam Hornacek }).closest('.sol-optiongroup') 1305*87e82791SAdam Hornacek 1306*87e82791SAdam Hornacek $changedInputs = $changedInputs.find('.sol-option input[type="checkbox"]:not([disabled]):checked') 1307*87e82791SAdam Hornacek .prop('checked', false) 1308*87e82791SAdam Hornacek .trigger('change', true); 1309*87e82791SAdam Hornacek 1310*87e82791SAdam Hornacek this.config.closeOnClick && this.close(); 1311*87e82791SAdam Hornacek 1312*87e82791SAdam Hornacek if ($.isFunction(this.config.events.onChange)) { 1313*87e82791SAdam Hornacek this.config.events.onChange.call(this, this, $changedInputs); 1314*87e82791SAdam Hornacek } 1315*87e82791SAdam Hornacek 1316*87e82791SAdam Hornacek this.valMap = null; 1317*87e82791SAdam Hornacek this._setXItemsSelected(); 1318*87e82791SAdam Hornacek } 1319*87e82791SAdam Hornacek }, 1320*87e82791SAdam Hornacek 1321*87e82791SAdam Hornacek selectRadio: function(val) { 1322*87e82791SAdam Hornacek this.$selectionContainer.find('input[type="radio"]') 1323*87e82791SAdam Hornacek .each(function (index, item) { 1324*87e82791SAdam Hornacek var $currentOptionItem = $(item); 1325*87e82791SAdam Hornacek if ($currentOptionItem.val() === val) { 1326*87e82791SAdam Hornacek if (!$currentOptionItem.is(':checked')) { 1327*87e82791SAdam Hornacek $currentOptionItem.prop("checked", true).trigger('change', true); 1328*87e82791SAdam Hornacek } 1329*87e82791SAdam Hornacek return false; 1330*87e82791SAdam Hornacek } 1331*87e82791SAdam Hornacek }); 1332*87e82791SAdam Hornacek }, 1333*87e82791SAdam Hornacek 1334*87e82791SAdam Hornacek getSelection: function () { 1335*87e82791SAdam Hornacek return this.$selection.find('input:checked'); 1336*87e82791SAdam Hornacek } 1337*87e82791SAdam Hornacek }; 1338*87e82791SAdam Hornacek 1339*87e82791SAdam Hornacek // jquery plugin boiler plate code 1340*87e82791SAdam Hornacek SearchableOptionList.defaults = SearchableOptionList.prototype.defaults; 1341*87e82791SAdam Hornacek window.SearchableOptionList = SearchableOptionList; 1342*87e82791SAdam Hornacek 1343*87e82791SAdam Hornacek $.fn.searchableOptionList = function (options) { 1344*87e82791SAdam Hornacek var result = []; 1345*87e82791SAdam Hornacek this.each(function () { 1346*87e82791SAdam Hornacek var $this = $(this), 1347*87e82791SAdam Hornacek $alreadyInitializedSol = $this.data(SearchableOptionList.prototype.DATA_KEY); 1348*87e82791SAdam Hornacek 1349*87e82791SAdam Hornacek if ($alreadyInitializedSol) { 1350*87e82791SAdam Hornacek result.push($alreadyInitializedSol); 1351*87e82791SAdam Hornacek } else { 1352*87e82791SAdam Hornacek var newSol = new SearchableOptionList($this, options); 1353*87e82791SAdam Hornacek result.push(newSol); 1354*87e82791SAdam Hornacek 1355*87e82791SAdam Hornacek setTimeout(function() { 1356*87e82791SAdam Hornacek newSol.init(); 1357*87e82791SAdam Hornacek }, 0); 1358*87e82791SAdam Hornacek } 1359*87e82791SAdam Hornacek }); 1360*87e82791SAdam Hornacek 1361*87e82791SAdam Hornacek if (result.length === 1) { 1362*87e82791SAdam Hornacek return result[0]; 1363*87e82791SAdam Hornacek } 1364*87e82791SAdam Hornacek 1365*87e82791SAdam Hornacek return result; 1366*87e82791SAdam Hornacek }; 1367*87e82791SAdam Hornacek 1368*87e82791SAdam Hornacek}(jQuery, window, document)); 1369