bootstrap-combobox.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. /* =============================================================
  2. * bootstrap-combobox.js v1.0.0
  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. !function ($) {
  19. "use strict"
  20. var Combobox = function (element, options) {
  21. this.options = $.extend({}, $.fn.combobox.defaults, options)
  22. this.$container = this.setup(element)
  23. this.$element = this.$container.find('input')
  24. this.$button = this.$container.find('.dropdown-toggle')
  25. this.$target = this.$container.find('select')
  26. this.matcher = this.options.matcher || this.matcher
  27. this.sorter = this.options.sorter || this.sorter
  28. this.highlighter = this.options.highlighter || this.highlighter
  29. this.$menu = $(this.options.menu).appendTo('body')
  30. this.placeholder = this.options.placeholder || this.$target.attr('data-placeholder')
  31. this.$element.attr('placeholder', this.placeholder)
  32. this.shown = false
  33. this.selected = false
  34. this.refresh()
  35. this.listen()
  36. }
  37. /* NOTE: COMBOBOX EXTENDS BOOTSTRAP-TYPEAHEAD.js
  38. ========================================== */
  39. Combobox.prototype = $.extend({}, $.fn.typeahead.Constructor.prototype, {
  40. constructor:Combobox, setup:function (element) {
  41. var select = $(element)
  42. , combobox = $(this.options.template)
  43. select.before(combobox)
  44. select.detach()
  45. combobox.append(select)
  46. return combobox
  47. },
  48. parse:function () {
  49. var map = {}
  50. , source = []
  51. , selected = false
  52. this.$target.find('option').each(function () {
  53. var option = $(this)
  54. map[option.text()] = option.val()
  55. source.push(option.text())
  56. if (option.attr('selected')) selected = option.html()
  57. })
  58. this.map = map
  59. if (selected) {
  60. this.$element.val(selected)
  61. this.$container.addClass('combobox-selected')
  62. this.selected = true
  63. }
  64. return source
  65. },
  66. toggle:function () {
  67. if (this.$container.hasClass('combobox-selected')) {
  68. this.clearTarget()
  69. this.$element.val('').focus()
  70. } else {
  71. if (this.shown) {
  72. this.hide()
  73. } else {
  74. this.lookup()
  75. }
  76. }
  77. },
  78. clearTarget:function () {
  79. this.$target.val('')
  80. this.$container.removeClass('combobox-selected')
  81. this.selected = false
  82. this.$target.trigger('change')
  83. },
  84. refresh:function () {
  85. this.source = this.parse()
  86. this.options.items = this.source.length
  87. }
  88. // modified typeahead function adding container and target handling
  89. , select:function () {
  90. var val = this.$menu.find('.active').attr('data-value')
  91. this.$element.val(val)
  92. this.$container.addClass('combobox-selected')
  93. this.$target.val(this.map[val])
  94. this.$target.trigger('change')
  95. this.selected = true
  96. return this.hide()
  97. }
  98. // modified typeahead function removing the blank handling
  99. , lookup:function (event) {
  100. var that = this
  101. , items
  102. , q
  103. this.query = this.$element.val()
  104. items = $.grep(this.source, function (item) {
  105. if (that.matcher(item)) return item
  106. })
  107. items = this.sorter(items)
  108. if (!items.length) {
  109. return this.shown ? this.hide() : this
  110. }
  111. return this.render(items.slice(0, this.options.items)).show()
  112. }
  113. // modified typeahead function adding button handling
  114. , listen:function () {
  115. this.$element
  116. .on('blur', $.proxy(this.blur, this))
  117. .on('keypress', $.proxy(this.keypress, this))
  118. .on('keyup', $.proxy(this.keyup, this))
  119. if ($.browser.webkit || $.browser.msie) {
  120. this.$element.on('keydown', $.proxy(this.keypress, this))
  121. }
  122. this.$menu
  123. .on('click', $.proxy(this.click, this))
  124. .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
  125. this.$button
  126. .on('click', $.proxy(this.toggle, this))
  127. }
  128. // modified typeahead function to clear on type and prevent on moving around
  129. , keyup:function (e) {
  130. switch (e.keyCode) {
  131. case 40: // down arrow
  132. case 39: // right arrow
  133. case 38: // up arrow
  134. case 37: // left arrow
  135. case 36: // home
  136. case 35: // end
  137. case 16: // shift
  138. break
  139. case 9: // tab
  140. case 13: // enter
  141. if (!this.shown) return
  142. this.select()
  143. break
  144. case 27: // escape
  145. if (!this.shown) return
  146. this.hide()
  147. break
  148. default:
  149. this.clearTarget()
  150. this.lookup()
  151. }
  152. e.stopPropagation()
  153. e.preventDefault()
  154. }
  155. // modified typeahead function to only hide menu if it is visible
  156. , blur:function (e) {
  157. var that = this
  158. e.stopPropagation()
  159. e.preventDefault()
  160. var val = this.$element.val()
  161. if (!this.selected && val != "") {
  162. this.$element.val("")
  163. this.$target.val("").trigger('change')
  164. }
  165. if (this.shown) {
  166. setTimeout(function () {
  167. that.hide()
  168. }, 150)
  169. }
  170. }
  171. })
  172. /* COMBOBOX PLUGIN DEFINITION
  173. * =========================== */
  174. $.fn.combobox = function (option) {
  175. return this.each(function () {
  176. var $this = $(this)
  177. , data = $this.data('combobox')
  178. , options = typeof option == 'object' && option
  179. if (!data) $this.data('combobox', (data = new Combobox(this, options)))
  180. if (typeof option == 'string') data[option]()
  181. })
  182. }
  183. $.fn.combobox.defaults = {
  184. template:'<div class="combobox-container"><input type="text" autocomplete="off" /><button class="add-on btn dropdown-toggle" data-dropdown="dropdown"><span class="caret"/><span class="combobox-clear"><i class="icon-remove"/></span></button></div>',
  185. menu:'<ul class="typeahead typeahead-long dropdown-menu"></ul>',
  186. item:'<li><a href="#"></a></li>', placeholder:null
  187. }
  188. $.fn.combobox.Constructor = Combobox
  189. }(window.jQuery);