PageSourceSearch

https://www.icprjc.edu/js/bootstrap-combobox.js

js icprjc.edu collected 2026-10-04 18:05:02 UTC 13,802 bytes, 462 lines download raw bytes

1/* =============================================================
2 * bootstrap-combobox.js v1.1.8
3 * =============================================================
4 * Copyright 2012 Daniel Farrell
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 * ============================================================ */
18
19(function( $ ) {
20
21 "use strict";
22
23 /* COMBOBOX PUBLIC CLASS DEFINITION
24  * ================================ */
25
26  var Combobox = function ( element, options ) {
27    this.options = $.extend({}, $.fn.combobox.defaults, options);
28    this.template = this.options.template || this.template
29    this.$source = $(element);
30    this.$container = this.setup();
31    this.$element = this.$container.find('input[type=text]');
32    this.$target = this.$container.find('input[type=hidden]');
33    this.$button = this.$container.find('.dropdown-toggle');
34    this.$menu = $(this.options.menu).appendTo('body');
35    this.matcher = this.options.matcher || this.matcher;
36    this.sorter = this.options.sorter || this.sorter;
37    this.highlighter = this.options.highlighter || this.highlighter;
38    this.shown = false;
39    this.selected = false;
40    this.refresh();
41    this.transferAttributes();
42    this.listen();
43  };
44
45  Combobox.prototype = {
46
47    constructor: Combobox
48
49  , setup: function () {
50      var combobox = $(this.template());
51      this.$source.before(combobox);
52      this.$source.hide();
53      return combobox;
54    }
55
56  , disable: function() {
57      this.$element.prop('disabled', true);
58      this.$button.attr('disabled', true);
59      this.disabled = true;
60      this.$container.addClass('combobox-disabled');
61    }
62
63  , enable: function() {
64      this.$element.prop('disabled', false);
65      this.$button.attr('disabled', false);
66      this.disabled = false;
67      this.$container.removeClass('combobox-disabled');
68    }
69  , parse: function () {
70      var that = this
71        , map = {}
72        , source = []
73        , selected = false
74        , selectedValue = '';
75      this.$source.find('option').each(function() {
76        var option = $(this);
77        if (option.val() === '') {
78          that.options.placeholder = option.text();
79          return;
80        }
81        map[option.text()] = option.val();
82        source.push(option.text());
83        if (option.prop('selected')) {
84          selected = option.text();
85          selectedValue = option.val();
86        }
87      })
88      this.map = map;
89      if (selected) {
90        this.$element.val(selected);
91        this.$target.val(selectedValue);
92        this.$container.addClass('combobox-selected');
93        this.selected = true;
94      }
95      return source;
96    }
97
98  , transferAttributes: function() {
99    this.options.placeholder = this.$source.attr('data-placeholder') || this.options.placeholder
100    if(this.options.appendId !== undefined) {
101    	this.$element.attr('id', this.$source.attr('id') + this.options.appendId);
102    }
103    this.$element.attr('placeholder', this.options.placeholder)
104    this.$target.prop('name', this.$source.prop('name'))
105    this.$target.val(this.$source.val())
106    this.$source.removeAttr('name')  // Remove from source otherwise form will pass parameter twice.
107    this.$element.attr('required', this.$source.attr('required'))
108    this.$element.attr('rel', this.$source.attr('rel'))
109    this.$element.attr('title', this.$source.attr('title'))
110    this.$element.attr('class', this.$source.attr('class'))
111    this.$element.attr('tabindex', this.$source.attr('tabindex'))
112    this.$source.removeAttr('tabindex')
113    if (this.$source.attr('disabled')!==undefined)
114      this.disable();
115  }
116
117  , select: function () {
118      var val = this.$menu.find('.active').attr('data-value');
119      this.$element.val(this.updater(val)).trigger('change');
120      this.$target.val(this.map[val]).trigger('change');
121      this.$source.val(this.map[val]).trigger('change');
122      this.$container.addClass('combobox-selected');
123      this.selected = true;
124      return this.hide();
125    }
126
127  , updater: function (item) {
128      return item;
129    }
130
131  , show: function () {
132      var pos = $.extend({}, this.$element.position(), {
133        height: this.$element[0].offsetHeight
134      });
135
136      this.$menu
137        .insertAfter(this.$element)
138        .css({
139          top: pos.top + pos.height
140        , left: pos.left
141        })
142        .show();
143
144      $('.dropdown-menu').on('mousedown', $.proxy(this.scrollSafety, this));
145
146      this.shown = true;
147      return this;
148    }
149
150  , hide: function () {
151      this.$menu.hide();
152      $('.dropdown-menu').off('mousedown', $.proxy(this.scrollSafety, this));
153      this.$element.on('blur', $.proxy(this.blur, this));
154      this.shown = false;
155      return this;
156    }
157
158  , lookup: function (event) {
159      this.query = this.$element.val();
160      return this.process(this.source);
161    }
162
163  , process: function (items) {
164      var that = this;
165
166      items = $.grep(items, function (item) {
167        return that.matcher(item);
168      })
169
170      items = this.sorter(items);
171
172      if (!items.length) {
173        return this.shown ? this.hide() : this;
174      }
175
176      return this.render(items.slice(0, this.options.items)).show();
177    }
178
179  , template: function() {
180      if (this.options.bsVersion == '2') {
181        return '<div class="combobox-container"><input type="hidden" /> <div class="input-append"> <input type="text" autocomplete="off" /> <span class="add-on dropdown-toggle" data-dropdown="dropdown"> <span class="caret"/> <i class="icon-remove"/>
181 </span> </div> </div>'
182      } else {
183        return '<div class="combobox-container"> <input type="hidden" /> <div class="input-group"> <input type="text" autocomplete="off" /> <span class="input-group-addon dropdown-toggle" data-dropdown="dropdown"> <span class="caret" /> <span class="glyphicon glyphicon-remove" /> </span> </div> </div>'
184      }
185    }
186
187  , matcher: function (item) {
188      return ~item.toLowerCase().indexOf(this.query.toLowerCase());
189    }
190
191  , sorter: function (items) {
192      var beginswith = []
193        , caseSensitive = []
194        , caseInsensitive = []
195        , item;
196
197      while (item = items.shift()) {
198        if (!item.toLowerCase().indexOf(this.query.toLowerCase())) {beginswith.push(item);}
199        else if (~item.indexOf(this.query)) {caseSensitive.push(item);}
200        else {caseInsensitive.push(item);}
201      }
202
203      return beginswith.concat(caseSensitive, caseInsensitive);
204    }
205
206  , highlighter: function (item) {
207      var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
208      return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
209        return '<strong>' + match + '</strong>';
210      })
211    }
212
213  , render: function (items) {
214      var that = this;
215
216      items = $(items).map(function (i, item) {
217        i = $(that.options.item).attr('data-value', item);
218        i.find('a').html(that.highlighter(item));
219        return i[0];
220      })
221
222      items.first().addClass('active');
223      this.$menu.html(items);
224      return this;
225    }
226
227  , next: function (event) {
228      var active = this.$menu.find('.active').removeClass('active')
229        , next = active.next();
230
231      if (!next.length) {
232        next = $(this.$menu.find('li')[0]);
233      }
234
235      next.addClass('active');
236    }
237
238  , prev: function (event) {
239      var active = this.$menu.find('.active').removeClass('active')
240        , prev = active.prev();
241
242      if (!prev.length) {
243        prev = this.$menu.find('li').last();
244      }
245
246      prev.addClass('active');
247    }
248
249  , toggle: function () {
250    if (!this.disabled) {
251      if (this.$container.hasClass('combobox-selected')) {
252        this.clearTarget();
253        this.triggerChange();
254        this.clearElement();
255      } else {
256        if (this.shown) {
257          this.hide();
258        } else {
259          this.clearElement();
260          this.lookup();
261        }
262      }
263    }
264  }
265
266  , scrollSafety: function(e) {
267      if (e.target.tagName == 'UL') {
268          this.$element.off('blur');
269      }
270  }
271  , clearElement: function () {
272    this.$element.val('').focus();
273  }
274
275  , clearTarget: function () {
276    this.$source.val('');
277    this.$target.val('');
278    this.$container.removeClass('combobox-selected');
279    this.selected = false;
280  }
281
282  , triggerChange: function () {
283    this.$source.trigger('change');
284  }
285
286  , refresh: function () {
287    this.source = this.parse();
288    this.options.items = this.source.length;
289  }
290
291  , listen: function () {
292      this.$element
293        .on('focus',    $.proxy(this.focus, this))
294        .on('blur',     $.proxy(this.blur, this))
295        .on('keypress', $.proxy(this.keypress, this))
296        .on('keyup',    $.proxy(this.keyup, this));
297
298      if (this.eventSupported('keydown')) {
299        this.$element.on('keydown', $.proxy(this.keydown, this));
300      }
301
302      this.$menu
303        .on('click', $.proxy(this.click, this))
304        .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
305        .on('mouseleave', 'li', $.proxy(this.mouseleave, this));
306
307      this.$button
308        .on('click', $.proxy(this.toggle, this));
309    }
310
311  , eventSupported: function(eventName) {
312      var isSupported = eventName in this.$element;
313      if (!isSupported) {
314        this.$element.setAttribute(eventName, 'return;');
315        isSupported = typeof this.$element[eventName] === 'function';
316      }
317      return isSupported;
318    }
319
320  , move: function (e) {
321      if (!this.shown) {return;}
322
323      switch(e.keyCode) {
324        case 9: // tab
325        case 13: // enter
326        case 27: // escape
327          e.preventDefault();
328          break;
329
330        case 38: // up arrow
331          e.preventDefault();
332          this.prev();
333          this.fixMenuScroll();
334          break;
335
336        case 40: // down arrow
337          e.preventDefault();
338          this.next();
339          this.fixMenuScroll();
340          break;
341      }
342
343      e.stopPropagation();
344    }
345
346  , fixMenuScroll: function(){
347      var active = this.$menu.find('.active');
348      if(active.length){
349          var top = active.position().top;
350          var bottom = top + active.height();
351          var scrollTop = this.$menu.scrollTop();
352          var menuHeight = this.$menu.height();
353          if(bottom > menuHeight){
354              this.$menu.scrollTop(scrollTop + bottom - menuHeight);
355          } else if(top < 0){
356              this.$menu.scrollTop(scrollTop + top);
357          }
358      }
359  }
360
361  , keydown: function (e) {
362      this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27]);
363      this.move(e);
364    }
365
366  , keypress: function (e) {
367      if (this.suppressKeyPressRepeat) {return;}
368      this.move(e);
369    }
370
371  , keyup: function (e) {
372      switch(e.keyCode) {
373        case 40: // down arrow
374         if (!this.shown){
375           this.toggle();
376         }
377         break;
378        case 39: // right arrow
379        case 38: // up arrow
380        case 37: // left arrow
381        case 36: // home
382        case 35: // end
383        case 16: // shift
384        case 17: // ctrl
385        case 18: // alt
386          break;
387
388        case 9: // tab
389        case 13: // enter
390          if (!this.shown) {return;}
391          this.select();
392          break;
393
394        case 27: // escape
395          if (!this.shown) {return;}
396          this.hide();
397          break;
398
399        default:
400          this.clearTarget();
401          this.lookup();
402      }
403
404      e.stopPropagation();
405      e.preventDefault();
406  }
407
408  , focus: function (e) {
409      this.focused = true;
410    }
411
412  , blur: function (e) {
413      var that = this;
414      this.focused = false;
415      var val = this.$element.val();
416      if (!this.selected && val !== '' ) {
417        this.$element.val('');
418        this.$source.val('').trigger('change');
419        this.$target.val('').trigger('change');
420      }
421      if (!this.mousedover && this.shown) {setTimeout(function () { that.hide(); }, 200);}
422    }
423
424  , click: function (e) {
425      e.stopPropagation();
426      e.preventDefault();
427      this.select();
428      this.$element.focus();
429    }
430
431  , mouseenter: function (e) {
432      this.mousedover = true;
433      this.$menu.find('.active').removeClass('active');
434      $(e.currentTarget).addClass('active');
435    }
436
437  , mouseleave: function (e) {
438      this.mousedover = false;
439    }
440  };
441
442  /* COMBOBOX PLUGIN DEFINITION
443   * =========================== */
444  $.fn.combobox = function ( option ) {
445    return this.each(function () {
446      var $this = $(this)
447        , data = $this.data('combobox')
448        , options = typeof option == 'object' && option;
449      if(!data) {$this.data('combobox', (data = new Combobox(this, options)));}
450      if (typeof option == 'string') {data[option]();}
451    });
452  };
453
454  $.fn.combobox.defaults = {
455    bsVersion: '4'
456  , menu: '<ul class="typeahead typeahead-long dropdown-menu"></ul>'
457  , item: '<li><a href="#" class="dropdown-item"></a></li>'
458  };
459
460  $.fn.combobox.Constructor = Combobox;
461
462}( window.jQuery ));

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.