This repository has been archived on 2023-07-11. You can view files and clone it, but cannot push or open issues or pull requests.
blarg/resources/public/js/bootstrap-combobox.js

266 lines
7.6 KiB
JavaScript

/* =============================================================
* bootstrap-combobox.js v1.1.1
* =============================================================
* Copyright 2012 Daniel Farrell
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================ */
!function($) {
"use strict";
var Combobox = function(element, options) {
this.options = $.extend({}, $.fn.combobox.defaults, options)
this.$source = $(element)
this.$container = this.setup()
this.$element = this.$container.find('input[type=text]')
this.$target = this.$container.find('input[type=hidden]')
this.$button = this.$container.find('.dropdown-toggle')
this.$menu = $(this.options.menu).appendTo('body')
this.matcher = this.options.matcher || this.matcher
this.sorter = this.options.sorter || this.sorter
this.highlighter = this.options.highlighter || this.highlighter
this.shown = false
this.selected = false
this.refresh()
this.transferAttributes()
this.listen()
}
/* NOTE: COMBOBOX EXTENDS BOOTSTRAP-TYPEAHEAD.js
========================================== */
Combobox.prototype = $.extend({}, $.fn.typeahead.Constructor.prototype, {
constructor: Combobox,
setup: function() {
var combobox = $(this.options.template)
this.$source.before(combobox)
this.$source.hide()
return combobox
},
parse: function() {
var that = this, map = {}, source = [], selected = false
this.$source.find('option').each(function() {
var option = $(this)
if (option.val() === '') {
that.options.placeholder = option.text()
return
}
map[option.text()] = option.val()
source.push(option.text())
if (option.attr('selected'))
selected = option.html()
})
this.map = map
if (selected) {
this.$element.val(selected)
this.$container.addClass('combobox-selected')
this.selected = true
}
return source
},
transferAttributes: function() {
this.options.placeholder = this.$source.attr('data-placeholder')
|| this.options.placeholder
this.$element.attr('placeholder', this.options.placeholder)
this.$target.prop('name', this.$source.prop('name'))
this.$target.val(this.$source.val())
this.$source.removeAttr('name') // Remove from source otherwise form will pass parameter twice.
this.$element.attr('required', this.$source.attr('required'))
this.$element.attr('rel', this.$source.attr('rel'))
this.$element.attr('title', this.$source.attr('title'))
this.$element.attr('class', this.$source.attr('class'))
},
toggle: function(e) {
if (this.shown) {
this.hide()
} else {
// don't clear the textbox when the arrow is clicked and keep the focus
// inside the textbox to make sure that when the focus leaves (e.g. user
// clicks outside of it) that the dropdown closes as expected
this.lookup(e, '') // pass empty query to show all options (this seems more intuitive to me?)
this.$element.focus()
}
},
clearElement: function() {
this.$element.val('').focus()
},
clearTarget: function() {
this.$source.val('')
this.$target.val('')
this.$container.removeClass('combobox-selected')
this.selected = false
},
triggerChange: function() {
this.$source.trigger('change')
},
refresh: function() {
this.source = this.parse()
this.options.items = this.source.length
},
// modified typeahead function adding container and target handling
select: function() {
var val = this.$menu.find('.active').attr('data-value')
this.$element.val(this.updater(val)).trigger('change')
this.$target.val(this.map[val]).trigger('change')
this.$source.val(this.map[val]).trigger('change')
this.$container.addClass('combobox-selected')
this.selected = true
return this.hide()
},
// modified typeahead function removing the blank handling and source function handling
lookup: function(event, existingVal) {
if (existingVal != undefined)
this.query = existingVal
else
this.query = this.$element.val()
return this.process(this.source)
},
// modified typeahead function adding button handling and remove mouseleave
listen: function() {
this.$element.on('focus', $.proxy(this.focus, this)).on('blur',
$.proxy(this.blur, this)).on('keypress',
$.proxy(this.keypress, this)).on('keyup',
$.proxy(this.keyup, this))
if (this.eventSupported('keydown')) {
this.$element.on('keydown', $.proxy(this.keydown, this))
}
this.$menu.on('click', $.proxy(this.click, this)).on('mouseenter',
'li', $.proxy(this.mouseenter, this)).on('mouseleave',
'li', $.proxy(this.mouseleave, this))
this.$button.on('click', $.proxy(this.toggle, this))
},
// modified typeahead function to clear on type and prevent on moving around
keyup: function(e) {
switch (e.keyCode) {
case 40: // down arrow
case 39: // right arrow
case 38: // up arrow
case 37: // left arrow
case 36: // home
case 35: // end
case 16: // shift
case 17: // ctrl
case 18: // alt
break
case 9: // tab
case 13: // enter
if (!this.shown) {
if (this.options.enter_triggers_change === true)
this.select()
else
return
}
this.select()
break
case 27: // escape
if (!this.shown)
return
this.hide()
break
default:
this.clearTarget()
this.lookup()
}
e.stopPropagation()
e.preventDefault()
},
// modified typeahead function to force a match and add a delay on hide
blur: function(e) {
var that = this
this.focused = false
var val = this.$element.val()
if (!!this.options.force_match) {
if (!this.selected && val !== '') {
this.$element.val('')
this.$target.val('').trigger('change')
this.$source.val('').trigger('change')
}
} else {
this.$target.val(val).trigger('change')
this.$source.val(val).trigger('change')
}
if (!this.mousedover && this.shown)
setTimeout(function() {
that.hide()
}, 200)
},
// modified typeahead function to not hide
mouseleave: function(e) {
this.mousedover = false
},
val: function(val) {
if (val != undefined) {
this.$element.val(val)
this.$target.val(val).trigger('change')
this.$source.val(val).trigger('change')
return val;
} else
return this.$target.val()
}
})
/* COMBOBOX PLUGIN DEFINITION
* =========================== */
$.fn.combobox = function(option) {
return this
.each(function() {
var $this = $(this), data = $this.data('combobox'), options = typeof option == 'object'
&& option
if (!data)
$this.data('combobox', (data = new Combobox(this,
options)))
if (typeof option == 'string')
data[option]()
})
}
$.fn.combobox.defaults = {
template: '<div class="combobox-container"><input type="hidden" /><input type="text" autocomplete="off" /><span class="add-on btn dropdown-toggle" data-dropdown="dropdown"><span class="caret"/><span class="combobox-clear"><i class="icon-remove"/></span></span></div>',
menu: '<ul class="typeahead typeahead-long dropdown-menu"></ul>',
item: '<li><a href="#"></a></li>',
force_match: true,
enter_triggers_change: false
}
$.fn.combobox.Constructor = Combobox
}(window.jQuery);