js/bootstrap-typeahead.js
author indvd00m (gotoindvdum[at]gmail[dot]com)
Fri, 04 Jul 2014 16:42:41 +0400
changeset 0 ba8ab09f730e
permissions -rw-r--r--
First home page
indvd00m@0
     1
/* =============================================================
indvd00m@0
     2
 * bootstrap-typeahead.js v2.3.1
indvd00m@0
     3
 * http://twitter.github.com/bootstrap/javascript.html#typeahead
indvd00m@0
     4
 * =============================================================
indvd00m@0
     5
 * Copyright 2012 Twitter, Inc.
indvd00m@0
     6
 *
indvd00m@0
     7
 * Licensed under the Apache License, Version 2.0 (the "License");
indvd00m@0
     8
 * you may not use this file except in compliance with the License.
indvd00m@0
     9
 * You may obtain a copy of the License at
indvd00m@0
    10
 *
indvd00m@0
    11
 * http://www.apache.org/licenses/LICENSE-2.0
indvd00m@0
    12
 *
indvd00m@0
    13
 * Unless required by applicable law or agreed to in writing, software
indvd00m@0
    14
 * distributed under the License is distributed on an "AS IS" BASIS,
indvd00m@0
    15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
indvd00m@0
    16
 * See the License for the specific language governing permissions and
indvd00m@0
    17
 * limitations under the License.
indvd00m@0
    18
 * ============================================================ */
indvd00m@0
    19
indvd00m@0
    20
indvd00m@0
    21
!function($){
indvd00m@0
    22
indvd00m@0
    23
  "use strict"; // jshint ;_;
indvd00m@0
    24
indvd00m@0
    25
indvd00m@0
    26
 /* TYPEAHEAD PUBLIC CLASS DEFINITION
indvd00m@0
    27
  * ================================= */
indvd00m@0
    28
indvd00m@0
    29
  var Typeahead = function (element, options) {
indvd00m@0
    30
    this.$element = $(element)
indvd00m@0
    31
    this.options = $.extend({}, $.fn.typeahead.defaults, options)
indvd00m@0
    32
    this.matcher = this.options.matcher || this.matcher
indvd00m@0
    33
    this.sorter = this.options.sorter || this.sorter
indvd00m@0
    34
    this.highlighter = this.options.highlighter || this.highlighter
indvd00m@0
    35
    this.updater = this.options.updater || this.updater
indvd00m@0
    36
    this.source = this.options.source
indvd00m@0
    37
    this.$menu = $(this.options.menu)
indvd00m@0
    38
    this.shown = false
indvd00m@0
    39
    this.listen()
indvd00m@0
    40
  }
indvd00m@0
    41
indvd00m@0
    42
  Typeahead.prototype = {
indvd00m@0
    43
indvd00m@0
    44
    constructor: Typeahead
indvd00m@0
    45
indvd00m@0
    46
  , select: function () {
indvd00m@0
    47
      var val = this.$menu.find('.active').attr('data-value')
indvd00m@0
    48
      this.$element
indvd00m@0
    49
        .val(this.updater(val))
indvd00m@0
    50
        .change()
indvd00m@0
    51
      return this.hide()
indvd00m@0
    52
    }
indvd00m@0
    53
indvd00m@0
    54
  , updater: function (item) {
indvd00m@0
    55
      return item
indvd00m@0
    56
    }
indvd00m@0
    57
indvd00m@0
    58
  , show: function () {
indvd00m@0
    59
      var pos = $.extend({}, this.$element.position(), {
indvd00m@0
    60
        height: this.$element[0].offsetHeight
indvd00m@0
    61
      })
indvd00m@0
    62
indvd00m@0
    63
      this.$menu
indvd00m@0
    64
        .insertAfter(this.$element)
indvd00m@0
    65
        .css({
indvd00m@0
    66
          top: pos.top + pos.height
indvd00m@0
    67
        , left: pos.left
indvd00m@0
    68
        })
indvd00m@0
    69
        .show()
indvd00m@0
    70
indvd00m@0
    71
      this.shown = true
indvd00m@0
    72
      return this
indvd00m@0
    73
    }
indvd00m@0
    74
indvd00m@0
    75
  , hide: function () {
indvd00m@0
    76
      this.$menu.hide()
indvd00m@0
    77
      this.shown = false
indvd00m@0
    78
      return this
indvd00m@0
    79
    }
indvd00m@0
    80
indvd00m@0
    81
  , lookup: function (event) {
indvd00m@0
    82
      var items
indvd00m@0
    83
indvd00m@0
    84
      this.query = this.$element.val()
indvd00m@0
    85
indvd00m@0
    86
      if (!this.query || this.query.length < this.options.minLength) {
indvd00m@0
    87
        return this.shown ? this.hide() : this
indvd00m@0
    88
      }
indvd00m@0
    89
indvd00m@0
    90
      items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source
indvd00m@0
    91
indvd00m@0
    92
      return items ? this.process(items) : this
indvd00m@0
    93
    }
indvd00m@0
    94
indvd00m@0
    95
  , process: function (items) {
indvd00m@0
    96
      var that = this
indvd00m@0
    97
indvd00m@0
    98
      items = $.grep(items, function (item) {
indvd00m@0
    99
        return that.matcher(item)
indvd00m@0
   100
      })
indvd00m@0
   101
indvd00m@0
   102
      items = this.sorter(items)
indvd00m@0
   103
indvd00m@0
   104
      if (!items.length) {
indvd00m@0
   105
        return this.shown ? this.hide() : this
indvd00m@0
   106
      }
indvd00m@0
   107
indvd00m@0
   108
      return this.render(items.slice(0, this.options.items)).show()
indvd00m@0
   109
    }
indvd00m@0
   110
indvd00m@0
   111
  , matcher: function (item) {
indvd00m@0
   112
      return ~item.toLowerCase().indexOf(this.query.toLowerCase())
indvd00m@0
   113
    }
indvd00m@0
   114
indvd00m@0
   115
  , sorter: function (items) {
indvd00m@0
   116
      var beginswith = []
indvd00m@0
   117
        , caseSensitive = []
indvd00m@0
   118
        , caseInsensitive = []
indvd00m@0
   119
        , item
indvd00m@0
   120
indvd00m@0
   121
      while (item = items.shift()) {
indvd00m@0
   122
        if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
indvd00m@0
   123
        else if (~item.indexOf(this.query)) caseSensitive.push(item)
indvd00m@0
   124
        else caseInsensitive.push(item)
indvd00m@0
   125
      }
indvd00m@0
   126
indvd00m@0
   127
      return beginswith.concat(caseSensitive, caseInsensitive)
indvd00m@0
   128
    }
indvd00m@0
   129
indvd00m@0
   130
  , highlighter: function (item) {
indvd00m@0
   131
      var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
indvd00m@0
   132
      return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
indvd00m@0
   133
        return '<strong>' + match + '</strong>'
indvd00m@0
   134
      })
indvd00m@0
   135
    }
indvd00m@0
   136
indvd00m@0
   137
  , render: function (items) {
indvd00m@0
   138
      var that = this
indvd00m@0
   139
indvd00m@0
   140
      items = $(items).map(function (i, item) {
indvd00m@0
   141
        i = $(that.options.item).attr('data-value', item)
indvd00m@0
   142
        i.find('a').html(that.highlighter(item))
indvd00m@0
   143
        return i[0]
indvd00m@0
   144
      })
indvd00m@0
   145
indvd00m@0
   146
      items.first().addClass('active')
indvd00m@0
   147
      this.$menu.html(items)
indvd00m@0
   148
      return this
indvd00m@0
   149
    }
indvd00m@0
   150
indvd00m@0
   151
  , next: function (event) {
indvd00m@0
   152
      var active = this.$menu.find('.active').removeClass('active')
indvd00m@0
   153
        , next = active.next()
indvd00m@0
   154
indvd00m@0
   155
      if (!next.length) {
indvd00m@0
   156
        next = $(this.$menu.find('li')[0])
indvd00m@0
   157
      }
indvd00m@0
   158
indvd00m@0
   159
      next.addClass('active')
indvd00m@0
   160
    }
indvd00m@0
   161
indvd00m@0
   162
  , prev: function (event) {
indvd00m@0
   163
      var active = this.$menu.find('.active').removeClass('active')
indvd00m@0
   164
        , prev = active.prev()
indvd00m@0
   165
indvd00m@0
   166
      if (!prev.length) {
indvd00m@0
   167
        prev = this.$menu.find('li').last()
indvd00m@0
   168
      }
indvd00m@0
   169
indvd00m@0
   170
      prev.addClass('active')
indvd00m@0
   171
    }
indvd00m@0
   172
indvd00m@0
   173
  , listen: function () {
indvd00m@0
   174
      this.$element
indvd00m@0
   175
        .on('focus',    $.proxy(this.focus, this))
indvd00m@0
   176
        .on('blur',     $.proxy(this.blur, this))
indvd00m@0
   177
        .on('keypress', $.proxy(this.keypress, this))
indvd00m@0
   178
        .on('keyup',    $.proxy(this.keyup, this))
indvd00m@0
   179
indvd00m@0
   180
      if (this.eventSupported('keydown')) {
indvd00m@0
   181
        this.$element.on('keydown', $.proxy(this.keydown, this))
indvd00m@0
   182
      }
indvd00m@0
   183
indvd00m@0
   184
      this.$menu
indvd00m@0
   185
        .on('click', $.proxy(this.click, this))
indvd00m@0
   186
        .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
indvd00m@0
   187
        .on('mouseleave', 'li', $.proxy(this.mouseleave, this))
indvd00m@0
   188
    }
indvd00m@0
   189
indvd00m@0
   190
  , eventSupported: function(eventName) {
indvd00m@0
   191
      var isSupported = eventName in this.$element
indvd00m@0
   192
      if (!isSupported) {
indvd00m@0
   193
        this.$element.setAttribute(eventName, 'return;')
indvd00m@0
   194
        isSupported = typeof this.$element[eventName] === 'function'
indvd00m@0
   195
      }
indvd00m@0
   196
      return isSupported
indvd00m@0
   197
    }
indvd00m@0
   198
indvd00m@0
   199
  , move: function (e) {
indvd00m@0
   200
      if (!this.shown) return
indvd00m@0
   201
indvd00m@0
   202
      switch(e.keyCode) {
indvd00m@0
   203
        case 9: // tab
indvd00m@0
   204
        case 13: // enter
indvd00m@0
   205
        case 27: // escape
indvd00m@0
   206
          e.preventDefault()
indvd00m@0
   207
          break
indvd00m@0
   208
indvd00m@0
   209
        case 38: // up arrow
indvd00m@0
   210
          e.preventDefault()
indvd00m@0
   211
          this.prev()
indvd00m@0
   212
          break
indvd00m@0
   213
indvd00m@0
   214
        case 40: // down arrow
indvd00m@0
   215
          e.preventDefault()
indvd00m@0
   216
          this.next()
indvd00m@0
   217
          break
indvd00m@0
   218
      }
indvd00m@0
   219
indvd00m@0
   220
      e.stopPropagation()
indvd00m@0
   221
    }
indvd00m@0
   222
indvd00m@0
   223
  , keydown: function (e) {
indvd00m@0
   224
      this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27])
indvd00m@0
   225
      this.move(e)
indvd00m@0
   226
    }
indvd00m@0
   227
indvd00m@0
   228
  , keypress: function (e) {
indvd00m@0
   229
      if (this.suppressKeyPressRepeat) return
indvd00m@0
   230
      this.move(e)
indvd00m@0
   231
    }
indvd00m@0
   232
indvd00m@0
   233
  , keyup: function (e) {
indvd00m@0
   234
      switch(e.keyCode) {
indvd00m@0
   235
        case 40: // down arrow
indvd00m@0
   236
        case 38: // up arrow
indvd00m@0
   237
        case 16: // shift
indvd00m@0
   238
        case 17: // ctrl
indvd00m@0
   239
        case 18: // alt
indvd00m@0
   240
          break
indvd00m@0
   241
indvd00m@0
   242
        case 9: // tab
indvd00m@0
   243
        case 13: // enter
indvd00m@0
   244
          if (!this.shown) return
indvd00m@0
   245
          this.select()
indvd00m@0
   246
          break
indvd00m@0
   247
indvd00m@0
   248
        case 27: // escape
indvd00m@0
   249
          if (!this.shown) return
indvd00m@0
   250
          this.hide()
indvd00m@0
   251
          break
indvd00m@0
   252
indvd00m@0
   253
        default:
indvd00m@0
   254
          this.lookup()
indvd00m@0
   255
      }
indvd00m@0
   256
indvd00m@0
   257
      e.stopPropagation()
indvd00m@0
   258
      e.preventDefault()
indvd00m@0
   259
  }
indvd00m@0
   260
indvd00m@0
   261
  , focus: function (e) {
indvd00m@0
   262
      this.focused = true
indvd00m@0
   263
    }
indvd00m@0
   264
indvd00m@0
   265
  , blur: function (e) {
indvd00m@0
   266
      this.focused = false
indvd00m@0
   267
      if (!this.mousedover && this.shown) this.hide()
indvd00m@0
   268
    }
indvd00m@0
   269
indvd00m@0
   270
  , click: function (e) {
indvd00m@0
   271
      e.stopPropagation()
indvd00m@0
   272
      e.preventDefault()
indvd00m@0
   273
      this.select()
indvd00m@0
   274
      this.$element.focus()
indvd00m@0
   275
    }
indvd00m@0
   276
indvd00m@0
   277
  , mouseenter: function (e) {
indvd00m@0
   278
      this.mousedover = true
indvd00m@0
   279
      this.$menu.find('.active').removeClass('active')
indvd00m@0
   280
      $(e.currentTarget).addClass('active')
indvd00m@0
   281
    }
indvd00m@0
   282
indvd00m@0
   283
  , mouseleave: function (e) {
indvd00m@0
   284
      this.mousedover = false
indvd00m@0
   285
      if (!this.focused && this.shown) this.hide()
indvd00m@0
   286
    }
indvd00m@0
   287
indvd00m@0
   288
  }
indvd00m@0
   289
indvd00m@0
   290
indvd00m@0
   291
  /* TYPEAHEAD PLUGIN DEFINITION
indvd00m@0
   292
   * =========================== */
indvd00m@0
   293
indvd00m@0
   294
  var old = $.fn.typeahead
indvd00m@0
   295
indvd00m@0
   296
  $.fn.typeahead = function (option) {
indvd00m@0
   297
    return this.each(function () {
indvd00m@0
   298
      var $this = $(this)
indvd00m@0
   299
        , data = $this.data('typeahead')
indvd00m@0
   300
        , options = typeof option == 'object' && option
indvd00m@0
   301
      if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
indvd00m@0
   302
      if (typeof option == 'string') data[option]()
indvd00m@0
   303
    })
indvd00m@0
   304
  }
indvd00m@0
   305
indvd00m@0
   306
  $.fn.typeahead.defaults = {
indvd00m@0
   307
    source: []
indvd00m@0
   308
  , items: 8
indvd00m@0
   309
  , menu: '<ul class="typeahead dropdown-menu"></ul>'
indvd00m@0
   310
  , item: '<li><a href="#"></a></li>'
indvd00m@0
   311
  , minLength: 1
indvd00m@0
   312
  }
indvd00m@0
   313
indvd00m@0
   314
  $.fn.typeahead.Constructor = Typeahead
indvd00m@0
   315
indvd00m@0
   316
indvd00m@0
   317
 /* TYPEAHEAD NO CONFLICT
indvd00m@0
   318
  * =================== */
indvd00m@0
   319
indvd00m@0
   320
  $.fn.typeahead.noConflict = function () {
indvd00m@0
   321
    $.fn.typeahead = old
indvd00m@0
   322
    return this
indvd00m@0
   323
  }
indvd00m@0
   324
indvd00m@0
   325
indvd00m@0
   326
 /* TYPEAHEAD DATA-API
indvd00m@0
   327
  * ================== */
indvd00m@0
   328
indvd00m@0
   329
  $(document).on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
indvd00m@0
   330
    var $this = $(this)
indvd00m@0
   331
    if ($this.data('typeahead')) return
indvd00m@0
   332
    $this.typeahead($this.data())
indvd00m@0
   333
  })
indvd00m@0
   334
indvd00m@0
   335
}(window.jQuery);