/* =============================================================

* bootstrap-typeahead.js v2.0.3
* 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.position(), {
       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 that = this
       , items
       , q

     this.query = this.$element.val()

     if (!this.query) {
       return this.shown ? this.hide() : this
     }

     items = $.grep(this.source, 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.keypress, this))
     }

     this.$menu
       .on('click', $.proxy(this.click, this))
       .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
   }

 , 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()
 }

 , keypress: 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
         if (e.type != 'keydown') break
         e.preventDefault()
         this.prev()
         break

       case 40: // down arrow
         if (e.type != 'keydown') break
         e.preventDefault()
         this.next()
         break
     }

     e.stopPropagation()
   }

 , 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>'
 }

 $.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);