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