| /* ============================================================= |
| * bootstrap-typeahead.js v2.1.0 |
| * http://twitter.github.com/bootstrap/javascript.html#typeahead |
| * ============================================================= |
| * Copyright 2012 Twitter, Inc. |
| * |
| * 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"; // jshint ;_; |
| |
| |
| /* TYPEAHEAD PUBLIC CLASS DEFINITION |
| * ================================= */ |
| |
| var Typeahead = function (element, options) { |
| this.$element = $(element) |
| this.options = $.extend({}, $.fn.typeahead.defaults, options) |
| this.matcher = this.options.matcher || this.matcher |
| this.sorter = this.options.sorter || this.sorter |
| this.highlighter = this.options.highlighter || this.highlighter |
| this.updater = this.options.updater || this.updater |
| this.$menu = $(this.options.menu).appendTo('body') |
| this.source = this.options.source |
| this.shown = false |
| this.listen() |
| } |
| |
| Typeahead.prototype = { |
| |
| constructor: Typeahead |
| |
| , select: function () { |
| var val = this.$menu.find('.active').attr('data-value') |
| this.$element |
| .val(this.updater(val)) |
| .change() |
| return this.hide() |
| } |
| |
| , updater: function (item) { |
| return item |
| } |
| |
| , show: function () { |
| var pos = $.extend({}, this.$element.offset(), { |
| height: this.$element[0].offsetHeight |
| }) |
| |
| this.$menu.css({ |
| top: pos.top + pos.height |
| , left: pos.left |
| }) |
| |
| this.$menu.show() |
| this.shown = true |
| return this |
| } |
| |
| , hide: function () { |
| this.$menu.hide() |
| this.shown = false |
| return this |
| } |
| |
| , lookup: function (event) { |
| var items |
| |
| this.query = this.$element.val() |
| |
| if (!this.query || this.query.length < this.options.minLength) { |
| return this.shown ? this.hide() : this |
| } |
| |
| items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source |
| |
| return items ? this.process(items) : this |
| } |
| |
| , process: function (items) { |
| var that = this |
| |
| items = $.grep(items, function (item) { |
| return that.matcher(item) |
| }) |
| |
| items = this.sorter(items) |
| |
| if (!items.length) { |
| return this.shown ? this.hide() : this |
| } |
| |
| return this.render(items.slice(0, this.options.items)).show() |
| } |
| |
| , matcher: function (item) { |
| return ~item.toLowerCase().indexOf(this.query.toLowerCase()) |
| } |
| |
| , sorter: function (items) { |
| var beginswith = [] |
| , caseSensitive = [] |
| , caseInsensitive = [] |
| , item |
| |
| while (item = items.shift()) { |
| if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item) |
| else if (~item.indexOf(this.query)) caseSensitive.push(item) |
| else caseInsensitive.push(item) |
| } |
| |
| return beginswith.concat(caseSensitive, caseInsensitive) |
| } |
| |
| , highlighter: function (item) { |
| var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&') |
| return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) { |
| return '<strong>' + match + '</strong>' |
| }) |
| } |
| |
| , render: function (items) { |
| var that = this |
| |
| items = $(items).map(function (i, item) { |
| i = $(that.options.item).attr('data-value', item) |
| i.find('a').html(that.highlighter(item)) |
| return i[0] |
| }) |
| |
| items.first().addClass('active') |
| this.$menu.html(items) |
| return this |
| } |
| |
| , next: function (event) { |
| var active = this.$menu.find('.active').removeClass('active') |
| , next = active.next() |
| |
| if (!next.length) { |
| next = $(this.$menu.find('li')[0]) |
| } |
| |
| next.addClass('active') |
| } |
| |
| , prev: function (event) { |
| var active = this.$menu.find('.active').removeClass('active') |
| , prev = active.prev() |
| |
| if (!prev.length) { |
| prev = this.$menu.find('li').last() |
| } |
| |
| prev.addClass('active') |
| } |
| |
| , listen: function () { |
| this.$element |
| .on('blur', $.proxy(this.blur, this)) |
| .on('keypress', $.proxy(this.keypress, this)) |
| .on('keyup', $.proxy(this.keyup, this)) |
| |
| if ($.browser.webkit || $.browser.msie) { |
| this.$element.on('keydown', $.proxy(this.keydown, this)) |
| } |
| |
| this.$menu |
| .on('click', $.proxy(this.click, this)) |
| .on('mouseenter', 'li', $.proxy(this.mouseenter, this)) |
| } |
| |
| , move: function (e) { |
| if (!this.shown) return |
| |
| switch(e.keyCode) { |
| case 9: // tab |
| case 13: // enter |
| case 27: // escape |
| e.preventDefault() |
| break |
| |
| case 38: // up arrow |
| e.preventDefault() |
| this.prev() |
| break |
| |
| case 40: // down arrow |
| e.preventDefault() |
| this.next() |
| break |
| } |
| |
| e.stopPropagation() |
| } |
| |
| , keydown: function (e) { |
| this.suppressKeyPressRepeat = !~$.inArray(e.keyCode, [40,38,9,13,27]) |
| this.move(e) |
| } |
| |
| , keypress: function (e) { |
| if (this.suppressKeyPressRepeat) return |
| this.move(e) |
| } |
| |
| , keyup: function (e) { |
| switch(e.keyCode) { |
| case 40: // down arrow |
| case 38: // up arrow |
| break |
| |
| case 9: // tab |
| case 13: // enter |
| if (!this.shown) return |
| this.select() |
| break |
| |
| case 27: // escape |
| if (!this.shown) return |
| this.hide() |
| break |
| |
| default: |
| this.lookup() |
| } |
| |
| e.stopPropagation() |
| e.preventDefault() |
| } |
| |
| , blur: function (e) { |
| var that = this |
| setTimeout(function () { that.hide() }, 150) |
| } |
| |
| , click: function (e) { |
| e.stopPropagation() |
| e.preventDefault() |
| this.select() |
| } |
| |
| , mouseenter: function (e) { |
| this.$menu.find('.active').removeClass('active') |
| $(e.currentTarget).addClass('active') |
| } |
| |
| } |
| |
| |
| /* TYPEAHEAD PLUGIN DEFINITION |
| * =========================== */ |
| |
| $.fn.typeahead = function (option) { |
| return this.each(function () { |
| var $this = $(this) |
| , data = $this.data('typeahead') |
| , options = typeof option == 'object' && option |
| if (!data) $this.data('typeahead', (data = new Typeahead(this, options))) |
| if (typeof option == 'string') data[option]() |
| }) |
| } |
| |
| $.fn.typeahead.defaults = { |
| source: [] |
| , items: 8 |
| , menu: '<ul class="typeahead dropdown-menu"></ul>' |
| , item: '<li><a href="#"></a></li>' |
| , minLength: 1 |
| } |
| |
| $.fn.typeahead.Constructor = Typeahead |
| |
| |
| /* TYPEAHEAD DATA-API |
| * ================== */ |
| |
| $(function () { |
| $('body').on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) { |
| var $this = $(this) |
| if ($this.data('typeahead')) return |
| e.preventDefault() |
| $this.typeahead($this.data()) |
| }) |
| }) |
| |
| }(window.jQuery); |