提交 | 用户 | 时间
|
58d006
|
1 |
/*! |
A |
2 |
* typeahead.js 0.11.1 |
|
3 |
* https://github.com/twitter/typeahead.js |
|
4 |
* Copyright 2013-2015 Twitter, Inc. and other contributors; Licensed MIT |
|
5 |
*/ |
|
6 |
|
|
7 |
(function(root, factory) { |
|
8 |
if (typeof define === "function" && define.amd) { |
|
9 |
define("typeahead.js", [ "jquery" ], function(a0) { |
|
10 |
return factory(a0); |
|
11 |
}); |
|
12 |
} else if (typeof exports === "object") { |
|
13 |
module.exports = factory(require("jquery")); |
|
14 |
} else { |
|
15 |
factory(jQuery); |
|
16 |
} |
|
17 |
})(this, function($) { |
|
18 |
var _ = function() { |
|
19 |
"use strict"; |
|
20 |
return { |
|
21 |
isMsie: function() { |
|
22 |
return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false; |
|
23 |
}, |
|
24 |
isBlankString: function(str) { |
|
25 |
return !str || /^\s*$/.test(str); |
|
26 |
}, |
|
27 |
escapeRegExChars: function(str) { |
|
28 |
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); |
|
29 |
}, |
|
30 |
isString: function(obj) { |
|
31 |
return typeof obj === "string"; |
|
32 |
}, |
|
33 |
isNumber: function(obj) { |
|
34 |
return typeof obj === "number"; |
|
35 |
}, |
|
36 |
isArray: $.isArray, |
|
37 |
isFunction: $.isFunction, |
|
38 |
isObject: $.isPlainObject, |
|
39 |
isUndefined: function(obj) { |
|
40 |
return typeof obj === "undefined"; |
|
41 |
}, |
|
42 |
isElement: function(obj) { |
|
43 |
return !!(obj && obj.nodeType === 1); |
|
44 |
}, |
|
45 |
isJQuery: function(obj) { |
|
46 |
return obj instanceof $; |
|
47 |
}, |
|
48 |
toStr: function toStr(s) { |
|
49 |
return _.isUndefined(s) || s === null ? "" : s + ""; |
|
50 |
}, |
|
51 |
bind: $.proxy, |
|
52 |
each: function(collection, cb) { |
|
53 |
$.each(collection, reverseArgs); |
|
54 |
function reverseArgs(index, value) { |
|
55 |
return cb(value, index); |
|
56 |
} |
|
57 |
}, |
|
58 |
map: $.map, |
|
59 |
filter: $.grep, |
|
60 |
every: function(obj, test) { |
|
61 |
var result = true; |
|
62 |
if (!obj) { |
|
63 |
return result; |
|
64 |
} |
|
65 |
$.each(obj, function(key, val) { |
|
66 |
if (!(result = test.call(null, val, key, obj))) { |
|
67 |
return false; |
|
68 |
} |
|
69 |
}); |
|
70 |
return !!result; |
|
71 |
}, |
|
72 |
some: function(obj, test) { |
|
73 |
var result = false; |
|
74 |
if (!obj) { |
|
75 |
return result; |
|
76 |
} |
|
77 |
$.each(obj, function(key, val) { |
|
78 |
if (result = test.call(null, val, key, obj)) { |
|
79 |
return false; |
|
80 |
} |
|
81 |
}); |
|
82 |
return !!result; |
|
83 |
}, |
|
84 |
mixin: $.extend, |
|
85 |
identity: function(x) { |
|
86 |
return x; |
|
87 |
}, |
|
88 |
clone: function(obj) { |
|
89 |
return $.extend(true, {}, obj); |
|
90 |
}, |
|
91 |
getIdGenerator: function() { |
|
92 |
var counter = 0; |
|
93 |
return function() { |
|
94 |
return counter++; |
|
95 |
}; |
|
96 |
}, |
|
97 |
templatify: function templatify(obj) { |
|
98 |
return $.isFunction(obj) ? obj : template; |
|
99 |
function template() { |
|
100 |
return String(obj); |
|
101 |
} |
|
102 |
}, |
|
103 |
defer: function(fn) { |
|
104 |
setTimeout(fn, 0); |
|
105 |
}, |
|
106 |
debounce: function(func, wait, immediate) { |
|
107 |
var timeout, result; |
|
108 |
return function() { |
|
109 |
var context = this, args = arguments, later, callNow; |
|
110 |
later = function() { |
|
111 |
timeout = null; |
|
112 |
if (!immediate) { |
|
113 |
result = func.apply(context, args); |
|
114 |
} |
|
115 |
}; |
|
116 |
callNow = immediate && !timeout; |
|
117 |
clearTimeout(timeout); |
|
118 |
timeout = setTimeout(later, wait); |
|
119 |
if (callNow) { |
|
120 |
result = func.apply(context, args); |
|
121 |
} |
|
122 |
return result; |
|
123 |
}; |
|
124 |
}, |
|
125 |
throttle: function(func, wait) { |
|
126 |
var context, args, timeout, result, previous, later; |
|
127 |
previous = 0; |
|
128 |
later = function() { |
|
129 |
previous = new Date(); |
|
130 |
timeout = null; |
|
131 |
result = func.apply(context, args); |
|
132 |
}; |
|
133 |
return function() { |
|
134 |
var now = new Date(), remaining = wait - (now - previous); |
|
135 |
context = this; |
|
136 |
args = arguments; |
|
137 |
if (remaining <= 0) { |
|
138 |
clearTimeout(timeout); |
|
139 |
timeout = null; |
|
140 |
previous = now; |
|
141 |
result = func.apply(context, args); |
|
142 |
} else if (!timeout) { |
|
143 |
timeout = setTimeout(later, remaining); |
|
144 |
} |
|
145 |
return result; |
|
146 |
}; |
|
147 |
}, |
|
148 |
stringify: function(val) { |
|
149 |
return _.isString(val) ? val : JSON.stringify(val); |
|
150 |
}, |
|
151 |
noop: function() {} |
|
152 |
}; |
|
153 |
}(); |
|
154 |
var WWW = function() { |
|
155 |
"use strict"; |
|
156 |
var defaultClassNames = { |
|
157 |
wrapper: "twitter-typeahead", |
|
158 |
input: "tt-input", |
|
159 |
hint: "tt-hint", |
|
160 |
menu: "tt-menu", |
|
161 |
dataset: "tt-dataset", |
|
162 |
suggestion: "tt-suggestion", |
|
163 |
selectable: "tt-selectable", |
|
164 |
empty: "tt-empty", |
|
165 |
open: "tt-open", |
|
166 |
cursor: "tt-cursor", |
|
167 |
highlight: "tt-highlight" |
|
168 |
}; |
|
169 |
return build; |
|
170 |
function build(o) { |
|
171 |
var www, classes; |
|
172 |
classes = _.mixin({}, defaultClassNames, o); |
|
173 |
www = { |
|
174 |
css: buildCss(), |
|
175 |
classes: classes, |
|
176 |
html: buildHtml(classes), |
|
177 |
selectors: buildSelectors(classes) |
|
178 |
}; |
|
179 |
return { |
|
180 |
css: www.css, |
|
181 |
html: www.html, |
|
182 |
classes: www.classes, |
|
183 |
selectors: www.selectors, |
|
184 |
mixin: function(o) { |
|
185 |
_.mixin(o, www); |
|
186 |
} |
|
187 |
}; |
|
188 |
} |
|
189 |
function buildHtml(c) { |
|
190 |
return { |
|
191 |
wrapper: '<span class="' + c.wrapper + '"></span>', |
|
192 |
menu: '<div class="' + c.menu + '"></div>' |
|
193 |
}; |
|
194 |
} |
|
195 |
function buildSelectors(classes) { |
|
196 |
var selectors = {}; |
|
197 |
_.each(classes, function(v, k) { |
|
198 |
selectors[k] = "." + v; |
|
199 |
}); |
|
200 |
return selectors; |
|
201 |
} |
|
202 |
function buildCss() { |
|
203 |
var css = { |
|
204 |
wrapper: { |
|
205 |
position: "relative", |
|
206 |
display: "inline-block" |
|
207 |
}, |
|
208 |
hint: { |
|
209 |
position: "absolute", |
|
210 |
top: "0", |
|
211 |
left: "0", |
|
212 |
borderColor: "transparent", |
|
213 |
boxShadow: "none", |
|
214 |
opacity: "1" |
|
215 |
}, |
|
216 |
input: { |
|
217 |
position: "relative", |
|
218 |
verticalAlign: "top", |
|
219 |
backgroundColor: "transparent" |
|
220 |
}, |
|
221 |
inputWithNoHint: { |
|
222 |
position: "relative", |
|
223 |
verticalAlign: "top" |
|
224 |
}, |
|
225 |
menu: { |
|
226 |
position: "absolute", |
|
227 |
top: "100%", |
|
228 |
left: "0", |
|
229 |
zIndex: "100", |
|
230 |
display: "none" |
|
231 |
}, |
|
232 |
ltr: { |
|
233 |
left: "0", |
|
234 |
right: "auto" |
|
235 |
}, |
|
236 |
rtl: { |
|
237 |
left: "auto", |
|
238 |
right: " 0" |
|
239 |
} |
|
240 |
}; |
|
241 |
if (_.isMsie()) { |
|
242 |
_.mixin(css.input, { |
|
243 |
backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" |
|
244 |
}); |
|
245 |
} |
|
246 |
return css; |
|
247 |
} |
|
248 |
}(); |
|
249 |
var EventBus = function() { |
|
250 |
"use strict"; |
|
251 |
var namespace, deprecationMap; |
|
252 |
namespace = "typeahead:"; |
|
253 |
deprecationMap = { |
|
254 |
render: "rendered", |
|
255 |
cursorchange: "cursorchanged", |
|
256 |
select: "selected", |
|
257 |
autocomplete: "autocompleted" |
|
258 |
}; |
|
259 |
function EventBus(o) { |
|
260 |
if (!o || !o.el) { |
|
261 |
$.error("EventBus initialized without el"); |
|
262 |
} |
|
263 |
this.$el = $(o.el); |
|
264 |
} |
|
265 |
_.mixin(EventBus.prototype, { |
|
266 |
_trigger: function(type, args) { |
|
267 |
var $e; |
|
268 |
$e = $.Event(namespace + type); |
|
269 |
(args = args || []).unshift($e); |
|
270 |
this.$el.trigger.apply(this.$el, args); |
|
271 |
return $e; |
|
272 |
}, |
|
273 |
before: function(type) { |
|
274 |
var args, $e; |
|
275 |
args = [].slice.call(arguments, 1); |
|
276 |
$e = this._trigger("before" + type, args); |
|
277 |
return $e.isDefaultPrevented(); |
|
278 |
}, |
|
279 |
trigger: function(type) { |
|
280 |
var deprecatedType; |
|
281 |
this._trigger(type, [].slice.call(arguments, 1)); |
|
282 |
if (deprecatedType = deprecationMap[type]) { |
|
283 |
this._trigger(deprecatedType, [].slice.call(arguments, 1)); |
|
284 |
} |
|
285 |
} |
|
286 |
}); |
|
287 |
return EventBus; |
|
288 |
}(); |
|
289 |
var EventEmitter = function() { |
|
290 |
"use strict"; |
|
291 |
var splitter = /\s+/, nextTick = getNextTick(); |
|
292 |
return { |
|
293 |
onSync: onSync, |
|
294 |
onAsync: onAsync, |
|
295 |
off: off, |
|
296 |
trigger: trigger |
|
297 |
}; |
|
298 |
function on(method, types, cb, context) { |
|
299 |
var type; |
|
300 |
if (!cb) { |
|
301 |
return this; |
|
302 |
} |
|
303 |
types = types.split(splitter); |
|
304 |
cb = context ? bindContext(cb, context) : cb; |
|
305 |
this._callbacks = this._callbacks || {}; |
|
306 |
while (type = types.shift()) { |
|
307 |
this._callbacks[type] = this._callbacks[type] || { |
|
308 |
sync: [], |
|
309 |
async: [] |
|
310 |
}; |
|
311 |
this._callbacks[type][method].push(cb); |
|
312 |
} |
|
313 |
return this; |
|
314 |
} |
|
315 |
function onAsync(types, cb, context) { |
|
316 |
return on.call(this, "async", types, cb, context); |
|
317 |
} |
|
318 |
function onSync(types, cb, context) { |
|
319 |
return on.call(this, "sync", types, cb, context); |
|
320 |
} |
|
321 |
function off(types) { |
|
322 |
var type; |
|
323 |
if (!this._callbacks) { |
|
324 |
return this; |
|
325 |
} |
|
326 |
types = types.split(splitter); |
|
327 |
while (type = types.shift()) { |
|
328 |
delete this._callbacks[type]; |
|
329 |
} |
|
330 |
return this; |
|
331 |
} |
|
332 |
function trigger(types) { |
|
333 |
var type, callbacks, args, syncFlush, asyncFlush; |
|
334 |
if (!this._callbacks) { |
|
335 |
return this; |
|
336 |
} |
|
337 |
types = types.split(splitter); |
|
338 |
args = [].slice.call(arguments, 1); |
|
339 |
while ((type = types.shift()) && (callbacks = this._callbacks[type])) { |
|
340 |
syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); |
|
341 |
asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); |
|
342 |
syncFlush() && nextTick(asyncFlush); |
|
343 |
} |
|
344 |
return this; |
|
345 |
} |
|
346 |
function getFlush(callbacks, context, args) { |
|
347 |
return flush; |
|
348 |
function flush() { |
|
349 |
var cancelled; |
|
350 |
for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { |
|
351 |
cancelled = callbacks[i].apply(context, args) === false; |
|
352 |
} |
|
353 |
return !cancelled; |
|
354 |
} |
|
355 |
} |
|
356 |
function getNextTick() { |
|
357 |
var nextTickFn; |
|
358 |
if (window.setImmediate) { |
|
359 |
nextTickFn = function nextTickSetImmediate(fn) { |
|
360 |
setImmediate(function() { |
|
361 |
fn(); |
|
362 |
}); |
|
363 |
}; |
|
364 |
} else { |
|
365 |
nextTickFn = function nextTickSetTimeout(fn) { |
|
366 |
setTimeout(function() { |
|
367 |
fn(); |
|
368 |
}, 0); |
|
369 |
}; |
|
370 |
} |
|
371 |
return nextTickFn; |
|
372 |
} |
|
373 |
function bindContext(fn, context) { |
|
374 |
return fn.bind ? fn.bind(context) : function() { |
|
375 |
fn.apply(context, [].slice.call(arguments, 0)); |
|
376 |
}; |
|
377 |
} |
|
378 |
}(); |
|
379 |
var highlight = function(doc) { |
|
380 |
"use strict"; |
|
381 |
var defaults = { |
|
382 |
node: null, |
|
383 |
pattern: null, |
|
384 |
tagName: "strong", |
|
385 |
className: null, |
|
386 |
wordsOnly: false, |
|
387 |
caseSensitive: false |
|
388 |
}; |
|
389 |
return function hightlight(o) { |
|
390 |
var regex; |
|
391 |
o = _.mixin({}, defaults, o); |
|
392 |
if (!o.node || !o.pattern) { |
|
393 |
return; |
|
394 |
} |
|
395 |
o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; |
|
396 |
regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly); |
|
397 |
traverse(o.node, hightlightTextNode); |
|
398 |
function hightlightTextNode(textNode) { |
|
399 |
var match, patternNode, wrapperNode; |
|
400 |
if (match = regex.exec(textNode.data)) { |
|
401 |
wrapperNode = doc.createElement(o.tagName); |
|
402 |
o.className && (wrapperNode.className = o.className); |
|
403 |
patternNode = textNode.splitText(match.index); |
|
404 |
patternNode.splitText(match[0].length); |
|
405 |
wrapperNode.appendChild(patternNode.cloneNode(true)); |
|
406 |
textNode.parentNode.replaceChild(wrapperNode, patternNode); |
|
407 |
} |
|
408 |
return !!match; |
|
409 |
} |
|
410 |
function traverse(el, hightlightTextNode) { |
|
411 |
var childNode, TEXT_NODE_TYPE = 3; |
|
412 |
for (var i = 0; i < el.childNodes.length; i++) { |
|
413 |
childNode = el.childNodes[i]; |
|
414 |
if (childNode.nodeType === TEXT_NODE_TYPE) { |
|
415 |
i += hightlightTextNode(childNode) ? 1 : 0; |
|
416 |
} else { |
|
417 |
traverse(childNode, hightlightTextNode); |
|
418 |
} |
|
419 |
} |
|
420 |
} |
|
421 |
}; |
|
422 |
function getRegex(patterns, caseSensitive, wordsOnly) { |
|
423 |
var escapedPatterns = [], regexStr; |
|
424 |
for (var i = 0, len = patterns.length; i < len; i++) { |
|
425 |
escapedPatterns.push(_.escapeRegExChars(patterns[i])); |
|
426 |
} |
|
427 |
regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; |
|
428 |
return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); |
|
429 |
} |
|
430 |
}(window.document); |
|
431 |
var Input = function() { |
|
432 |
"use strict"; |
|
433 |
var specialKeyCodeMap; |
|
434 |
specialKeyCodeMap = { |
|
435 |
9: "tab", |
|
436 |
27: "esc", |
|
437 |
37: "left", |
|
438 |
39: "right", |
|
439 |
13: "enter", |
|
440 |
38: "up", |
|
441 |
40: "down" |
|
442 |
}; |
|
443 |
function Input(o, www) { |
|
444 |
o = o || {}; |
|
445 |
if (!o.input) { |
|
446 |
$.error("input is missing"); |
|
447 |
} |
|
448 |
www.mixin(this); |
|
449 |
this.$hint = $(o.hint); |
|
450 |
this.$input = $(o.input); |
|
451 |
this.query = this.$input.val(); |
|
452 |
this.queryWhenFocused = this.hasFocus() ? this.query : null; |
|
453 |
this.$overflowHelper = buildOverflowHelper(this.$input); |
|
454 |
this._checkLanguageDirection(); |
|
455 |
if (this.$hint.length === 0) { |
|
456 |
this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; |
|
457 |
} |
|
458 |
} |
|
459 |
Input.normalizeQuery = function(str) { |
|
460 |
return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); |
|
461 |
}; |
|
462 |
_.mixin(Input.prototype, EventEmitter, { |
|
463 |
_onBlur: function onBlur() { |
|
464 |
this.resetInputValue(); |
|
465 |
this.trigger("blurred"); |
|
466 |
}, |
|
467 |
_onFocus: function onFocus() { |
|
468 |
this.queryWhenFocused = this.query; |
|
469 |
this.trigger("focused"); |
|
470 |
}, |
|
471 |
_onKeydown: function onKeydown($e) { |
|
472 |
var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; |
|
473 |
this._managePreventDefault(keyName, $e); |
|
474 |
if (keyName && this._shouldTrigger(keyName, $e)) { |
|
475 |
this.trigger(keyName + "Keyed", $e); |
|
476 |
} |
|
477 |
}, |
|
478 |
_onInput: function onInput() { |
|
479 |
this._setQuery(this.getInputValue()); |
|
480 |
this.clearHintIfInvalid(); |
|
481 |
this._checkLanguageDirection(); |
|
482 |
}, |
|
483 |
_managePreventDefault: function managePreventDefault(keyName, $e) { |
|
484 |
var preventDefault; |
|
485 |
switch (keyName) { |
|
486 |
case "up": |
|
487 |
case "down": |
|
488 |
preventDefault = !withModifier($e); |
|
489 |
break; |
|
490 |
|
|
491 |
default: |
|
492 |
preventDefault = false; |
|
493 |
} |
|
494 |
preventDefault && $e.preventDefault(); |
|
495 |
}, |
|
496 |
_shouldTrigger: function shouldTrigger(keyName, $e) { |
|
497 |
var trigger; |
|
498 |
switch (keyName) { |
|
499 |
case "tab": |
|
500 |
trigger = !withModifier($e); |
|
501 |
break; |
|
502 |
|
|
503 |
default: |
|
504 |
trigger = true; |
|
505 |
} |
|
506 |
return trigger; |
|
507 |
}, |
|
508 |
_checkLanguageDirection: function checkLanguageDirection() { |
|
509 |
var dir = (this.$input.css("direction") || "ltr").toLowerCase(); |
|
510 |
if (this.dir !== dir) { |
|
511 |
this.dir = dir; |
|
512 |
this.$hint.attr("dir", dir); |
|
513 |
this.trigger("langDirChanged", dir); |
|
514 |
} |
|
515 |
}, |
|
516 |
_setQuery: function setQuery(val, silent) { |
|
517 |
var areEquivalent, hasDifferentWhitespace; |
|
518 |
areEquivalent = areQueriesEquivalent(val, this.query); |
|
519 |
hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false; |
|
520 |
this.query = val; |
|
521 |
if (!silent && !areEquivalent) { |
|
522 |
this.trigger("queryChanged", this.query); |
|
523 |
} else if (!silent && hasDifferentWhitespace) { |
|
524 |
this.trigger("whitespaceChanged", this.query); |
|
525 |
} |
|
526 |
}, |
|
527 |
bind: function() { |
|
528 |
var that = this, onBlur, onFocus, onKeydown, onInput; |
|
529 |
onBlur = _.bind(this._onBlur, this); |
|
530 |
onFocus = _.bind(this._onFocus, this); |
|
531 |
onKeydown = _.bind(this._onKeydown, this); |
|
532 |
onInput = _.bind(this._onInput, this); |
|
533 |
this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); |
|
534 |
if (!_.isMsie() || _.isMsie() > 9) { |
|
535 |
this.$input.on("input.tt", onInput); |
|
536 |
} else { |
|
537 |
this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { |
|
538 |
if (specialKeyCodeMap[$e.which || $e.keyCode]) { |
|
539 |
return; |
|
540 |
} |
|
541 |
_.defer(_.bind(that._onInput, that, $e)); |
|
542 |
}); |
|
543 |
} |
|
544 |
return this; |
|
545 |
}, |
|
546 |
focus: function focus() { |
|
547 |
this.$input.focus(); |
|
548 |
}, |
|
549 |
blur: function blur() { |
|
550 |
this.$input.blur(); |
|
551 |
}, |
|
552 |
getLangDir: function getLangDir() { |
|
553 |
return this.dir; |
|
554 |
}, |
|
555 |
getQuery: function getQuery() { |
|
556 |
return this.query || ""; |
|
557 |
}, |
|
558 |
setQuery: function setQuery(val, silent) { |
|
559 |
this.setInputValue(val); |
|
560 |
this._setQuery(val, silent); |
|
561 |
}, |
|
562 |
hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() { |
|
563 |
return this.query !== this.queryWhenFocused; |
|
564 |
}, |
|
565 |
getInputValue: function getInputValue() { |
|
566 |
return this.$input.val(); |
|
567 |
}, |
|
568 |
setInputValue: function setInputValue(value) { |
|
569 |
this.$input.val(value); |
|
570 |
this.clearHintIfInvalid(); |
|
571 |
this._checkLanguageDirection(); |
|
572 |
}, |
|
573 |
resetInputValue: function resetInputValue() { |
|
574 |
this.setInputValue(this.query); |
|
575 |
}, |
|
576 |
getHint: function getHint() { |
|
577 |
return this.$hint.val(); |
|
578 |
}, |
|
579 |
setHint: function setHint(value) { |
|
580 |
this.$hint.val(value); |
|
581 |
}, |
|
582 |
clearHint: function clearHint() { |
|
583 |
this.setHint(""); |
|
584 |
}, |
|
585 |
clearHintIfInvalid: function clearHintIfInvalid() { |
|
586 |
var val, hint, valIsPrefixOfHint, isValid; |
|
587 |
val = this.getInputValue(); |
|
588 |
hint = this.getHint(); |
|
589 |
valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; |
|
590 |
isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); |
|
591 |
!isValid && this.clearHint(); |
|
592 |
}, |
|
593 |
hasFocus: function hasFocus() { |
|
594 |
return this.$input.is(":focus"); |
|
595 |
}, |
|
596 |
hasOverflow: function hasOverflow() { |
|
597 |
var constraint = this.$input.width() - 2; |
|
598 |
this.$overflowHelper.text(this.getInputValue()); |
|
599 |
return this.$overflowHelper.width() >= constraint; |
|
600 |
}, |
|
601 |
isCursorAtEnd: function() { |
|
602 |
var valueLength, selectionStart, range; |
|
603 |
valueLength = this.$input.val().length; |
|
604 |
selectionStart = this.$input[0].selectionStart; |
|
605 |
if (_.isNumber(selectionStart)) { |
|
606 |
return selectionStart === valueLength; |
|
607 |
} else if (document.selection) { |
|
608 |
range = document.selection.createRange(); |
|
609 |
range.moveStart("character", -valueLength); |
|
610 |
return valueLength === range.text.length; |
|
611 |
} |
|
612 |
return true; |
|
613 |
}, |
|
614 |
destroy: function destroy() { |
|
615 |
this.$hint.off(".tt"); |
|
616 |
this.$input.off(".tt"); |
|
617 |
this.$overflowHelper.remove(); |
|
618 |
this.$hint = this.$input = this.$overflowHelper = $("<div>"); |
|
619 |
} |
|
620 |
}); |
|
621 |
return Input; |
|
622 |
function buildOverflowHelper($input) { |
|
623 |
return $('<pre aria-hidden="true"></pre>').css({ |
|
624 |
position: "absolute", |
|
625 |
visibility: "hidden", |
|
626 |
whiteSpace: "pre", |
|
627 |
fontFamily: $input.css("font-family"), |
|
628 |
fontSize: $input.css("font-size"), |
|
629 |
fontStyle: $input.css("font-style"), |
|
630 |
fontVariant: $input.css("font-variant"), |
|
631 |
fontWeight: $input.css("font-weight"), |
|
632 |
wordSpacing: $input.css("word-spacing"), |
|
633 |
letterSpacing: $input.css("letter-spacing"), |
|
634 |
textIndent: $input.css("text-indent"), |
|
635 |
textRendering: $input.css("text-rendering"), |
|
636 |
textTransform: $input.css("text-transform") |
|
637 |
}).insertAfter($input); |
|
638 |
} |
|
639 |
function areQueriesEquivalent(a, b) { |
|
640 |
return Input.normalizeQuery(a) === Input.normalizeQuery(b); |
|
641 |
} |
|
642 |
function withModifier($e) { |
|
643 |
return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; |
|
644 |
} |
|
645 |
}(); |
|
646 |
var Dataset = function() { |
|
647 |
"use strict"; |
|
648 |
var keys, nameGenerator; |
|
649 |
keys = { |
|
650 |
val: "tt-selectable-display", |
|
651 |
obj: "tt-selectable-object" |
|
652 |
}; |
|
653 |
nameGenerator = _.getIdGenerator(); |
|
654 |
function Dataset(o, www) { |
|
655 |
o = o || {}; |
|
656 |
o.templates = o.templates || {}; |
|
657 |
o.templates.notFound = o.templates.notFound || o.templates.empty; |
|
658 |
if (!o.source) { |
|
659 |
$.error("missing source"); |
|
660 |
} |
|
661 |
if (!o.node) { |
|
662 |
$.error("missing node"); |
|
663 |
} |
|
664 |
if (o.name && !isValidName(o.name)) { |
|
665 |
$.error("invalid dataset name: " + o.name); |
|
666 |
} |
|
667 |
www.mixin(this); |
|
668 |
this.highlight = !!o.highlight; |
|
669 |
this.name = o.name || nameGenerator(); |
|
670 |
this.limit = o.limit || 5; |
|
671 |
this.displayFn = getDisplayFn(o.display || o.displayKey); |
|
672 |
this.templates = getTemplates(o.templates, this.displayFn); |
|
673 |
this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; |
|
674 |
this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; |
|
675 |
this._resetLastSuggestion(); |
|
676 |
this.$el = $(o.node).addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); |
|
677 |
} |
|
678 |
Dataset.extractData = function extractData(el) { |
|
679 |
var $el = $(el); |
|
680 |
if ($el.data(keys.obj)) { |
|
681 |
return { |
|
682 |
val: $el.data(keys.val) || "", |
|
683 |
obj: $el.data(keys.obj) || null |
|
684 |
}; |
|
685 |
} |
|
686 |
return null; |
|
687 |
}; |
|
688 |
_.mixin(Dataset.prototype, EventEmitter, { |
|
689 |
_overwrite: function overwrite(query, suggestions) { |
|
690 |
suggestions = suggestions || []; |
|
691 |
if (suggestions.length) { |
|
692 |
this._renderSuggestions(query, suggestions); |
|
693 |
} else if (this.async && this.templates.pending) { |
|
694 |
this._renderPending(query); |
|
695 |
} else if (!this.async && this.templates.notFound) { |
|
696 |
this._renderNotFound(query); |
|
697 |
} else { |
|
698 |
this._empty(); |
|
699 |
} |
|
700 |
this.trigger("rendered", this.name, suggestions, false); |
|
701 |
}, |
|
702 |
_append: function append(query, suggestions) { |
|
703 |
suggestions = suggestions || []; |
|
704 |
if (suggestions.length && this.$lastSuggestion.length) { |
|
705 |
this._appendSuggestions(query, suggestions); |
|
706 |
} else if (suggestions.length) { |
|
707 |
this._renderSuggestions(query, suggestions); |
|
708 |
} else if (!this.$lastSuggestion.length && this.templates.notFound) { |
|
709 |
this._renderNotFound(query); |
|
710 |
} |
|
711 |
this.trigger("rendered", this.name, suggestions, true); |
|
712 |
}, |
|
713 |
_renderSuggestions: function renderSuggestions(query, suggestions) { |
|
714 |
var $fragment; |
|
715 |
$fragment = this._getSuggestionsFragment(query, suggestions); |
|
716 |
this.$lastSuggestion = $fragment.children().last(); |
|
717 |
this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions)); |
|
718 |
}, |
|
719 |
_appendSuggestions: function appendSuggestions(query, suggestions) { |
|
720 |
var $fragment, $lastSuggestion; |
|
721 |
$fragment = this._getSuggestionsFragment(query, suggestions); |
|
722 |
$lastSuggestion = $fragment.children().last(); |
|
723 |
this.$lastSuggestion.after($fragment); |
|
724 |
this.$lastSuggestion = $lastSuggestion; |
|
725 |
}, |
|
726 |
_renderPending: function renderPending(query) { |
|
727 |
var template = this.templates.pending; |
|
728 |
this._resetLastSuggestion(); |
|
729 |
template && this.$el.html(template({ |
|
730 |
query: query, |
|
731 |
dataset: this.name |
|
732 |
})); |
|
733 |
}, |
|
734 |
_renderNotFound: function renderNotFound(query) { |
|
735 |
var template = this.templates.notFound; |
|
736 |
this._resetLastSuggestion(); |
|
737 |
template && this.$el.html(template({ |
|
738 |
query: query, |
|
739 |
dataset: this.name |
|
740 |
})); |
|
741 |
}, |
|
742 |
_empty: function empty() { |
|
743 |
this.$el.empty(); |
|
744 |
this._resetLastSuggestion(); |
|
745 |
}, |
|
746 |
_getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) { |
|
747 |
var that = this, fragment; |
|
748 |
fragment = document.createDocumentFragment(); |
|
749 |
_.each(suggestions, function getSuggestionNode(suggestion) { |
|
750 |
var $el, context; |
|
751 |
context = that._injectQuery(query, suggestion); |
|
752 |
$el = $(that.templates.suggestion(context)).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); |
|
753 |
fragment.appendChild($el[0]); |
|
754 |
}); |
|
755 |
this.highlight && highlight({ |
|
756 |
className: this.classes.highlight, |
|
757 |
node: fragment, |
|
758 |
pattern: query |
|
759 |
}); |
|
760 |
return $(fragment); |
|
761 |
}, |
|
762 |
_getFooter: function getFooter(query, suggestions) { |
|
763 |
return this.templates.footer ? this.templates.footer({ |
|
764 |
query: query, |
|
765 |
suggestions: suggestions, |
|
766 |
dataset: this.name |
|
767 |
}) : null; |
|
768 |
}, |
|
769 |
_getHeader: function getHeader(query, suggestions) { |
|
770 |
return this.templates.header ? this.templates.header({ |
|
771 |
query: query, |
|
772 |
suggestions: suggestions, |
|
773 |
dataset: this.name |
|
774 |
}) : null; |
|
775 |
}, |
|
776 |
_resetLastSuggestion: function resetLastSuggestion() { |
|
777 |
this.$lastSuggestion = $(); |
|
778 |
}, |
|
779 |
_injectQuery: function injectQuery(query, obj) { |
|
780 |
return _.isObject(obj) ? _.mixin({ |
|
781 |
_query: query |
|
782 |
}, obj) : obj; |
|
783 |
}, |
|
784 |
update: function update(query) { |
|
785 |
var that = this, canceled = false, syncCalled = false, rendered = 0; |
|
786 |
this.cancel(); |
|
787 |
this.cancel = function cancel() { |
|
788 |
canceled = true; |
|
789 |
that.cancel = $.noop; |
|
790 |
that.async && that.trigger("asyncCanceled", query); |
|
791 |
}; |
|
792 |
this.source(query, sync, async); |
|
793 |
!syncCalled && sync([]); |
|
794 |
function sync(suggestions) { |
|
795 |
if (syncCalled) { |
|
796 |
return; |
|
797 |
} |
|
798 |
syncCalled = true; |
|
799 |
suggestions = (suggestions || []).slice(0, that.limit); |
|
800 |
rendered = suggestions.length; |
|
801 |
that._overwrite(query, suggestions); |
|
802 |
if (rendered < that.limit && that.async) { |
|
803 |
that.trigger("asyncRequested", query); |
|
804 |
} |
|
805 |
} |
|
806 |
function async(suggestions) { |
|
807 |
suggestions = suggestions || []; |
|
808 |
if (!canceled && rendered < that.limit) { |
|
809 |
that.cancel = $.noop; |
|
810 |
rendered += suggestions.length; |
|
811 |
that._append(query, suggestions.slice(0, that.limit - rendered)); |
|
812 |
that.async && that.trigger("asyncReceived", query); |
|
813 |
} |
|
814 |
} |
|
815 |
}, |
|
816 |
cancel: $.noop, |
|
817 |
clear: function clear() { |
|
818 |
this._empty(); |
|
819 |
this.cancel(); |
|
820 |
this.trigger("cleared"); |
|
821 |
}, |
|
822 |
isEmpty: function isEmpty() { |
|
823 |
return this.$el.is(":empty"); |
|
824 |
}, |
|
825 |
destroy: function destroy() { |
|
826 |
this.$el = $("<div>"); |
|
827 |
} |
|
828 |
}); |
|
829 |
return Dataset; |
|
830 |
function getDisplayFn(display) { |
|
831 |
display = display || _.stringify; |
|
832 |
return _.isFunction(display) ? display : displayFn; |
|
833 |
function displayFn(obj) { |
|
834 |
return obj[display]; |
|
835 |
} |
|
836 |
} |
|
837 |
function getTemplates(templates, displayFn) { |
|
838 |
return { |
|
839 |
notFound: templates.notFound && _.templatify(templates.notFound), |
|
840 |
pending: templates.pending && _.templatify(templates.pending), |
|
841 |
header: templates.header && _.templatify(templates.header), |
|
842 |
footer: templates.footer && _.templatify(templates.footer), |
|
843 |
suggestion: templates.suggestion || suggestionTemplate |
|
844 |
}; |
|
845 |
function suggestionTemplate(context) { |
|
846 |
return $("<div>").text(displayFn(context)); |
|
847 |
} |
|
848 |
} |
|
849 |
function isValidName(str) { |
|
850 |
return /^[_a-zA-Z0-9-]+$/.test(str); |
|
851 |
} |
|
852 |
}(); |
|
853 |
var Menu = function() { |
|
854 |
"use strict"; |
|
855 |
function Menu(o, www) { |
|
856 |
var that = this; |
|
857 |
o = o || {}; |
|
858 |
if (!o.node) { |
|
859 |
$.error("node is required"); |
|
860 |
} |
|
861 |
www.mixin(this); |
|
862 |
this.$node = $(o.node); |
|
863 |
this.query = null; |
|
864 |
this.datasets = _.map(o.datasets, initializeDataset); |
|
865 |
function initializeDataset(oDataset) { |
|
866 |
var node = that.$node.find(oDataset.node).first(); |
|
867 |
oDataset.node = node.length ? node : $("<div>").appendTo(that.$node); |
|
868 |
return new Dataset(oDataset, www); |
|
869 |
} |
|
870 |
} |
|
871 |
_.mixin(Menu.prototype, EventEmitter, { |
|
872 |
_onSelectableClick: function onSelectableClick($e) { |
|
873 |
this.trigger("selectableClicked", $($e.currentTarget)); |
|
874 |
}, |
|
875 |
_onRendered: function onRendered(type, dataset, suggestions, async) { |
|
876 |
this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); |
|
877 |
this.trigger("datasetRendered", dataset, suggestions, async); |
|
878 |
}, |
|
879 |
_onCleared: function onCleared() { |
|
880 |
this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); |
|
881 |
this.trigger("datasetCleared"); |
|
882 |
}, |
|
883 |
_propagate: function propagate() { |
|
884 |
this.trigger.apply(this, arguments); |
|
885 |
}, |
|
886 |
_allDatasetsEmpty: function allDatasetsEmpty() { |
|
887 |
return _.every(this.datasets, isDatasetEmpty); |
|
888 |
function isDatasetEmpty(dataset) { |
|
889 |
return dataset.isEmpty(); |
|
890 |
} |
|
891 |
}, |
|
892 |
_getSelectables: function getSelectables() { |
|
893 |
return this.$node.find(this.selectors.selectable); |
|
894 |
}, |
|
895 |
_removeCursor: function _removeCursor() { |
|
896 |
var $selectable = this.getActiveSelectable(); |
|
897 |
$selectable && $selectable.removeClass(this.classes.cursor); |
|
898 |
}, |
|
899 |
_ensureVisible: function ensureVisible($el) { |
|
900 |
var elTop, elBottom, nodeScrollTop, nodeHeight; |
|
901 |
elTop = $el.position().top; |
|
902 |
elBottom = elTop + $el.outerHeight(true); |
|
903 |
nodeScrollTop = this.$node.scrollTop(); |
|
904 |
nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10); |
|
905 |
if (elTop < 0) { |
|
906 |
this.$node.scrollTop(nodeScrollTop + elTop); |
|
907 |
} else if (nodeHeight < elBottom) { |
|
908 |
this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight)); |
|
909 |
} |
|
910 |
}, |
|
911 |
bind: function() { |
|
912 |
var that = this, onSelectableClick; |
|
913 |
onSelectableClick = _.bind(this._onSelectableClick, this); |
|
914 |
this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); |
|
915 |
_.each(this.datasets, function(dataset) { |
|
916 |
dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); |
|
917 |
}); |
|
918 |
return this; |
|
919 |
}, |
|
920 |
isOpen: function isOpen() { |
|
921 |
return this.$node.hasClass(this.classes.open); |
|
922 |
}, |
|
923 |
open: function open() { |
|
924 |
this.$node.addClass(this.classes.open); |
|
925 |
}, |
|
926 |
close: function close() { |
|
927 |
this.$node.removeClass(this.classes.open); |
|
928 |
this._removeCursor(); |
|
929 |
}, |
|
930 |
setLanguageDirection: function setLanguageDirection(dir) { |
|
931 |
this.$node.attr("dir", dir); |
|
932 |
}, |
|
933 |
selectableRelativeToCursor: function selectableRelativeToCursor(delta) { |
|
934 |
var $selectables, $oldCursor, oldIndex, newIndex; |
|
935 |
$oldCursor = this.getActiveSelectable(); |
|
936 |
$selectables = this._getSelectables(); |
|
937 |
oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1; |
|
938 |
newIndex = oldIndex + delta; |
|
939 |
newIndex = (newIndex + 1) % ($selectables.length + 1) - 1; |
|
940 |
newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex; |
|
941 |
return newIndex === -1 ? null : $selectables.eq(newIndex); |
|
942 |
}, |
|
943 |
setCursor: function setCursor($selectable) { |
|
944 |
this._removeCursor(); |
|
945 |
if ($selectable = $selectable && $selectable.first()) { |
|
946 |
$selectable.addClass(this.classes.cursor); |
|
947 |
this._ensureVisible($selectable); |
|
948 |
} |
|
949 |
}, |
|
950 |
getSelectableData: function getSelectableData($el) { |
|
951 |
return $el && $el.length ? Dataset.extractData($el) : null; |
|
952 |
}, |
|
953 |
getActiveSelectable: function getActiveSelectable() { |
|
954 |
var $selectable = this._getSelectables().filter(this.selectors.cursor).first(); |
|
955 |
return $selectable.length ? $selectable : null; |
|
956 |
}, |
|
957 |
getTopSelectable: function getTopSelectable() { |
|
958 |
var $selectable = this._getSelectables().first(); |
|
959 |
return $selectable.length ? $selectable : null; |
|
960 |
}, |
|
961 |
update: function update(query) { |
|
962 |
var isValidUpdate = query !== this.query; |
|
963 |
if (isValidUpdate) { |
|
964 |
this.query = query; |
|
965 |
_.each(this.datasets, updateDataset); |
|
966 |
} |
|
967 |
return isValidUpdate; |
|
968 |
function updateDataset(dataset) { |
|
969 |
dataset.update(query); |
|
970 |
} |
|
971 |
}, |
|
972 |
empty: function empty() { |
|
973 |
_.each(this.datasets, clearDataset); |
|
974 |
this.query = null; |
|
975 |
this.$node.addClass(this.classes.empty); |
|
976 |
function clearDataset(dataset) { |
|
977 |
dataset.clear(); |
|
978 |
} |
|
979 |
}, |
|
980 |
destroy: function destroy() { |
|
981 |
this.$node.off(".tt"); |
|
982 |
this.$node = $("<div>"); |
|
983 |
_.each(this.datasets, destroyDataset); |
|
984 |
function destroyDataset(dataset) { |
|
985 |
dataset.destroy(); |
|
986 |
} |
|
987 |
} |
|
988 |
}); |
|
989 |
return Menu; |
|
990 |
}(); |
|
991 |
var DefaultMenu = function() { |
|
992 |
"use strict"; |
|
993 |
var s = Menu.prototype; |
|
994 |
function DefaultMenu() { |
|
995 |
Menu.apply(this, [].slice.call(arguments, 0)); |
|
996 |
} |
|
997 |
_.mixin(DefaultMenu.prototype, Menu.prototype, { |
|
998 |
open: function open() { |
|
999 |
!this._allDatasetsEmpty() && this._show(); |
|
1000 |
return s.open.apply(this, [].slice.call(arguments, 0)); |
|
1001 |
}, |
|
1002 |
close: function close() { |
|
1003 |
this._hide(); |
|
1004 |
return s.close.apply(this, [].slice.call(arguments, 0)); |
|
1005 |
}, |
|
1006 |
_onRendered: function onRendered() { |
|
1007 |
if (this._allDatasetsEmpty()) { |
|
1008 |
this._hide(); |
|
1009 |
} else { |
|
1010 |
this.isOpen() && this._show(); |
|
1011 |
} |
|
1012 |
return s._onRendered.apply(this, [].slice.call(arguments, 0)); |
|
1013 |
}, |
|
1014 |
_onCleared: function onCleared() { |
|
1015 |
if (this._allDatasetsEmpty()) { |
|
1016 |
this._hide(); |
|
1017 |
} else { |
|
1018 |
this.isOpen() && this._show(); |
|
1019 |
} |
|
1020 |
return s._onCleared.apply(this, [].slice.call(arguments, 0)); |
|
1021 |
}, |
|
1022 |
setLanguageDirection: function setLanguageDirection(dir) { |
|
1023 |
this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl); |
|
1024 |
return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0)); |
|
1025 |
}, |
|
1026 |
_hide: function hide() { |
|
1027 |
this.$node.hide(); |
|
1028 |
}, |
|
1029 |
_show: function show() { |
|
1030 |
this.$node.css("display", "block"); |
|
1031 |
} |
|
1032 |
}); |
|
1033 |
return DefaultMenu; |
|
1034 |
}(); |
|
1035 |
var Typeahead = function() { |
|
1036 |
"use strict"; |
|
1037 |
function Typeahead(o, www) { |
|
1038 |
var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged; |
|
1039 |
o = o || {}; |
|
1040 |
if (!o.input) { |
|
1041 |
$.error("missing input"); |
|
1042 |
} |
|
1043 |
if (!o.menu) { |
|
1044 |
$.error("missing menu"); |
|
1045 |
} |
|
1046 |
if (!o.eventBus) { |
|
1047 |
$.error("missing event bus"); |
|
1048 |
} |
|
1049 |
www.mixin(this); |
|
1050 |
this.eventBus = o.eventBus; |
|
1051 |
this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; |
|
1052 |
this.input = o.input; |
|
1053 |
this.menu = o.menu; |
|
1054 |
this.enabled = true; |
|
1055 |
this.active = false; |
|
1056 |
this.input.hasFocus() && this.activate(); |
|
1057 |
this.dir = this.input.getLangDir(); |
|
1058 |
this._hacks(); |
|
1059 |
this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this); |
|
1060 |
onFocused = c(this, "activate", "open", "_onFocused"); |
|
1061 |
onBlurred = c(this, "deactivate", "_onBlurred"); |
|
1062 |
onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed"); |
|
1063 |
onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed"); |
|
1064 |
onEscKeyed = c(this, "isActive", "_onEscKeyed"); |
|
1065 |
onUpKeyed = c(this, "isActive", "open", "_onUpKeyed"); |
|
1066 |
onDownKeyed = c(this, "isActive", "open", "_onDownKeyed"); |
|
1067 |
onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed"); |
|
1068 |
onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed"); |
|
1069 |
onQueryChanged = c(this, "_openIfActive", "_onQueryChanged"); |
|
1070 |
onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged"); |
|
1071 |
this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this); |
|
1072 |
} |
|
1073 |
_.mixin(Typeahead.prototype, { |
|
1074 |
_hacks: function hacks() { |
|
1075 |
var $input, $menu; |
|
1076 |
$input = this.input.$input || $("<div>"); |
|
1077 |
$menu = this.menu.$node || $("<div>"); |
|
1078 |
$input.on("blur.tt", function($e) { |
|
1079 |
var active, isActive, hasActive; |
|
1080 |
active = document.activeElement; |
|
1081 |
isActive = $menu.is(active); |
|
1082 |
hasActive = $menu.has(active).length > 0; |
|
1083 |
if (_.isMsie() && (isActive || hasActive)) { |
|
1084 |
$e.preventDefault(); |
|
1085 |
$e.stopImmediatePropagation(); |
|
1086 |
_.defer(function() { |
|
1087 |
$input.focus(); |
|
1088 |
}); |
|
1089 |
} |
|
1090 |
}); |
|
1091 |
$menu.on("mousedown.tt", function($e) { |
|
1092 |
$e.preventDefault(); |
|
1093 |
}); |
|
1094 |
}, |
|
1095 |
_onSelectableClicked: function onSelectableClicked(type, $el) { |
|
1096 |
this.select($el); |
|
1097 |
}, |
|
1098 |
_onDatasetCleared: function onDatasetCleared() { |
|
1099 |
this._updateHint(); |
|
1100 |
}, |
|
1101 |
_onDatasetRendered: function onDatasetRendered(type, dataset, suggestions, async) { |
|
1102 |
this._updateHint(); |
|
1103 |
this.eventBus.trigger("render", suggestions, async, dataset); |
|
1104 |
}, |
|
1105 |
_onAsyncRequested: function onAsyncRequested(type, dataset, query) { |
|
1106 |
this.eventBus.trigger("asyncrequest", query, dataset); |
|
1107 |
}, |
|
1108 |
_onAsyncCanceled: function onAsyncCanceled(type, dataset, query) { |
|
1109 |
this.eventBus.trigger("asynccancel", query, dataset); |
|
1110 |
}, |
|
1111 |
_onAsyncReceived: function onAsyncReceived(type, dataset, query) { |
|
1112 |
this.eventBus.trigger("asyncreceive", query, dataset); |
|
1113 |
}, |
|
1114 |
_onFocused: function onFocused() { |
|
1115 |
this._minLengthMet() && this.menu.update(this.input.getQuery()); |
|
1116 |
}, |
|
1117 |
_onBlurred: function onBlurred() { |
|
1118 |
if (this.input.hasQueryChangedSinceLastFocus()) { |
|
1119 |
this.eventBus.trigger("change", this.input.getQuery()); |
|
1120 |
} |
|
1121 |
}, |
|
1122 |
_onEnterKeyed: function onEnterKeyed(type, $e) { |
|
1123 |
var $selectable; |
|
1124 |
if ($selectable = this.menu.getActiveSelectable()) { |
|
1125 |
this.select($selectable) && $e.preventDefault(); |
|
1126 |
} |
|
1127 |
}, |
|
1128 |
_onTabKeyed: function onTabKeyed(type, $e) { |
|
1129 |
var $selectable; |
|
1130 |
if ($selectable = this.menu.getActiveSelectable()) { |
|
1131 |
this.select($selectable) && $e.preventDefault(); |
|
1132 |
} else if ($selectable = this.menu.getTopSelectable()) { |
|
1133 |
this.autocomplete($selectable) && $e.preventDefault(); |
|
1134 |
} |
|
1135 |
}, |
|
1136 |
_onEscKeyed: function onEscKeyed() { |
|
1137 |
this.close(); |
|
1138 |
}, |
|
1139 |
_onUpKeyed: function onUpKeyed() { |
|
1140 |
this.moveCursor(-1); |
|
1141 |
}, |
|
1142 |
_onDownKeyed: function onDownKeyed() { |
|
1143 |
this.moveCursor(+1); |
|
1144 |
}, |
|
1145 |
_onLeftKeyed: function onLeftKeyed() { |
|
1146 |
if (this.dir === "rtl" && this.input.isCursorAtEnd()) { |
|
1147 |
this.autocomplete(this.menu.getTopSelectable()); |
|
1148 |
} |
|
1149 |
}, |
|
1150 |
_onRightKeyed: function onRightKeyed() { |
|
1151 |
if (this.dir === "ltr" && this.input.isCursorAtEnd()) { |
|
1152 |
this.autocomplete(this.menu.getTopSelectable()); |
|
1153 |
} |
|
1154 |
}, |
|
1155 |
_onQueryChanged: function onQueryChanged(e, query) { |
|
1156 |
this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty(); |
|
1157 |
}, |
|
1158 |
_onWhitespaceChanged: function onWhitespaceChanged() { |
|
1159 |
this._updateHint(); |
|
1160 |
}, |
|
1161 |
_onLangDirChanged: function onLangDirChanged(e, dir) { |
|
1162 |
if (this.dir !== dir) { |
|
1163 |
this.dir = dir; |
|
1164 |
this.menu.setLanguageDirection(dir); |
|
1165 |
} |
|
1166 |
}, |
|
1167 |
_openIfActive: function openIfActive() { |
|
1168 |
this.isActive() && this.open(); |
|
1169 |
}, |
|
1170 |
_minLengthMet: function minLengthMet(query) { |
|
1171 |
query = _.isString(query) ? query : this.input.getQuery() || ""; |
|
1172 |
return query.length >= this.minLength; |
|
1173 |
}, |
|
1174 |
_updateHint: function updateHint() { |
|
1175 |
var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match; |
|
1176 |
$selectable = this.menu.getTopSelectable(); |
|
1177 |
data = this.menu.getSelectableData($selectable); |
|
1178 |
val = this.input.getInputValue(); |
|
1179 |
if (data && !_.isBlankString(val) && !this.input.hasOverflow()) { |
|
1180 |
query = Input.normalizeQuery(val); |
|
1181 |
escapedQuery = _.escapeRegExChars(query); |
|
1182 |
frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); |
|
1183 |
match = frontMatchRegEx.exec(data.val); |
|
1184 |
match && this.input.setHint(val + match[1]); |
|
1185 |
} else { |
|
1186 |
this.input.clearHint(); |
|
1187 |
} |
|
1188 |
}, |
|
1189 |
isEnabled: function isEnabled() { |
|
1190 |
return this.enabled; |
|
1191 |
}, |
|
1192 |
enable: function enable() { |
|
1193 |
this.enabled = true; |
|
1194 |
}, |
|
1195 |
disable: function disable() { |
|
1196 |
this.enabled = false; |
|
1197 |
}, |
|
1198 |
isActive: function isActive() { |
|
1199 |
return this.active; |
|
1200 |
}, |
|
1201 |
activate: function activate() { |
|
1202 |
if (this.isActive()) { |
|
1203 |
return true; |
|
1204 |
} else if (!this.isEnabled() || this.eventBus.before("active")) { |
|
1205 |
return false; |
|
1206 |
} else { |
|
1207 |
this.active = true; |
|
1208 |
this.eventBus.trigger("active"); |
|
1209 |
return true; |
|
1210 |
} |
|
1211 |
}, |
|
1212 |
deactivate: function deactivate() { |
|
1213 |
if (!this.isActive()) { |
|
1214 |
return true; |
|
1215 |
} else if (this.eventBus.before("idle")) { |
|
1216 |
return false; |
|
1217 |
} else { |
|
1218 |
this.active = false; |
|
1219 |
this.close(); |
|
1220 |
this.eventBus.trigger("idle"); |
|
1221 |
return true; |
|
1222 |
} |
|
1223 |
}, |
|
1224 |
isOpen: function isOpen() { |
|
1225 |
return this.menu.isOpen(); |
|
1226 |
}, |
|
1227 |
open: function open() { |
|
1228 |
if (!this.isOpen() && !this.eventBus.before("open")) { |
|
1229 |
this.menu.open(); |
|
1230 |
this._updateHint(); |
|
1231 |
this.eventBus.trigger("open"); |
|
1232 |
} |
|
1233 |
return this.isOpen(); |
|
1234 |
}, |
|
1235 |
close: function close() { |
|
1236 |
if (this.isOpen() && !this.eventBus.before("close")) { |
|
1237 |
this.menu.close(); |
|
1238 |
this.input.clearHint(); |
|
1239 |
this.input.resetInputValue(); |
|
1240 |
this.eventBus.trigger("close"); |
|
1241 |
} |
|
1242 |
return !this.isOpen(); |
|
1243 |
}, |
|
1244 |
setVal: function setVal(val) { |
|
1245 |
this.input.setQuery(_.toStr(val)); |
|
1246 |
}, |
|
1247 |
getVal: function getVal() { |
|
1248 |
return this.input.getQuery(); |
|
1249 |
}, |
|
1250 |
select: function select($selectable) { |
|
1251 |
var data = this.menu.getSelectableData($selectable); |
|
1252 |
if (data && !this.eventBus.before("select", data.obj)) { |
|
1253 |
this.input.setQuery(data.val, true); |
|
1254 |
this.eventBus.trigger("select", data.obj); |
|
1255 |
this.close(); |
|
1256 |
return true; |
|
1257 |
} |
|
1258 |
return false; |
|
1259 |
}, |
|
1260 |
autocomplete: function autocomplete($selectable) { |
|
1261 |
var query, data, isValid; |
|
1262 |
query = this.input.getQuery(); |
|
1263 |
data = this.menu.getSelectableData($selectable); |
|
1264 |
isValid = data && query !== data.val; |
|
1265 |
if (isValid && !this.eventBus.before("autocomplete", data.obj)) { |
|
1266 |
this.input.setQuery(data.val); |
|
1267 |
this.eventBus.trigger("autocomplete", data.obj); |
|
1268 |
return true; |
|
1269 |
} |
|
1270 |
return false; |
|
1271 |
}, |
|
1272 |
moveCursor: function moveCursor(delta) { |
|
1273 |
var query, $candidate, data, payload, cancelMove; |
|
1274 |
query = this.input.getQuery(); |
|
1275 |
$candidate = this.menu.selectableRelativeToCursor(delta); |
|
1276 |
data = this.menu.getSelectableData($candidate); |
|
1277 |
payload = data ? data.obj : null; |
|
1278 |
cancelMove = this._minLengthMet() && this.menu.update(query); |
|
1279 |
if (!cancelMove && !this.eventBus.before("cursorchange", payload)) { |
|
1280 |
this.menu.setCursor($candidate); |
|
1281 |
if (data) { |
|
1282 |
this.input.setInputValue(data.val); |
|
1283 |
} else { |
|
1284 |
this.input.resetInputValue(); |
|
1285 |
this._updateHint(); |
|
1286 |
} |
|
1287 |
this.eventBus.trigger("cursorchange", payload); |
|
1288 |
return true; |
|
1289 |
} |
|
1290 |
return false; |
|
1291 |
}, |
|
1292 |
destroy: function destroy() { |
|
1293 |
this.input.destroy(); |
|
1294 |
this.menu.destroy(); |
|
1295 |
} |
|
1296 |
}); |
|
1297 |
return Typeahead; |
|
1298 |
function c(ctx) { |
|
1299 |
var methods = [].slice.call(arguments, 1); |
|
1300 |
return function() { |
|
1301 |
var args = [].slice.call(arguments); |
|
1302 |
_.each(methods, function(method) { |
|
1303 |
return ctx[method].apply(ctx, args); |
|
1304 |
}); |
|
1305 |
}; |
|
1306 |
} |
|
1307 |
}(); |
|
1308 |
(function() { |
|
1309 |
"use strict"; |
|
1310 |
var old, keys, methods; |
|
1311 |
old = $.fn.typeahead; |
|
1312 |
keys = { |
|
1313 |
www: "tt-www", |
|
1314 |
attrs: "tt-attrs", |
|
1315 |
typeahead: "tt-typeahead" |
|
1316 |
}; |
|
1317 |
methods = { |
|
1318 |
initialize: function initialize(o, datasets) { |
|
1319 |
var www; |
|
1320 |
datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); |
|
1321 |
o = o || {}; |
|
1322 |
www = WWW(o.classNames); |
|
1323 |
return this.each(attach); |
|
1324 |
function attach() { |
|
1325 |
var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, typeahead, MenuConstructor; |
|
1326 |
_.each(datasets, function(d) { |
|
1327 |
d.highlight = !!o.highlight; |
|
1328 |
}); |
|
1329 |
$input = $(this); |
|
1330 |
$wrapper = $(www.html.wrapper); |
|
1331 |
$hint = $elOrNull(o.hint); |
|
1332 |
$menu = $elOrNull(o.menu); |
|
1333 |
defaultHint = o.hint !== false && !$hint; |
|
1334 |
defaultMenu = o.menu !== false && !$menu; |
|
1335 |
defaultHint && ($hint = buildHintFromInput($input, www)); |
|
1336 |
defaultMenu && ($menu = $(www.html.menu).css(www.css.menu)); |
|
1337 |
$hint && $hint.val(""); |
|
1338 |
$input = prepInput($input, www); |
|
1339 |
if (defaultHint || defaultMenu) { |
|
1340 |
$wrapper.css(www.css.wrapper); |
|
1341 |
$input.css(defaultHint ? www.css.input : www.css.inputWithNoHint); |
|
1342 |
$input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null); |
|
1343 |
} |
|
1344 |
MenuConstructor = defaultMenu ? DefaultMenu : Menu; |
|
1345 |
eventBus = new EventBus({ |
|
1346 |
el: $input |
|
1347 |
}); |
|
1348 |
input = new Input({ |
|
1349 |
hint: $hint, |
|
1350 |
input: $input |
|
1351 |
}, www); |
|
1352 |
menu = new MenuConstructor({ |
|
1353 |
node: $menu, |
|
1354 |
datasets: datasets |
|
1355 |
}, www); |
|
1356 |
typeahead = new Typeahead({ |
|
1357 |
input: input, |
|
1358 |
menu: menu, |
|
1359 |
eventBus: eventBus, |
|
1360 |
minLength: o.minLength |
|
1361 |
}, www); |
|
1362 |
$input.data(keys.www, www); |
|
1363 |
$input.data(keys.typeahead, typeahead); |
|
1364 |
} |
|
1365 |
}, |
|
1366 |
isEnabled: function isEnabled() { |
|
1367 |
var enabled; |
|
1368 |
ttEach(this.first(), function(t) { |
|
1369 |
enabled = t.isEnabled(); |
|
1370 |
}); |
|
1371 |
return enabled; |
|
1372 |
}, |
|
1373 |
enable: function enable() { |
|
1374 |
ttEach(this, function(t) { |
|
1375 |
t.enable(); |
|
1376 |
}); |
|
1377 |
return this; |
|
1378 |
}, |
|
1379 |
disable: function disable() { |
|
1380 |
ttEach(this, function(t) { |
|
1381 |
t.disable(); |
|
1382 |
}); |
|
1383 |
return this; |
|
1384 |
}, |
|
1385 |
isActive: function isActive() { |
|
1386 |
var active; |
|
1387 |
ttEach(this.first(), function(t) { |
|
1388 |
active = t.isActive(); |
|
1389 |
}); |
|
1390 |
return active; |
|
1391 |
}, |
|
1392 |
activate: function activate() { |
|
1393 |
ttEach(this, function(t) { |
|
1394 |
t.activate(); |
|
1395 |
}); |
|
1396 |
return this; |
|
1397 |
}, |
|
1398 |
deactivate: function deactivate() { |
|
1399 |
ttEach(this, function(t) { |
|
1400 |
t.deactivate(); |
|
1401 |
}); |
|
1402 |
return this; |
|
1403 |
}, |
|
1404 |
isOpen: function isOpen() { |
|
1405 |
var open; |
|
1406 |
ttEach(this.first(), function(t) { |
|
1407 |
open = t.isOpen(); |
|
1408 |
}); |
|
1409 |
return open; |
|
1410 |
}, |
|
1411 |
open: function open() { |
|
1412 |
ttEach(this, function(t) { |
|
1413 |
t.open(); |
|
1414 |
}); |
|
1415 |
return this; |
|
1416 |
}, |
|
1417 |
close: function close() { |
|
1418 |
ttEach(this, function(t) { |
|
1419 |
t.close(); |
|
1420 |
}); |
|
1421 |
return this; |
|
1422 |
}, |
|
1423 |
select: function select(el) { |
|
1424 |
var success = false, $el = $(el); |
|
1425 |
ttEach(this.first(), function(t) { |
|
1426 |
success = t.select($el); |
|
1427 |
}); |
|
1428 |
return success; |
|
1429 |
}, |
|
1430 |
autocomplete: function autocomplete(el) { |
|
1431 |
var success = false, $el = $(el); |
|
1432 |
ttEach(this.first(), function(t) { |
|
1433 |
success = t.autocomplete($el); |
|
1434 |
}); |
|
1435 |
return success; |
|
1436 |
}, |
|
1437 |
moveCursor: function moveCursoe(delta) { |
|
1438 |
var success = false; |
|
1439 |
ttEach(this.first(), function(t) { |
|
1440 |
success = t.moveCursor(delta); |
|
1441 |
}); |
|
1442 |
return success; |
|
1443 |
}, |
|
1444 |
val: function val(newVal) { |
|
1445 |
var query; |
|
1446 |
if (!arguments.length) { |
|
1447 |
ttEach(this.first(), function(t) { |
|
1448 |
query = t.getVal(); |
|
1449 |
}); |
|
1450 |
return query; |
|
1451 |
} else { |
|
1452 |
ttEach(this, function(t) { |
|
1453 |
t.setVal(newVal); |
|
1454 |
}); |
|
1455 |
return this; |
|
1456 |
} |
|
1457 |
}, |
|
1458 |
destroy: function destroy() { |
|
1459 |
ttEach(this, function(typeahead, $input) { |
|
1460 |
revert($input); |
|
1461 |
typeahead.destroy(); |
|
1462 |
}); |
|
1463 |
return this; |
|
1464 |
} |
|
1465 |
}; |
|
1466 |
$.fn.typeahead = function(method) { |
|
1467 |
if (methods[method]) { |
|
1468 |
return methods[method].apply(this, [].slice.call(arguments, 1)); |
|
1469 |
} else { |
|
1470 |
return methods.initialize.apply(this, arguments); |
|
1471 |
} |
|
1472 |
}; |
|
1473 |
$.fn.typeahead.noConflict = function noConflict() { |
|
1474 |
$.fn.typeahead = old; |
|
1475 |
return this; |
|
1476 |
}; |
|
1477 |
function ttEach($els, fn) { |
|
1478 |
$els.each(function() { |
|
1479 |
var $input = $(this), typeahead; |
|
1480 |
(typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input); |
|
1481 |
}); |
|
1482 |
} |
|
1483 |
function buildHintFromInput($input, www) { |
|
1484 |
return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop("readonly", true).removeAttr("id name placeholder required").attr({ |
|
1485 |
autocomplete: "off", |
|
1486 |
spellcheck: "false", |
|
1487 |
tabindex: -1 |
|
1488 |
}); |
|
1489 |
} |
|
1490 |
function prepInput($input, www) { |
|
1491 |
$input.data(keys.attrs, { |
|
1492 |
dir: $input.attr("dir"), |
|
1493 |
autocomplete: $input.attr("autocomplete"), |
|
1494 |
spellcheck: $input.attr("spellcheck"), |
|
1495 |
style: $input.attr("style") |
|
1496 |
}); |
|
1497 |
$input.addClass(www.classes.input).attr({ |
|
1498 |
autocomplete: "off", |
|
1499 |
spellcheck: false |
|
1500 |
}); |
|
1501 |
try { |
|
1502 |
!$input.attr("dir") && $input.attr("dir", "auto"); |
|
1503 |
} catch (e) {} |
|
1504 |
return $input; |
|
1505 |
} |
|
1506 |
function getBackgroundStyles($el) { |
|
1507 |
return { |
|
1508 |
backgroundAttachment: $el.css("background-attachment"), |
|
1509 |
backgroundClip: $el.css("background-clip"), |
|
1510 |
backgroundColor: $el.css("background-color"), |
|
1511 |
backgroundImage: $el.css("background-image"), |
|
1512 |
backgroundOrigin: $el.css("background-origin"), |
|
1513 |
backgroundPosition: $el.css("background-position"), |
|
1514 |
backgroundRepeat: $el.css("background-repeat"), |
|
1515 |
backgroundSize: $el.css("background-size") |
|
1516 |
}; |
|
1517 |
} |
|
1518 |
function revert($input) { |
|
1519 |
var www, $wrapper; |
|
1520 |
www = $input.data(keys.www); |
|
1521 |
$wrapper = $input.parent().filter(www.selectors.wrapper); |
|
1522 |
_.each($input.data(keys.attrs), function(val, key) { |
|
1523 |
_.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); |
|
1524 |
}); |
|
1525 |
$input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input); |
|
1526 |
if ($wrapper.length) { |
|
1527 |
$input.detach().insertAfter($wrapper); |
|
1528 |
$wrapper.remove(); |
|
1529 |
} |
|
1530 |
} |
|
1531 |
function $elOrNull(obj) { |
|
1532 |
var isValid, $el; |
|
1533 |
isValid = _.isJQuery(obj) || _.isElement(obj); |
|
1534 |
$el = isValid ? $(obj).first() : []; |
|
1535 |
return $el.length ? $el : null; |
|
1536 |
} |
|
1537 |
})(); |
|
1538 |
}); |