twitter-typeahead-rails 0.8.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -0
- data/README.md +4 -12
- data/lib/twitter-typeahead-rails/version.rb +1 -1
- data/vendor/assets/javascripts/twitter/typeahead/typeahead.js +653 -511
- data/vendor/assets/javascripts/twitter/typeahead/typeahead.min.js +2 -2
- metadata +2 -6
- data/vendor/assets/stylesheets/twitter/typeahead.css +0 -3
- data/vendor/assets/stylesheets/twitter/typeahead.min.css +0 -3
- data/vendor/assets/stylesheets/twitter/typeahead/typeahead.css +0 -72
- data/vendor/assets/stylesheets/twitter/typeahead/typeahead.min.css +0 -7
data/.gitignore
CHANGED
data/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Add this line to your application's Gemfile:
|
|
|
14
14
|
|
|
15
15
|
or
|
|
16
16
|
|
|
17
|
-
gem 'twitter-typeahead-rails', :git => "git
|
|
17
|
+
gem 'twitter-typeahead-rails', :git => "git://github.com/yourabi/twitter-typeahead-rails.git"
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
And then execute:
|
|
@@ -34,6 +34,7 @@ Add one the folllwing to your application.js mainifest:
|
|
|
34
34
|
```js
|
|
35
35
|
|
|
36
36
|
//= require twitter/typeahead
|
|
37
|
+
|
|
37
38
|
//= require twitter/typeahead.min
|
|
38
39
|
|
|
39
40
|
```
|
|
@@ -48,17 +49,7 @@ $(document).ready(function() {
|
|
|
48
49
|
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
```js
|
|
54
|
-
|
|
55
|
-
*= require twitter/typeahead
|
|
56
|
-
|
|
57
|
-
*= require twitter/typeahead.min
|
|
58
|
-
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
Currently this version tracks version v0.8.0.
|
|
52
|
+
Currently this version tracks version v0.9.2.
|
|
62
53
|
|
|
63
54
|
## Contributing
|
|
64
55
|
|
|
@@ -67,3 +58,4 @@ Currently this version tracks version v0.8.0.
|
|
|
67
58
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
68
59
|
4. Push to the branch (`git push origin my-new-feature`)
|
|
69
60
|
5. Create new Pull Request
|
|
61
|
+
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
*
|
|
2
|
+
* typeahead.js 0.9.2
|
|
3
3
|
* https://github.com/twitter/typeahead
|
|
4
4
|
* Copyright 2013 Twitter, Inc. and other contributors; Licensed MIT
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
(function() {
|
|
8
|
-
var VERSION = "0.
|
|
7
|
+
(function($) {
|
|
8
|
+
var VERSION = "0.9.2";
|
|
9
9
|
var utils = {
|
|
10
10
|
isMsie: function() {
|
|
11
|
-
|
|
11
|
+
var match = /(msie) ([\w.]+)/i.exec(navigator.userAgent);
|
|
12
|
+
return match ? parseInt(match[2], 10) : false;
|
|
13
|
+
},
|
|
14
|
+
isBlankString: function(str) {
|
|
15
|
+
return !str || /^\s*$/.test(str);
|
|
16
|
+
},
|
|
17
|
+
escapeRegExChars: function(str) {
|
|
18
|
+
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
|
12
19
|
},
|
|
13
20
|
isString: function(obj) {
|
|
14
21
|
return typeof obj === "string";
|
|
@@ -18,9 +25,7 @@
|
|
|
18
25
|
},
|
|
19
26
|
isArray: $.isArray,
|
|
20
27
|
isFunction: $.isFunction,
|
|
21
|
-
isObject:
|
|
22
|
-
return obj !== Object(obj);
|
|
23
|
-
},
|
|
28
|
+
isObject: $.isPlainObject,
|
|
24
29
|
isUndefined: function(obj) {
|
|
25
30
|
return typeof obj === "undefined";
|
|
26
31
|
},
|
|
@@ -28,7 +33,7 @@
|
|
|
28
33
|
bindAll: function(obj) {
|
|
29
34
|
var val;
|
|
30
35
|
for (var key in obj) {
|
|
31
|
-
|
|
36
|
+
$.isFunction(val = obj[key]) && (obj[key] = $.proxy(val, obj));
|
|
32
37
|
}
|
|
33
38
|
},
|
|
34
39
|
indexOf: function(haystack, needle) {
|
|
@@ -41,15 +46,7 @@
|
|
|
41
46
|
},
|
|
42
47
|
each: $.each,
|
|
43
48
|
map: $.map,
|
|
44
|
-
filter:
|
|
45
|
-
var results = [];
|
|
46
|
-
$.each(obj, function(key, val) {
|
|
47
|
-
if (test(val, key, obj)) {
|
|
48
|
-
results.push(val);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
return results;
|
|
52
|
-
},
|
|
49
|
+
filter: $.grep,
|
|
53
50
|
every: function(obj, test) {
|
|
54
51
|
var result = true;
|
|
55
52
|
if (!obj) {
|
|
@@ -62,13 +59,17 @@
|
|
|
62
59
|
});
|
|
63
60
|
return !!result;
|
|
64
61
|
},
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
some: function(obj, test) {
|
|
63
|
+
var result = false;
|
|
64
|
+
if (!obj) {
|
|
65
|
+
return result;
|
|
68
66
|
}
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
$.each(obj, function(key, val) {
|
|
68
|
+
if (result = test.call(null, val, key, obj)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
71
|
});
|
|
72
|
+
return !!result;
|
|
72
73
|
},
|
|
73
74
|
mixin: $.extend,
|
|
74
75
|
getUniqueId: function() {
|
|
@@ -77,6 +78,9 @@
|
|
|
77
78
|
return counter++;
|
|
78
79
|
};
|
|
79
80
|
}(),
|
|
81
|
+
defer: function(fn) {
|
|
82
|
+
setTimeout(fn, 0);
|
|
83
|
+
},
|
|
80
84
|
debounce: function(func, wait, immediate) {
|
|
81
85
|
var timeout, result;
|
|
82
86
|
return function() {
|
|
@@ -119,16 +123,8 @@
|
|
|
119
123
|
return result;
|
|
120
124
|
};
|
|
121
125
|
},
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
for (var i = 0, l = array.length; i < l; ++i) {
|
|
125
|
-
if (u.hasOwnProperty(array[i])) {
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
a.push(array[i]);
|
|
129
|
-
u[array[i]] = 1;
|
|
130
|
-
}
|
|
131
|
-
return a;
|
|
126
|
+
tokenizeQuery: function(str) {
|
|
127
|
+
return $.trim(str).toLowerCase().split(/[\s]+/);
|
|
132
128
|
},
|
|
133
129
|
tokenizeText: function(str) {
|
|
134
130
|
return $.trim(str).toLowerCase().split(/[\s\-_]+/);
|
|
@@ -174,49 +170,75 @@
|
|
|
174
170
|
}
|
|
175
171
|
};
|
|
176
172
|
}();
|
|
173
|
+
var EventBus = function() {
|
|
174
|
+
var namespace = "typeahead:";
|
|
175
|
+
function EventBus(o) {
|
|
176
|
+
if (!o || !o.el) {
|
|
177
|
+
$.error("EventBus initialized without el");
|
|
178
|
+
}
|
|
179
|
+
this.$el = $(o.el);
|
|
180
|
+
}
|
|
181
|
+
utils.mixin(EventBus.prototype, {
|
|
182
|
+
trigger: function(type) {
|
|
183
|
+
var args = [].slice.call(arguments, 1);
|
|
184
|
+
this.$el.trigger(namespace + type, args);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
return EventBus;
|
|
188
|
+
}();
|
|
177
189
|
var PersistentStorage = function() {
|
|
178
|
-
var ls
|
|
190
|
+
var ls, methods;
|
|
191
|
+
try {
|
|
192
|
+
ls = window.localStorage;
|
|
193
|
+
} catch (err) {
|
|
194
|
+
ls = null;
|
|
195
|
+
}
|
|
179
196
|
function PersistentStorage(namespace) {
|
|
180
197
|
this.prefix = [ "__", namespace, "__" ].join("");
|
|
181
198
|
this.ttlKey = "__ttl__";
|
|
182
199
|
this.keyMatcher = new RegExp("^" + this.prefix);
|
|
183
200
|
}
|
|
184
|
-
if (
|
|
201
|
+
if (ls && window.JSON) {
|
|
185
202
|
methods = {
|
|
203
|
+
_prefix: function(key) {
|
|
204
|
+
return this.prefix + key;
|
|
205
|
+
},
|
|
206
|
+
_ttlKey: function(key) {
|
|
207
|
+
return this._prefix(key) + this.ttlKey;
|
|
208
|
+
},
|
|
186
209
|
get: function(key) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
ls.removeItem(this.prefix + key + this.ttlKey);
|
|
210
|
+
if (this.isExpired(key)) {
|
|
211
|
+
this.remove(key);
|
|
190
212
|
}
|
|
191
|
-
return decode(ls.getItem(this.
|
|
213
|
+
return decode(ls.getItem(this._prefix(key)));
|
|
192
214
|
},
|
|
193
215
|
set: function(key, val, ttl) {
|
|
194
216
|
if (utils.isNumber(ttl)) {
|
|
195
|
-
ls.setItem(this.
|
|
217
|
+
ls.setItem(this._ttlKey(key), encode(now() + ttl));
|
|
196
218
|
} else {
|
|
197
|
-
ls.removeItem(this.
|
|
219
|
+
ls.removeItem(this._ttlKey(key));
|
|
198
220
|
}
|
|
199
|
-
return ls.setItem(this.
|
|
221
|
+
return ls.setItem(this._prefix(key), encode(val));
|
|
200
222
|
},
|
|
201
223
|
remove: function(key) {
|
|
202
|
-
ls.removeItem(this.
|
|
203
|
-
ls.removeItem(this.
|
|
224
|
+
ls.removeItem(this._ttlKey(key));
|
|
225
|
+
ls.removeItem(this._prefix(key));
|
|
204
226
|
return this;
|
|
205
227
|
},
|
|
206
228
|
clear: function() {
|
|
207
|
-
var i, key, len = ls.length;
|
|
208
|
-
for (i = 0; i < len; i
|
|
209
|
-
key = ls.key(i)
|
|
210
|
-
|
|
211
|
-
i -= 1;
|
|
212
|
-
len -= 1;
|
|
213
|
-
this.remove(key.replace(this.keyMatcher, ""));
|
|
229
|
+
var i, key, keys = [], len = ls.length;
|
|
230
|
+
for (i = 0; i < len; i++) {
|
|
231
|
+
if ((key = ls.key(i)).match(this.keyMatcher)) {
|
|
232
|
+
keys.push(key.replace(this.keyMatcher, ""));
|
|
214
233
|
}
|
|
215
234
|
}
|
|
235
|
+
for (i = keys.length; i--; ) {
|
|
236
|
+
this.remove(keys[i]);
|
|
237
|
+
}
|
|
216
238
|
return this;
|
|
217
239
|
},
|
|
218
240
|
isExpired: function(key) {
|
|
219
|
-
var ttl = decode(ls.getItem(this.
|
|
241
|
+
var ttl = decode(ls.getItem(this._ttlKey(key)));
|
|
220
242
|
return utils.isNumber(ttl) && now() > ttl ? true : false;
|
|
221
243
|
}
|
|
222
244
|
};
|
|
@@ -235,10 +257,10 @@
|
|
|
235
257
|
return new Date().getTime();
|
|
236
258
|
}
|
|
237
259
|
function encode(val) {
|
|
238
|
-
return JSON.stringify(val);
|
|
260
|
+
return JSON.stringify(utils.isUndefined(val) ? null : val);
|
|
239
261
|
}
|
|
240
262
|
function decode(val) {
|
|
241
|
-
return
|
|
263
|
+
return JSON.parse(val);
|
|
242
264
|
}
|
|
243
265
|
}();
|
|
244
266
|
var RequestCache = function() {
|
|
@@ -266,381 +288,350 @@
|
|
|
266
288
|
return RequestCache;
|
|
267
289
|
}();
|
|
268
290
|
var Transport = function() {
|
|
291
|
+
var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests, requestCache;
|
|
269
292
|
function Transport(o) {
|
|
270
|
-
var rateLimitFn;
|
|
271
293
|
utils.bindAll(this);
|
|
272
|
-
o = o
|
|
273
|
-
|
|
274
|
-
|
|
294
|
+
o = utils.isString(o) ? {
|
|
295
|
+
url: o
|
|
296
|
+
} : o;
|
|
297
|
+
requestCache = requestCache || new RequestCache();
|
|
298
|
+
maxPendingRequests = utils.isNumber(o.maxParallelRequests) ? o.maxParallelRequests : maxPendingRequests || 6;
|
|
299
|
+
this.url = o.url;
|
|
275
300
|
this.wildcard = o.wildcard || "%QUERY";
|
|
276
|
-
this.
|
|
277
|
-
this.
|
|
278
|
-
this.
|
|
279
|
-
|
|
280
|
-
|
|
301
|
+
this.filter = o.filter;
|
|
302
|
+
this.replace = o.replace;
|
|
303
|
+
this.ajaxSettings = {
|
|
304
|
+
type: "get",
|
|
305
|
+
cache: o.cache,
|
|
306
|
+
timeout: o.timeout,
|
|
307
|
+
dataType: o.dataType || "json",
|
|
308
|
+
beforeSend: o.beforeSend
|
|
309
|
+
};
|
|
310
|
+
this._get = (/^throttle$/i.test(o.rateLimitFn) ? utils.throttle : utils.debounce)(this._get, o.rateLimitWait || 300);
|
|
281
311
|
}
|
|
282
312
|
utils.mixin(Transport.prototype, {
|
|
283
|
-
|
|
284
|
-
this
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
this.concurrentRequests--;
|
|
288
|
-
},
|
|
289
|
-
_belowConcurrentRequestsThreshold: function() {
|
|
290
|
-
return this.concurrentRequests < this.maxConcurrentRequests;
|
|
291
|
-
},
|
|
292
|
-
get: function(url, query, cb) {
|
|
293
|
-
var that = this, resp;
|
|
294
|
-
url = url.replace(this.wildcard, encodeURIComponent(query || ""));
|
|
295
|
-
if (resp = this.cache.get(url)) {
|
|
296
|
-
cb && cb(resp);
|
|
297
|
-
} else if (this._belowConcurrentRequestsThreshold()) {
|
|
298
|
-
$.ajax({
|
|
299
|
-
url: url,
|
|
300
|
-
type: "GET",
|
|
301
|
-
dataType: "json",
|
|
302
|
-
beforeSend: function() {
|
|
303
|
-
that._incrementConcurrentRequests();
|
|
304
|
-
},
|
|
305
|
-
success: function(resp) {
|
|
306
|
-
cb && cb(resp);
|
|
307
|
-
that.cache.set(url, resp);
|
|
308
|
-
},
|
|
309
|
-
complete: function() {
|
|
310
|
-
that._decrementConcurrentRequests();
|
|
311
|
-
if (that.onDeckRequestArgs) {
|
|
312
|
-
that.get.apply(that, that.onDeckRequestArgs);
|
|
313
|
-
that.onDeckRequestArgs = null;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
});
|
|
313
|
+
_get: function(url, cb) {
|
|
314
|
+
var that = this;
|
|
315
|
+
if (belowPendingRequestsThreshold()) {
|
|
316
|
+
this._sendRequest(url).done(done);
|
|
317
317
|
} else {
|
|
318
318
|
this.onDeckRequestArgs = [].slice.call(arguments, 0);
|
|
319
319
|
}
|
|
320
|
+
function done(resp) {
|
|
321
|
+
var data = that.filter ? that.filter(resp) : resp;
|
|
322
|
+
cb && cb(data);
|
|
323
|
+
requestCache.set(url, resp);
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
_sendRequest: function(url) {
|
|
327
|
+
var that = this, jqXhr = pendingRequests[url];
|
|
328
|
+
if (!jqXhr) {
|
|
329
|
+
incrementPendingRequests();
|
|
330
|
+
jqXhr = pendingRequests[url] = $.ajax(url, this.ajaxSettings).always(always);
|
|
331
|
+
}
|
|
332
|
+
return jqXhr;
|
|
333
|
+
function always() {
|
|
334
|
+
decrementPendingRequests();
|
|
335
|
+
pendingRequests[url] = null;
|
|
336
|
+
if (that.onDeckRequestArgs) {
|
|
337
|
+
that._get.apply(that, that.onDeckRequestArgs);
|
|
338
|
+
that.onDeckRequestArgs = null;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
get: function(query, cb) {
|
|
343
|
+
var that = this, encodedQuery = encodeURIComponent(query || ""), url, resp;
|
|
344
|
+
cb = cb || utils.noop;
|
|
345
|
+
url = this.replace ? this.replace(this.url, encodedQuery) : this.url.replace(this.wildcard, encodedQuery);
|
|
346
|
+
if (resp = requestCache.get(url)) {
|
|
347
|
+
utils.defer(function() {
|
|
348
|
+
cb(that.filter ? that.filter(resp) : resp);
|
|
349
|
+
});
|
|
350
|
+
} else {
|
|
351
|
+
this._get(url, cb);
|
|
352
|
+
}
|
|
353
|
+
return !!resp;
|
|
320
354
|
}
|
|
321
355
|
});
|
|
322
356
|
return Transport;
|
|
357
|
+
function incrementPendingRequests() {
|
|
358
|
+
pendingRequestsCount++;
|
|
359
|
+
}
|
|
360
|
+
function decrementPendingRequests() {
|
|
361
|
+
pendingRequestsCount--;
|
|
362
|
+
}
|
|
363
|
+
function belowPendingRequestsThreshold() {
|
|
364
|
+
return pendingRequestsCount < maxPendingRequests;
|
|
365
|
+
}
|
|
323
366
|
}();
|
|
324
367
|
var Dataset = function() {
|
|
368
|
+
var keys = {
|
|
369
|
+
thumbprint: "thumbprint",
|
|
370
|
+
protocol: "protocol",
|
|
371
|
+
itemHash: "itemHash",
|
|
372
|
+
adjacencyList: "adjacencyList"
|
|
373
|
+
};
|
|
325
374
|
function Dataset(o) {
|
|
326
375
|
utils.bindAll(this);
|
|
327
|
-
|
|
328
|
-
|
|
376
|
+
if (utils.isString(o.template) && !o.engine) {
|
|
377
|
+
$.error("no template engine specified");
|
|
378
|
+
}
|
|
379
|
+
if (!o.local && !o.prefetch && !o.remote) {
|
|
380
|
+
$.error("one of local, prefetch, or remote is required");
|
|
381
|
+
}
|
|
382
|
+
this.name = o.name || utils.getUniqueId();
|
|
383
|
+
this.limit = o.limit || 5;
|
|
384
|
+
this.minLength = o.minLength || 1;
|
|
385
|
+
this.header = o.header;
|
|
386
|
+
this.footer = o.footer;
|
|
387
|
+
this.valueKey = o.valueKey || "value";
|
|
388
|
+
this.template = compileTemplate(o.template, o.engine, this.valueKey);
|
|
389
|
+
this.local = o.local;
|
|
390
|
+
this.prefetch = o.prefetch;
|
|
391
|
+
this.remote = o.remote;
|
|
329
392
|
this.itemHash = {};
|
|
330
|
-
this.
|
|
331
|
-
this.
|
|
332
|
-
this.prefetchUrl = o.prefetch;
|
|
333
|
-
this.queryUrl = o.remote;
|
|
334
|
-
this.rawData = o.local;
|
|
335
|
-
this.transport = o.transport;
|
|
336
|
-
this.limit = o.limit || 10;
|
|
337
|
-
this._customMatcher = o.matcher || null;
|
|
338
|
-
this._customRanker = o.ranker || null;
|
|
339
|
-
this._ttl_ms = o.ttl_ms || 3 * 24 * 60 * 60 * 1e3;
|
|
340
|
-
this.storageAdjacencyList = "adjacencyList";
|
|
341
|
-
this.storageHash = "itemHash";
|
|
342
|
-
this.storageProtocol = "protocol";
|
|
343
|
-
this.storageVersion = "version";
|
|
344
|
-
this._loadData();
|
|
393
|
+
this.adjacencyList = {};
|
|
394
|
+
this.storage = o.name ? new PersistentStorage(o.name) : null;
|
|
345
395
|
}
|
|
346
396
|
utils.mixin(Dataset.prototype, {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
var isCacheStale = this.storage.isExpired(this.storageAdjacencyList) || this.storage.isExpired(this.storageHash);
|
|
350
|
-
var resetForProtocolSwitch = this.resetDataOnProtocolSwitch && this.storage.get(this.storageProtocol) != utils.getProtocol();
|
|
351
|
-
if (VERSION == this.storage.get(this.storageVersion) && !resetForProtocolSwitch && !isExpired && !isCacheStale) {
|
|
352
|
-
return false;
|
|
353
|
-
}
|
|
354
|
-
return true;
|
|
355
|
-
},
|
|
356
|
-
_loadData: function() {
|
|
357
|
-
this.rawData && this._processRawData(this.rawData);
|
|
358
|
-
this._getDataFromLocalStorage();
|
|
359
|
-
if (this._isMetadataExpired() || this.itemHash === {}) {
|
|
360
|
-
this.prefetchUrl && this._prefetch(this.prefetchUrl);
|
|
361
|
-
}
|
|
397
|
+
_processLocalData: function(data) {
|
|
398
|
+
this._mergeProcessedData(this._processData(data));
|
|
362
399
|
},
|
|
363
|
-
|
|
364
|
-
this
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
utils.map(terms, utils.bind(function(term) {
|
|
371
|
-
var list = this.adjacencyList[term.charAt(0)];
|
|
372
|
-
if (!list) {
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
lists.push(list);
|
|
376
|
-
}, this));
|
|
377
|
-
if (lists.length === 1) {
|
|
378
|
-
return lists[0];
|
|
400
|
+
_loadPrefetchData: function(o) {
|
|
401
|
+
var that = this, thumbprint = VERSION + (o.thumbprint || ""), storedThumbprint, storedProtocol, storedItemHash, storedAdjacencyList, isExpired, deferred;
|
|
402
|
+
if (this.storage) {
|
|
403
|
+
storedThumbprint = this.storage.get(keys.thumbprint);
|
|
404
|
+
storedProtocol = this.storage.get(keys.protocol);
|
|
405
|
+
storedItemHash = this.storage.get(keys.itemHash);
|
|
406
|
+
storedAdjacencyList = this.storage.get(keys.adjacencyList);
|
|
379
407
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
408
|
+
isExpired = storedThumbprint !== thumbprint || storedProtocol !== utils.getProtocol();
|
|
409
|
+
o = utils.isString(o) ? {
|
|
410
|
+
url: o
|
|
411
|
+
} : o;
|
|
412
|
+
o.ttl = utils.isNumber(o.ttl) ? o.ttl : 24 * 60 * 60 * 1e3;
|
|
413
|
+
if (storedItemHash && storedAdjacencyList && !isExpired) {
|
|
414
|
+
this._mergeProcessedData({
|
|
415
|
+
itemHash: storedItemHash,
|
|
416
|
+
adjacencyList: storedAdjacencyList
|
|
389
417
|
});
|
|
390
|
-
|
|
391
|
-
return item;
|
|
392
|
-
}
|
|
393
|
-
});
|
|
394
|
-
return potentiallyMatchingIds;
|
|
395
|
-
},
|
|
396
|
-
_getItemsFromIds: function(ids) {
|
|
397
|
-
var items = [];
|
|
398
|
-
utils.map(ids, utils.bind(function(id) {
|
|
399
|
-
var item = this.itemHash[id];
|
|
400
|
-
if (item) {
|
|
401
|
-
items.push(item);
|
|
402
|
-
}
|
|
403
|
-
}, this));
|
|
404
|
-
return items;
|
|
405
|
-
},
|
|
406
|
-
_matcher: function(terms) {
|
|
407
|
-
if (this._customMatcher) {
|
|
408
|
-
var customMatcher = this._customMatcher;
|
|
409
|
-
return function(item) {
|
|
410
|
-
return customMatcher(item);
|
|
411
|
-
};
|
|
418
|
+
deferred = $.Deferred().resolve();
|
|
412
419
|
} else {
|
|
413
|
-
|
|
414
|
-
var tokens = item.tokens;
|
|
415
|
-
var allTermsMatched = utils.every(terms, function(term) {
|
|
416
|
-
var tokensMatched = utils.filter(tokens, function(token) {
|
|
417
|
-
return token.indexOf(term) === 0;
|
|
418
|
-
});
|
|
419
|
-
return tokensMatched.length;
|
|
420
|
-
});
|
|
421
|
-
if (allTermsMatched) {
|
|
422
|
-
return item;
|
|
423
|
-
}
|
|
424
|
-
};
|
|
420
|
+
deferred = $.getJSON(o.url).done(processPrefetchData);
|
|
425
421
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
422
|
+
return deferred;
|
|
423
|
+
function processPrefetchData(data) {
|
|
424
|
+
var filteredData = o.filter ? o.filter(data) : data, processedData = that._processData(filteredData), itemHash = processedData.itemHash, adjacencyList = processedData.adjacencyList;
|
|
425
|
+
if (that.storage) {
|
|
426
|
+
that.storage.set(keys.itemHash, itemHash, o.ttl);
|
|
427
|
+
that.storage.set(keys.adjacencyList, adjacencyList, o.ttl);
|
|
428
|
+
that.storage.set(keys.thumbprint, thumbprint, o.ttl);
|
|
429
|
+
that.storage.set(keys.protocol, utils.getProtocol(), o.ttl);
|
|
430
|
+
}
|
|
431
|
+
that._mergeProcessedData(processedData);
|
|
433
432
|
}
|
|
434
433
|
},
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
434
|
+
_transformDatum: function(datum) {
|
|
435
|
+
var value = utils.isString(datum) ? datum : datum[this.valueKey], tokens = datum.tokens || utils.tokenizeText(value), item = {
|
|
436
|
+
value: value,
|
|
437
|
+
tokens: tokens
|
|
438
|
+
};
|
|
439
|
+
if (utils.isString(datum)) {
|
|
440
|
+
item.datum = {};
|
|
441
|
+
item.datum[this.valueKey] = datum;
|
|
438
442
|
} else {
|
|
439
|
-
|
|
440
|
-
var bIsLocal = b.weight && b.weight !== 0;
|
|
441
|
-
if (aIsLocal && !bIsLocal) {
|
|
442
|
-
return -1;
|
|
443
|
-
} else if (bIsLocal && !aIsLocal) {
|
|
444
|
-
return 1;
|
|
445
|
-
} else {
|
|
446
|
-
return aIsLocal && bIsLocal ? this._compareItems(a, b, true) : this._compareItems(a, b, false);
|
|
447
|
-
}
|
|
443
|
+
item.datum = datum;
|
|
448
444
|
}
|
|
445
|
+
item.tokens = utils.filter(item.tokens, function(token) {
|
|
446
|
+
return !utils.isBlankString(token);
|
|
447
|
+
});
|
|
448
|
+
item.tokens = utils.map(item.tokens, function(token) {
|
|
449
|
+
return token.toLowerCase();
|
|
450
|
+
});
|
|
451
|
+
return item;
|
|
449
452
|
},
|
|
450
|
-
|
|
451
|
-
this
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
item = {
|
|
459
|
-
tokens: utils.tokenizeText(item),
|
|
460
|
-
value: item
|
|
461
|
-
};
|
|
462
|
-
tokens = item.tokens;
|
|
463
|
-
}
|
|
464
|
-
item.id = utils.getUniqueId(item.value);
|
|
465
|
-
utils.map(tokens, utils.bind(function(token) {
|
|
466
|
-
var firstChar = token.charAt(0);
|
|
467
|
-
if (!this.adjacencyList[firstChar]) {
|
|
468
|
-
this.adjacencyList[firstChar] = [ item.id ];
|
|
469
|
-
} else {
|
|
470
|
-
if (utils.indexOf(this.adjacencyList[firstChar], item.id) === -1) {
|
|
471
|
-
this.adjacencyList[firstChar].push(item.id);
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
}, this));
|
|
475
|
-
this.itemHash[item.id] = item;
|
|
476
|
-
}, this));
|
|
477
|
-
this.storage.set(this.storageHash, this.itemHash, this._ttl_ms);
|
|
478
|
-
this.storage.set(this.storageAdjacencyList, this.adjacencyList, this._ttl_ms);
|
|
479
|
-
this.storage.set(this.storageVersion, VERSION, this._ttl_ms);
|
|
480
|
-
this.storage.set(this.storageProtocol, utils.getProtocol(), this._ttl_ms);
|
|
481
|
-
},
|
|
482
|
-
_prefetch: function(url) {
|
|
483
|
-
var processPrefetchSuccess = function(data) {
|
|
484
|
-
if (!data) {
|
|
485
|
-
return;
|
|
486
|
-
}
|
|
487
|
-
utils.map(data, function(item) {
|
|
488
|
-
if (utils.isString(item)) {
|
|
489
|
-
return {
|
|
490
|
-
value: item,
|
|
491
|
-
tokens: utils.tokenizeText(item.toLowerCase())
|
|
492
|
-
};
|
|
493
|
-
} else {
|
|
494
|
-
utils.map(item.tokens, function(token, i) {
|
|
495
|
-
item.tokens[i] = token.toLowerCase();
|
|
496
|
-
});
|
|
497
|
-
return item;
|
|
498
|
-
}
|
|
453
|
+
_processData: function(data) {
|
|
454
|
+
var that = this, itemHash = {}, adjacencyList = {};
|
|
455
|
+
utils.each(data, function(i, datum) {
|
|
456
|
+
var item = that._transformDatum(datum), id = utils.getUniqueId(item.value);
|
|
457
|
+
itemHash[id] = item;
|
|
458
|
+
utils.each(item.tokens, function(i, token) {
|
|
459
|
+
var character = token.charAt(0), adjacency = adjacencyList[character] || (adjacencyList[character] = [ id ]);
|
|
460
|
+
!~utils.indexOf(adjacency, id) && adjacency.push(id);
|
|
499
461
|
});
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
462
|
+
});
|
|
463
|
+
return {
|
|
464
|
+
itemHash: itemHash,
|
|
465
|
+
adjacencyList: adjacencyList
|
|
504
466
|
};
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
467
|
+
},
|
|
468
|
+
_mergeProcessedData: function(processedData) {
|
|
469
|
+
var that = this;
|
|
470
|
+
utils.mixin(this.itemHash, processedData.itemHash);
|
|
471
|
+
utils.each(processedData.adjacencyList, function(character, adjacency) {
|
|
472
|
+
var masterAdjacency = that.adjacencyList[character];
|
|
473
|
+
that.adjacencyList[character] = masterAdjacency ? masterAdjacency.concat(adjacency) : adjacency;
|
|
509
474
|
});
|
|
510
475
|
},
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
476
|
+
_getLocalSuggestions: function(terms) {
|
|
477
|
+
var that = this, firstChars = [], lists = [], shortestList, suggestions = [];
|
|
478
|
+
utils.each(terms, function(i, term) {
|
|
479
|
+
var firstChar = term.charAt(0);
|
|
480
|
+
!~utils.indexOf(firstChars, firstChar) && firstChars.push(firstChar);
|
|
481
|
+
});
|
|
482
|
+
utils.each(firstChars, function(i, firstChar) {
|
|
483
|
+
var list = that.adjacencyList[firstChar];
|
|
484
|
+
if (!list) {
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
lists.push(list);
|
|
488
|
+
if (!shortestList || list.length < shortestList.length) {
|
|
489
|
+
shortestList = list;
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
if (lists.length < firstChars.length) {
|
|
493
|
+
return [];
|
|
494
|
+
}
|
|
495
|
+
utils.each(shortestList, function(i, id) {
|
|
496
|
+
var item = that.itemHash[id], isCandidate, isMatch;
|
|
497
|
+
isCandidate = utils.every(lists, function(list) {
|
|
498
|
+
return ~utils.indexOf(list, id);
|
|
534
499
|
});
|
|
535
|
-
utils.
|
|
536
|
-
|
|
500
|
+
isMatch = isCandidate && utils.every(terms, function(term) {
|
|
501
|
+
return utils.some(item.tokens, function(token) {
|
|
502
|
+
return token.indexOf(term) === 0;
|
|
503
|
+
});
|
|
537
504
|
});
|
|
538
|
-
|
|
505
|
+
isMatch && suggestions.push(item);
|
|
506
|
+
});
|
|
507
|
+
return suggestions;
|
|
508
|
+
},
|
|
509
|
+
initialize: function() {
|
|
510
|
+
var deferred;
|
|
511
|
+
this.local && this._processLocalData(this.local);
|
|
512
|
+
this.transport = this.remote ? new Transport(this.remote) : null;
|
|
513
|
+
deferred = this.prefetch ? this._loadPrefetchData(this.prefetch) : $.Deferred().resolve();
|
|
514
|
+
this.local = this.prefetch = this.remote = null;
|
|
515
|
+
this.initialize = function() {
|
|
516
|
+
return deferred;
|
|
539
517
|
};
|
|
518
|
+
return deferred;
|
|
540
519
|
},
|
|
541
|
-
getSuggestions: function(query,
|
|
542
|
-
var terms =
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
if (
|
|
549
|
-
this.transport.get(
|
|
520
|
+
getSuggestions: function(query, cb) {
|
|
521
|
+
var that = this, terms, suggestions, cacheHit = false;
|
|
522
|
+
if (query.length < this.minLength) {
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
terms = utils.tokenizeQuery(query);
|
|
526
|
+
suggestions = this._getLocalSuggestions(terms).slice(0, this.limit);
|
|
527
|
+
if (suggestions.length < this.limit && this.transport) {
|
|
528
|
+
cacheHit = this.transport.get(query, processRemoteData);
|
|
529
|
+
}
|
|
530
|
+
!cacheHit && cb && cb(suggestions);
|
|
531
|
+
function processRemoteData(data) {
|
|
532
|
+
suggestions = suggestions.slice(0);
|
|
533
|
+
utils.each(data, function(i, datum) {
|
|
534
|
+
var item = that._transformDatum(datum), isDuplicate;
|
|
535
|
+
isDuplicate = utils.some(suggestions, function(suggestion) {
|
|
536
|
+
return item.value === suggestion.value;
|
|
537
|
+
});
|
|
538
|
+
!isDuplicate && suggestions.push(item);
|
|
539
|
+
return suggestions.length < that.limit;
|
|
540
|
+
});
|
|
541
|
+
cb && cb(suggestions);
|
|
550
542
|
}
|
|
551
543
|
}
|
|
552
544
|
});
|
|
553
545
|
return Dataset;
|
|
546
|
+
function compileTemplate(template, engine, valueKey) {
|
|
547
|
+
var renderFn, compiledTemplate;
|
|
548
|
+
if (utils.isFunction(template)) {
|
|
549
|
+
renderFn = template;
|
|
550
|
+
} else if (utils.isString(template)) {
|
|
551
|
+
compiledTemplate = engine.compile(template);
|
|
552
|
+
renderFn = utils.bind(compiledTemplate.render, compiledTemplate);
|
|
553
|
+
} else {
|
|
554
|
+
renderFn = function(context) {
|
|
555
|
+
return "<p>" + context[valueKey] + "</p>";
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
return renderFn;
|
|
559
|
+
}
|
|
554
560
|
}();
|
|
555
561
|
var InputView = function() {
|
|
556
562
|
function InputView(o) {
|
|
557
563
|
var that = this;
|
|
558
564
|
utils.bindAll(this);
|
|
559
565
|
this.specialKeyCodeMap = {
|
|
560
|
-
9:
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
event: "left"
|
|
568
|
-
},
|
|
569
|
-
39: {
|
|
570
|
-
event: "right"
|
|
571
|
-
},
|
|
572
|
-
13: {
|
|
573
|
-
event: "enter"
|
|
574
|
-
},
|
|
575
|
-
38: {
|
|
576
|
-
event: "up",
|
|
577
|
-
preventDefault: true
|
|
578
|
-
},
|
|
579
|
-
40: {
|
|
580
|
-
event: "down",
|
|
581
|
-
preventDefault: true
|
|
582
|
-
}
|
|
566
|
+
9: "tab",
|
|
567
|
+
27: "esc",
|
|
568
|
+
37: "left",
|
|
569
|
+
39: "right",
|
|
570
|
+
13: "enter",
|
|
571
|
+
38: "up",
|
|
572
|
+
40: "down"
|
|
583
573
|
};
|
|
584
|
-
this.query = "";
|
|
585
574
|
this.$hint = $(o.hint);
|
|
586
|
-
this.$input = $(o.input).on("blur", this._handleBlur).on("focus", this._handleFocus).on("keydown", this._handleSpecialKeyEvent);
|
|
575
|
+
this.$input = $(o.input).on("blur.tt", this._handleBlur).on("focus.tt", this._handleFocus).on("keydown.tt", this._handleSpecialKeyEvent);
|
|
587
576
|
if (!utils.isMsie()) {
|
|
588
|
-
this.$input.on("input", this._compareQueryToInputValue);
|
|
577
|
+
this.$input.on("input.tt", this._compareQueryToInputValue);
|
|
589
578
|
} else {
|
|
590
|
-
this.$input.on("keydown keypress cut paste", function(e) {
|
|
591
|
-
if (that.specialKeyCodeMap[e.which || e.keyCode]) {
|
|
579
|
+
this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) {
|
|
580
|
+
if (that.specialKeyCodeMap[$e.which || $e.keyCode]) {
|
|
592
581
|
return;
|
|
593
582
|
}
|
|
594
|
-
|
|
583
|
+
utils.defer(that._compareQueryToInputValue);
|
|
595
584
|
});
|
|
596
585
|
}
|
|
586
|
+
this.query = this.$input.val();
|
|
587
|
+
this.$overflowHelper = buildOverflowHelper(this.$input);
|
|
597
588
|
}
|
|
598
589
|
utils.mixin(InputView.prototype, EventTarget, {
|
|
599
590
|
_handleFocus: function() {
|
|
600
|
-
this.trigger("
|
|
591
|
+
this.trigger("focused");
|
|
601
592
|
},
|
|
602
593
|
_handleBlur: function() {
|
|
603
|
-
this.trigger("
|
|
594
|
+
this.trigger("blured");
|
|
604
595
|
},
|
|
605
|
-
_handleSpecialKeyEvent: function(e) {
|
|
606
|
-
var
|
|
607
|
-
|
|
608
|
-
this.trigger(keyCode.event, e);
|
|
609
|
-
keyCode.preventDefault && e.preventDefault();
|
|
610
|
-
}
|
|
596
|
+
_handleSpecialKeyEvent: function($e) {
|
|
597
|
+
var keyName = this.specialKeyCodeMap[$e.which || $e.keyCode];
|
|
598
|
+
keyName && this.trigger(keyName + "Keyed", $e);
|
|
611
599
|
},
|
|
612
600
|
_compareQueryToInputValue: function() {
|
|
613
601
|
var inputValue = this.getInputValue(), isSameQuery = compareQueries(this.query, inputValue), isSameQueryExceptWhitespace = isSameQuery ? this.query.length !== inputValue.length : false;
|
|
614
602
|
if (isSameQueryExceptWhitespace) {
|
|
615
|
-
this.trigger("
|
|
603
|
+
this.trigger("whitespaceChanged", {
|
|
616
604
|
value: this.query
|
|
617
605
|
});
|
|
618
606
|
} else if (!isSameQuery) {
|
|
619
|
-
this.trigger("
|
|
607
|
+
this.trigger("queryChanged", {
|
|
620
608
|
value: this.query = inputValue
|
|
621
609
|
});
|
|
622
610
|
}
|
|
623
611
|
},
|
|
612
|
+
destroy: function() {
|
|
613
|
+
this.$hint.off(".tt");
|
|
614
|
+
this.$input.off(".tt");
|
|
615
|
+
this.$hint = this.$input = this.$overflowHelper = null;
|
|
616
|
+
},
|
|
624
617
|
focus: function() {
|
|
625
618
|
this.$input.focus();
|
|
626
619
|
},
|
|
627
620
|
blur: function() {
|
|
628
621
|
this.$input.blur();
|
|
629
622
|
},
|
|
630
|
-
setPreventDefaultValueForKey: function(key, value) {
|
|
631
|
-
this.specialKeyCodeMap[key].preventDefault = !!value;
|
|
632
|
-
},
|
|
633
623
|
getQuery: function() {
|
|
634
624
|
return this.query;
|
|
635
625
|
},
|
|
626
|
+
setQuery: function(query) {
|
|
627
|
+
this.query = query;
|
|
628
|
+
},
|
|
636
629
|
getInputValue: function() {
|
|
637
630
|
return this.$input.val();
|
|
638
631
|
},
|
|
639
632
|
setInputValue: function(value, silent) {
|
|
640
633
|
this.$input.val(value);
|
|
641
|
-
|
|
642
|
-
this._compareQueryToInputValue();
|
|
643
|
-
}
|
|
634
|
+
!silent && this._compareQueryToInputValue();
|
|
644
635
|
},
|
|
645
636
|
getHintValue: function() {
|
|
646
637
|
return this.$hint.val();
|
|
@@ -651,9 +642,13 @@
|
|
|
651
642
|
getLanguageDirection: function() {
|
|
652
643
|
return (this.$input.css("direction") || "ltr").toLowerCase();
|
|
653
644
|
},
|
|
645
|
+
isOverflow: function() {
|
|
646
|
+
this.$overflowHelper.text(this.getInputValue());
|
|
647
|
+
return this.$overflowHelper.width() > this.$input.width();
|
|
648
|
+
},
|
|
654
649
|
isCursorAtEnd: function() {
|
|
655
650
|
var valueLength = this.$input.val().length, selectionStart = this.$input[0].selectionStart, range;
|
|
656
|
-
if (selectionStart) {
|
|
651
|
+
if (utils.isNumber(selectionStart)) {
|
|
657
652
|
return selectionStart === valueLength;
|
|
658
653
|
} else if (document.selection) {
|
|
659
654
|
range = document.selection.createRange();
|
|
@@ -664,17 +659,51 @@
|
|
|
664
659
|
}
|
|
665
660
|
});
|
|
666
661
|
return InputView;
|
|
662
|
+
function buildOverflowHelper($input) {
|
|
663
|
+
return $("<span></span>").css({
|
|
664
|
+
position: "absolute",
|
|
665
|
+
left: "-9999px",
|
|
666
|
+
visibility: "hidden",
|
|
667
|
+
whiteSpace: "nowrap",
|
|
668
|
+
fontFamily: $input.css("font-family"),
|
|
669
|
+
fontSize: $input.css("font-size"),
|
|
670
|
+
fontStyle: $input.css("font-style"),
|
|
671
|
+
fontVariant: $input.css("font-variant"),
|
|
672
|
+
fontWeight: $input.css("font-weight"),
|
|
673
|
+
wordSpacing: $input.css("word-spacing"),
|
|
674
|
+
letterSpacing: $input.css("letter-spacing"),
|
|
675
|
+
textIndent: $input.css("text-indent"),
|
|
676
|
+
textRendering: $input.css("text-rendering"),
|
|
677
|
+
textTransform: $input.css("text-transform")
|
|
678
|
+
}).insertAfter($input);
|
|
679
|
+
}
|
|
667
680
|
function compareQueries(a, b) {
|
|
668
|
-
a = (a || "").replace(/^\s*/g, "").replace(/\s{2,}/g, " ")
|
|
669
|
-
b = (b || "").replace(/^\s*/g, "").replace(/\s{2,}/g, " ")
|
|
681
|
+
a = (a || "").replace(/^\s*/g, "").replace(/\s{2,}/g, " ");
|
|
682
|
+
b = (b || "").replace(/^\s*/g, "").replace(/\s{2,}/g, " ");
|
|
670
683
|
return a === b;
|
|
671
684
|
}
|
|
672
685
|
}();
|
|
673
686
|
var DropdownView = function() {
|
|
687
|
+
var html = {
|
|
688
|
+
suggestionsList: '<span class="tt-suggestions"></span>'
|
|
689
|
+
}, css = {
|
|
690
|
+
suggestionsList: {
|
|
691
|
+
display: "block"
|
|
692
|
+
},
|
|
693
|
+
suggestion: {
|
|
694
|
+
whiteSpace: "nowrap",
|
|
695
|
+
cursor: "pointer"
|
|
696
|
+
},
|
|
697
|
+
suggestionChild: {
|
|
698
|
+
whiteSpace: "normal"
|
|
699
|
+
}
|
|
700
|
+
};
|
|
674
701
|
function DropdownView(o) {
|
|
675
702
|
utils.bindAll(this);
|
|
676
|
-
this.
|
|
677
|
-
this
|
|
703
|
+
this.isOpen = false;
|
|
704
|
+
this.isEmpty = true;
|
|
705
|
+
this.isMouseOverDropdown = false;
|
|
706
|
+
this.$menu = $(o.menu).on("mouseenter.tt", this._handleMouseenter).on("mouseleave.tt", this._handleMouseleave).on("click.tt", ".tt-suggestion", this._handleSelection).on("mouseover.tt", ".tt-suggestion", this._handleMouseover);
|
|
678
707
|
}
|
|
679
708
|
utils.mixin(DropdownView.prototype, EventTarget, {
|
|
680
709
|
_handleMouseenter: function() {
|
|
@@ -683,16 +712,24 @@
|
|
|
683
712
|
_handleMouseleave: function() {
|
|
684
713
|
this.isMouseOverDropdown = false;
|
|
685
714
|
},
|
|
686
|
-
_handleMouseover: function(e) {
|
|
715
|
+
_handleMouseover: function($e) {
|
|
716
|
+
var $suggestion = $($e.currentTarget);
|
|
687
717
|
this._getSuggestions().removeClass("tt-is-under-cursor");
|
|
688
|
-
$
|
|
718
|
+
$suggestion.addClass("tt-is-under-cursor");
|
|
689
719
|
},
|
|
690
|
-
_handleSelection: function(e) {
|
|
691
|
-
|
|
720
|
+
_handleSelection: function($e) {
|
|
721
|
+
var $suggestion = $($e.currentTarget);
|
|
722
|
+
this.trigger("suggestionSelected", extractSuggestion($suggestion));
|
|
723
|
+
},
|
|
724
|
+
_show: function() {
|
|
725
|
+
this.$menu.css("display", "block");
|
|
726
|
+
},
|
|
727
|
+
_hide: function() {
|
|
728
|
+
this.$menu.hide();
|
|
692
729
|
},
|
|
693
730
|
_moveCursor: function(increment) {
|
|
694
731
|
var $suggestions, $cur, nextIndex, $underCursor;
|
|
695
|
-
if (!this
|
|
732
|
+
if (!this.isVisible()) {
|
|
696
733
|
return;
|
|
697
734
|
}
|
|
698
735
|
$suggestions = this._getSuggestions();
|
|
@@ -701,38 +738,53 @@
|
|
|
701
738
|
nextIndex = $suggestions.index($cur) + increment;
|
|
702
739
|
nextIndex = (nextIndex + 1) % ($suggestions.length + 1) - 1;
|
|
703
740
|
if (nextIndex === -1) {
|
|
704
|
-
this.trigger("
|
|
741
|
+
this.trigger("cursorRemoved");
|
|
705
742
|
return;
|
|
706
743
|
} else if (nextIndex < -1) {
|
|
707
744
|
nextIndex = $suggestions.length - 1;
|
|
708
745
|
}
|
|
709
746
|
$underCursor = $suggestions.eq(nextIndex).addClass("tt-is-under-cursor");
|
|
710
|
-
this.trigger("
|
|
711
|
-
value: $underCursor.data("value")
|
|
712
|
-
});
|
|
747
|
+
this.trigger("cursorMoved", extractSuggestion($underCursor));
|
|
713
748
|
},
|
|
714
749
|
_getSuggestions: function() {
|
|
715
750
|
return this.$menu.find(".tt-suggestions > .tt-suggestion");
|
|
716
751
|
},
|
|
717
|
-
|
|
752
|
+
destroy: function() {
|
|
753
|
+
this.$menu.off(".tt");
|
|
754
|
+
this.$menu = null;
|
|
755
|
+
},
|
|
756
|
+
isVisible: function() {
|
|
757
|
+
return this.isOpen && !this.isEmpty;
|
|
758
|
+
},
|
|
759
|
+
closeUnlessMouseIsOverDropdown: function() {
|
|
718
760
|
if (!this.isMouseOverDropdown) {
|
|
719
|
-
this.
|
|
761
|
+
this.close();
|
|
720
762
|
}
|
|
721
763
|
},
|
|
722
|
-
|
|
723
|
-
if (this
|
|
724
|
-
this
|
|
725
|
-
this.
|
|
764
|
+
close: function() {
|
|
765
|
+
if (this.isOpen) {
|
|
766
|
+
this.isOpen = false;
|
|
767
|
+
this._hide();
|
|
768
|
+
this.$menu.find(".tt-suggestions > .tt-suggestion").removeClass("tt-is-under-cursor");
|
|
769
|
+
this.trigger("closed");
|
|
726
770
|
}
|
|
727
771
|
},
|
|
728
|
-
|
|
729
|
-
if (!this
|
|
730
|
-
this
|
|
731
|
-
this.
|
|
772
|
+
open: function() {
|
|
773
|
+
if (!this.isOpen) {
|
|
774
|
+
this.isOpen = true;
|
|
775
|
+
!this.isEmpty && this._show();
|
|
776
|
+
this.trigger("opened");
|
|
732
777
|
}
|
|
733
778
|
},
|
|
734
|
-
|
|
735
|
-
|
|
779
|
+
setLanguageDirection: function(dir) {
|
|
780
|
+
var ltrCss = {
|
|
781
|
+
left: "0",
|
|
782
|
+
right: "auto"
|
|
783
|
+
}, rtlCss = {
|
|
784
|
+
left: "auto",
|
|
785
|
+
right: " 0"
|
|
786
|
+
};
|
|
787
|
+
dir === "ltr" ? this.$menu.css(ltrCss) : this.$menu.css(rtlCss);
|
|
736
788
|
},
|
|
737
789
|
moveCursorUp: function() {
|
|
738
790
|
this._moveCursor(-1);
|
|
@@ -742,100 +794,147 @@
|
|
|
742
794
|
},
|
|
743
795
|
getSuggestionUnderCursor: function() {
|
|
744
796
|
var $suggestion = this._getSuggestions().filter(".tt-is-under-cursor").first();
|
|
745
|
-
return $suggestion.length > 0 ?
|
|
797
|
+
return $suggestion.length > 0 ? extractSuggestion($suggestion) : null;
|
|
746
798
|
},
|
|
747
799
|
getFirstSuggestion: function() {
|
|
748
800
|
var $suggestion = this._getSuggestions().first();
|
|
749
|
-
return $suggestion.length > 0 ?
|
|
801
|
+
return $suggestion.length > 0 ? extractSuggestion($suggestion) : null;
|
|
750
802
|
},
|
|
751
|
-
renderSuggestions: function(
|
|
752
|
-
var datasetClassName = "tt-dataset-" + dataset.name, $dataset = this.$menu.find("." + datasetClassName), elBuilder, fragment, el;
|
|
803
|
+
renderSuggestions: function(dataset, suggestions) {
|
|
804
|
+
var datasetClassName = "tt-dataset-" + dataset.name, wrapper = '<div class="tt-suggestion">%body</div>', compiledHtml, $suggestionsList, $dataset = this.$menu.find("." + datasetClassName), elBuilder, fragment, $el;
|
|
753
805
|
if ($dataset.length === 0) {
|
|
754
|
-
$
|
|
806
|
+
$suggestionsList = $(html.suggestionsList).css(css.suggestionsList);
|
|
807
|
+
$dataset = $("<div></div>").addClass(datasetClassName).append(dataset.header).append($suggestionsList).append(dataset.footer).appendTo(this.$menu);
|
|
755
808
|
}
|
|
756
|
-
elBuilder = document.createElement("div");
|
|
757
|
-
fragment = document.createDocumentFragment();
|
|
758
|
-
this.clearSuggestions(dataset.name);
|
|
759
809
|
if (suggestions.length > 0) {
|
|
760
|
-
this
|
|
810
|
+
this.isEmpty = false;
|
|
811
|
+
this.isOpen && this._show();
|
|
812
|
+
elBuilder = document.createElement("div");
|
|
813
|
+
fragment = document.createDocumentFragment();
|
|
761
814
|
utils.each(suggestions, function(i, suggestion) {
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
el.
|
|
765
|
-
|
|
815
|
+
compiledHtml = dataset.template(suggestion.datum);
|
|
816
|
+
elBuilder.innerHTML = wrapper.replace("%body", compiledHtml);
|
|
817
|
+
$el = $(elBuilder.firstChild).css(css.suggestion).data("suggestion", suggestion);
|
|
818
|
+
$el.children().each(function() {
|
|
819
|
+
$(this).css(css.suggestionChild);
|
|
820
|
+
});
|
|
821
|
+
fragment.appendChild($el[0]);
|
|
766
822
|
});
|
|
823
|
+
$dataset.show().find(".tt-suggestions").html(fragment);
|
|
824
|
+
} else {
|
|
825
|
+
this.clearSuggestions(dataset.name);
|
|
767
826
|
}
|
|
768
|
-
|
|
769
|
-
query: query,
|
|
770
|
-
dataset: dataset.name
|
|
771
|
-
}).append(fragment);
|
|
772
|
-
this.trigger("suggestionsRender");
|
|
827
|
+
this.trigger("suggestionsRendered");
|
|
773
828
|
},
|
|
774
829
|
clearSuggestions: function(datasetName) {
|
|
775
|
-
var $
|
|
830
|
+
var $datasets = datasetName ? this.$menu.find(".tt-dataset-" + datasetName) : this.$menu.find('[class^="tt-dataset-"]'), $suggestions = $datasets.find(".tt-suggestions");
|
|
831
|
+
$datasets.hide();
|
|
776
832
|
$suggestions.empty();
|
|
777
|
-
this._getSuggestions().length === 0
|
|
833
|
+
if (this._getSuggestions().length === 0) {
|
|
834
|
+
this.isEmpty = true;
|
|
835
|
+
this._hide();
|
|
836
|
+
}
|
|
778
837
|
}
|
|
779
838
|
});
|
|
780
839
|
return DropdownView;
|
|
781
|
-
function
|
|
782
|
-
|
|
783
|
-
return {
|
|
784
|
-
value: $suggestion.data("value"),
|
|
785
|
-
query: $suggestions.data("query"),
|
|
786
|
-
dataset: $suggestions.data("dataset")
|
|
787
|
-
};
|
|
840
|
+
function extractSuggestion($el) {
|
|
841
|
+
return $el.data("suggestion");
|
|
788
842
|
}
|
|
789
843
|
}();
|
|
790
844
|
var TypeaheadView = function() {
|
|
791
845
|
var html = {
|
|
792
846
|
wrapper: '<span class="twitter-typeahead"></span>',
|
|
793
|
-
hint: '<input class="tt-hint" type="text" autocomplete="
|
|
794
|
-
dropdown: '<
|
|
847
|
+
hint: '<input class="tt-hint" type="text" autocomplete="off" spellcheck="off" disabled>',
|
|
848
|
+
dropdown: '<span class="tt-dropdown-menu"></span>'
|
|
849
|
+
}, css = {
|
|
850
|
+
wrapper: {
|
|
851
|
+
position: "relative",
|
|
852
|
+
display: "inline-block"
|
|
853
|
+
},
|
|
854
|
+
hint: {
|
|
855
|
+
position: "absolute",
|
|
856
|
+
top: "0",
|
|
857
|
+
left: "0",
|
|
858
|
+
borderColor: "transparent",
|
|
859
|
+
boxShadow: "none"
|
|
860
|
+
},
|
|
861
|
+
query: {
|
|
862
|
+
position: "relative",
|
|
863
|
+
verticalAlign: "top",
|
|
864
|
+
backgroundColor: "transparent"
|
|
865
|
+
},
|
|
866
|
+
dropdown: {
|
|
867
|
+
position: "absolute",
|
|
868
|
+
top: "100%",
|
|
869
|
+
left: "0",
|
|
870
|
+
zIndex: "100",
|
|
871
|
+
display: "none"
|
|
872
|
+
}
|
|
795
873
|
};
|
|
874
|
+
if (utils.isMsie()) {
|
|
875
|
+
utils.mixin(css.query, {
|
|
876
|
+
backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
if (utils.isMsie() && utils.isMsie() <= 7) {
|
|
880
|
+
utils.mixin(css.wrapper, {
|
|
881
|
+
display: "inline",
|
|
882
|
+
zoom: "1"
|
|
883
|
+
});
|
|
884
|
+
utils.mixin(css.query, {
|
|
885
|
+
marginTop: "-1px"
|
|
886
|
+
});
|
|
887
|
+
}
|
|
796
888
|
function TypeaheadView(o) {
|
|
889
|
+
var $menu, $input, $hint;
|
|
797
890
|
utils.bindAll(this);
|
|
798
|
-
this.$node =
|
|
891
|
+
this.$node = buildDomStructure(o.input);
|
|
799
892
|
this.datasets = o.datasets;
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
dataset.template = {
|
|
806
|
-
render: function(context) {
|
|
807
|
-
return parentTemplate.replace("%body", "<p>" + context.value + "</p>");
|
|
808
|
-
}
|
|
809
|
-
};
|
|
810
|
-
}
|
|
811
|
-
});
|
|
812
|
-
this.inputView = new InputView({
|
|
813
|
-
input: this.$node.find(".tt-query"),
|
|
814
|
-
hint: this.$node.find(".tt-hint")
|
|
815
|
-
});
|
|
893
|
+
this.dir = null;
|
|
894
|
+
this.eventBus = o.eventBus;
|
|
895
|
+
$menu = this.$node.find(".tt-dropdown-menu");
|
|
896
|
+
$input = this.$node.find(".tt-query");
|
|
897
|
+
$hint = this.$node.find(".tt-hint");
|
|
816
898
|
this.dropdownView = new DropdownView({
|
|
817
|
-
menu:
|
|
818
|
-
});
|
|
819
|
-
this.
|
|
820
|
-
|
|
899
|
+
menu: $menu
|
|
900
|
+
}).on("suggestionSelected", this._handleSelection).on("cursorMoved", this._clearHint).on("cursorMoved", this._setInputValueToSuggestionUnderCursor).on("cursorRemoved", this._setInputValueToQuery).on("cursorRemoved", this._updateHint).on("suggestionsRendered", this._updateHint).on("opened", this._updateHint).on("closed", this._clearHint).on("opened closed", this._propagateEvent);
|
|
901
|
+
this.inputView = new InputView({
|
|
902
|
+
input: $input,
|
|
903
|
+
hint: $hint
|
|
904
|
+
}).on("focused", this._openDropdown).on("blured", this._closeDropdown).on("blured", this._setInputValueToQuery).on("enterKeyed", this._handleSelection).on("queryChanged", this._clearHint).on("queryChanged", this._clearSuggestions).on("queryChanged", this._getSuggestions).on("whitespaceChanged", this._updateHint).on("queryChanged whitespaceChanged", this._openDropdown).on("queryChanged whitespaceChanged", this._setLanguageDirection).on("escKeyed", this._closeDropdown).on("escKeyed", this._setInputValueToQuery).on("tabKeyed upKeyed downKeyed", this._managePreventDefault).on("upKeyed downKeyed", this._moveDropdownCursor).on("upKeyed downKeyed", this._openDropdown).on("tabKeyed leftKeyed rightKeyed", this._autocomplete);
|
|
821
905
|
}
|
|
822
906
|
utils.mixin(TypeaheadView.prototype, EventTarget, {
|
|
823
|
-
|
|
824
|
-
var
|
|
825
|
-
|
|
907
|
+
_managePreventDefault: function(e) {
|
|
908
|
+
var $e = e.data, hint, inputValue, preventDefault = false;
|
|
909
|
+
switch (e.type) {
|
|
910
|
+
case "tabKeyed":
|
|
911
|
+
hint = this.inputView.getHintValue();
|
|
912
|
+
inputValue = this.inputView.getInputValue();
|
|
913
|
+
preventDefault = hint && hint !== inputValue;
|
|
914
|
+
break;
|
|
915
|
+
|
|
916
|
+
case "upKeyed":
|
|
917
|
+
case "downKeyed":
|
|
918
|
+
preventDefault = !$e.shiftKey && !$e.ctrlKey && !$e.metaKey;
|
|
919
|
+
break;
|
|
920
|
+
}
|
|
921
|
+
preventDefault && $e.preventDefault();
|
|
826
922
|
},
|
|
827
923
|
_setLanguageDirection: function() {
|
|
828
|
-
var
|
|
829
|
-
if (
|
|
830
|
-
this
|
|
924
|
+
var dir = this.inputView.getLanguageDirection();
|
|
925
|
+
if (dir !== this.dir) {
|
|
926
|
+
this.dir = dir;
|
|
927
|
+
this.$node.css("direction", dir);
|
|
928
|
+
this.dropdownView.setLanguageDirection(dir);
|
|
831
929
|
}
|
|
832
930
|
},
|
|
833
931
|
_updateHint: function() {
|
|
834
|
-
var
|
|
835
|
-
if (hint &&
|
|
932
|
+
var suggestion = this.dropdownView.getFirstSuggestion(), hint = suggestion ? suggestion.value : null, dropdownIsVisible = this.dropdownView.isVisible(), inputHasOverflow = this.inputView.isOverflow(), inputValue, query, escapedQuery, beginsWithQuery, match;
|
|
933
|
+
if (hint && dropdownIsVisible && !inputHasOverflow) {
|
|
836
934
|
inputValue = this.inputView.getInputValue();
|
|
837
935
|
query = inputValue.replace(/\s{2,}/g, " ").replace(/^\s+/g, "");
|
|
838
|
-
|
|
936
|
+
escapedQuery = utils.escapeRegExChars(query);
|
|
937
|
+
beginsWithQuery = new RegExp("^(?:" + escapedQuery + ")(.*$)", "i");
|
|
839
938
|
match = beginsWithQuery.exec(hint);
|
|
840
939
|
this.inputView.setHintValue(inputValue + (match ? match[1] : ""));
|
|
841
940
|
}
|
|
@@ -850,45 +949,48 @@
|
|
|
850
949
|
this.inputView.setInputValue(this.inputView.getQuery());
|
|
851
950
|
},
|
|
852
951
|
_setInputValueToSuggestionUnderCursor: function(e) {
|
|
853
|
-
|
|
952
|
+
var suggestion = e.data;
|
|
953
|
+
this.inputView.setInputValue(suggestion.value, true);
|
|
854
954
|
},
|
|
855
|
-
|
|
856
|
-
this.dropdownView.
|
|
955
|
+
_openDropdown: function() {
|
|
956
|
+
this.dropdownView.open();
|
|
857
957
|
},
|
|
858
|
-
|
|
859
|
-
this.dropdownView[e.type === "
|
|
958
|
+
_closeDropdown: function(e) {
|
|
959
|
+
this.dropdownView[e.type === "blured" ? "closeUnlessMouseIsOverDropdown" : "close"]();
|
|
860
960
|
},
|
|
861
961
|
_moveDropdownCursor: function(e) {
|
|
862
|
-
|
|
962
|
+
var $e = e.data;
|
|
963
|
+
if (!$e.shiftKey && !$e.ctrlKey && !$e.metaKey) {
|
|
964
|
+
this.dropdownView[e.type === "upKeyed" ? "moveCursorUp" : "moveCursorDown"]();
|
|
965
|
+
}
|
|
863
966
|
},
|
|
864
967
|
_handleSelection: function(e) {
|
|
865
|
-
var byClick = e.type === "
|
|
866
|
-
if (
|
|
867
|
-
this.inputView.setInputValue(
|
|
968
|
+
var byClick = e.type === "suggestionSelected", suggestion = byClick ? e.data : this.dropdownView.getSuggestionUnderCursor();
|
|
969
|
+
if (suggestion) {
|
|
970
|
+
this.inputView.setInputValue(suggestion.value);
|
|
868
971
|
byClick ? this.inputView.focus() : e.data.preventDefault();
|
|
869
|
-
byClick && utils.isMsie() ?
|
|
972
|
+
byClick && utils.isMsie() ? utils.defer(this.dropdownView.close) : this.dropdownView.close();
|
|
973
|
+
this.eventBus.trigger("selected", suggestion.datum);
|
|
870
974
|
}
|
|
871
975
|
},
|
|
872
976
|
_getSuggestions: function() {
|
|
873
977
|
var that = this, query = this.inputView.getQuery();
|
|
978
|
+
if (utils.isBlankString(query)) {
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
874
981
|
utils.each(this.datasets, function(i, dataset) {
|
|
875
982
|
dataset.getSuggestions(query, function(suggestions) {
|
|
876
|
-
|
|
983
|
+
if (query === that.inputView.getQuery()) {
|
|
984
|
+
that.dropdownView.renderSuggestions(dataset, suggestions);
|
|
985
|
+
}
|
|
877
986
|
});
|
|
878
987
|
});
|
|
879
988
|
},
|
|
880
|
-
_renderSuggestions: function(query, dataset, suggestions) {
|
|
881
|
-
if (query !== this.inputView.getQuery()) {
|
|
882
|
-
return;
|
|
883
|
-
}
|
|
884
|
-
suggestions = suggestions.slice(0, dataset.limit);
|
|
885
|
-
this.dropdownView.renderSuggestions(query, dataset, suggestions);
|
|
886
|
-
},
|
|
887
989
|
_autocomplete: function(e) {
|
|
888
|
-
var isCursorAtEnd, ignoreEvent, query, hint;
|
|
889
|
-
if (e.type === "
|
|
990
|
+
var isCursorAtEnd, ignoreEvent, query, hint, suggestion;
|
|
991
|
+
if (e.type === "rightKeyed" || e.type === "leftKeyed") {
|
|
890
992
|
isCursorAtEnd = this.inputView.isCursorAtEnd();
|
|
891
|
-
ignoreEvent = this.inputView.getLanguageDirection() === "ltr" ? e.type === "
|
|
993
|
+
ignoreEvent = this.inputView.getLanguageDirection() === "ltr" ? e.type === "leftKeyed" : e.type === "rightKeyed";
|
|
892
994
|
if (!isCursorAtEnd || ignoreEvent) {
|
|
893
995
|
return;
|
|
894
996
|
}
|
|
@@ -896,89 +998,129 @@
|
|
|
896
998
|
query = this.inputView.getQuery();
|
|
897
999
|
hint = this.inputView.getHintValue();
|
|
898
1000
|
if (hint !== "" && query !== hint) {
|
|
899
|
-
this.
|
|
1001
|
+
suggestion = this.dropdownView.getFirstSuggestion();
|
|
1002
|
+
this.inputView.setInputValue(suggestion.value);
|
|
1003
|
+
this.eventBus.trigger("autocompleted", suggestion.datum);
|
|
900
1004
|
}
|
|
1005
|
+
},
|
|
1006
|
+
_propagateEvent: function(e) {
|
|
1007
|
+
this.eventBus.trigger(e.type);
|
|
1008
|
+
},
|
|
1009
|
+
destroy: function() {
|
|
1010
|
+
this.inputView.destroy();
|
|
1011
|
+
this.dropdownView.destroy();
|
|
1012
|
+
destroyDomStructure(this.$node);
|
|
1013
|
+
this.$node = null;
|
|
1014
|
+
},
|
|
1015
|
+
setQuery: function(query) {
|
|
1016
|
+
this.inputView.setQuery(query);
|
|
1017
|
+
this.inputView.setInputValue(query);
|
|
1018
|
+
this._clearHint();
|
|
1019
|
+
this._clearSuggestions();
|
|
1020
|
+
this._getSuggestions();
|
|
901
1021
|
}
|
|
902
1022
|
});
|
|
903
1023
|
return TypeaheadView;
|
|
904
|
-
function
|
|
905
|
-
var $input = $(input), $hint = $(html.hint)
|
|
906
|
-
|
|
1024
|
+
function buildDomStructure(input) {
|
|
1025
|
+
var $wrapper = $(html.wrapper), $dropdown = $(html.dropdown), $input = $(input), $hint = $(html.hint);
|
|
1026
|
+
$wrapper = $wrapper.css(css.wrapper);
|
|
1027
|
+
$dropdown = $dropdown.css(css.dropdown);
|
|
1028
|
+
$hint.css(css.hint).css({
|
|
1029
|
+
backgroundAttachment: $input.css("background-attachment"),
|
|
1030
|
+
backgroundClip: $input.css("background-clip"),
|
|
1031
|
+
backgroundColor: $input.css("background-color"),
|
|
1032
|
+
backgroundImage: $input.css("background-image"),
|
|
1033
|
+
backgroundOrigin: $input.css("background-origin"),
|
|
1034
|
+
backgroundPosition: $input.css("background-position"),
|
|
1035
|
+
backgroundRepeat: $input.css("background-repeat"),
|
|
1036
|
+
backgroundSize: $input.css("background-size")
|
|
907
1037
|
});
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1038
|
+
$input.data("ttAttrs", {
|
|
1039
|
+
dir: $input.attr("dir"),
|
|
1040
|
+
autocomplete: $input.attr("autocomplete"),
|
|
1041
|
+
spellcheck: $input.attr("spellcheck"),
|
|
1042
|
+
style: $input.attr("style")
|
|
1043
|
+
});
|
|
1044
|
+
$input.addClass("tt-query").attr({
|
|
1045
|
+
autocomplete: "off",
|
|
1046
|
+
spellcheck: false
|
|
1047
|
+
}).css(css.query);
|
|
911
1048
|
try {
|
|
912
1049
|
!$input.attr("dir") && $input.attr("dir", "auto");
|
|
913
1050
|
} catch (e) {}
|
|
914
|
-
return $input.
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
1051
|
+
return $input.wrap($wrapper).parent().prepend($hint).append($dropdown);
|
|
1052
|
+
}
|
|
1053
|
+
function destroyDomStructure($node) {
|
|
1054
|
+
var $input = $node.find(".tt-query");
|
|
1055
|
+
utils.each($input.data("ttAttrs"), function(key, val) {
|
|
1056
|
+
utils.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val);
|
|
1057
|
+
});
|
|
1058
|
+
$input.detach().removeData("ttAttrs").removeClass("tt-query").insertAfter($node);
|
|
1059
|
+
$node.remove();
|
|
918
1060
|
}
|
|
919
1061
|
}();
|
|
920
1062
|
(function() {
|
|
921
|
-
var
|
|
922
|
-
jQuery.fn.typeahead = typeahead;
|
|
923
|
-
typeahead.configureTransport = configureTransport;
|
|
1063
|
+
var cache = {}, viewKey = "ttView", methods;
|
|
924
1064
|
methods = {
|
|
925
1065
|
initialize: function(datasetDefs) {
|
|
926
|
-
var datasets
|
|
1066
|
+
var datasets;
|
|
927
1067
|
datasetDefs = utils.isArray(datasetDefs) ? datasetDefs : [ datasetDefs ];
|
|
928
1068
|
if (datasetDefs.length === 0) {
|
|
929
|
-
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
if (initializedDatasets[name]) {
|
|
936
|
-
dataset = initializedDatasets[name];
|
|
937
|
-
} else {
|
|
938
|
-
datasetDef.limit = datasetDef.limit || 5;
|
|
939
|
-
datasetDef.template = datasetDef.template;
|
|
940
|
-
datasetDef.engine = datasetDef.engine;
|
|
941
|
-
if (datasetDef.template && !datasetDef.engine) {
|
|
942
|
-
throw new Error("no template engine specified for " + name);
|
|
943
|
-
}
|
|
944
|
-
dataset = initializedDatasets[name] = new Dataset({
|
|
945
|
-
name: datasetDef.name,
|
|
946
|
-
limit: datasetDef.limit,
|
|
947
|
-
local: datasetDef.local,
|
|
948
|
-
prefetch: datasetDef.prefetch,
|
|
949
|
-
remote: datasetDef.remote,
|
|
950
|
-
matcher: datasetDef.matcher,
|
|
951
|
-
ranker: datasetDef.ranker,
|
|
952
|
-
transport: transport
|
|
953
|
-
});
|
|
1069
|
+
$.error("no datasets provided");
|
|
1070
|
+
}
|
|
1071
|
+
datasets = utils.map(datasetDefs, function(o) {
|
|
1072
|
+
var dataset = cache[o.name] ? cache[o.name] : new Dataset(o);
|
|
1073
|
+
if (o.name) {
|
|
1074
|
+
cache[o.name] = dataset;
|
|
954
1075
|
}
|
|
955
|
-
|
|
956
|
-
name: datasetDef.name,
|
|
957
|
-
limit: datasetDef.limit,
|
|
958
|
-
template: datasetDef.template,
|
|
959
|
-
engine: datasetDef.engine,
|
|
960
|
-
getSuggestions: dataset.getSuggestions
|
|
961
|
-
};
|
|
1076
|
+
return dataset;
|
|
962
1077
|
});
|
|
963
|
-
return this.each(
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
datasets: datasets
|
|
968
|
-
})
|
|
1078
|
+
return this.each(initialize);
|
|
1079
|
+
function initialize() {
|
|
1080
|
+
var $input = $(this), deferreds, eventBus = new EventBus({
|
|
1081
|
+
el: $input
|
|
969
1082
|
});
|
|
970
|
-
|
|
1083
|
+
deferreds = utils.map(datasets, function(dataset) {
|
|
1084
|
+
return dataset.initialize();
|
|
1085
|
+
});
|
|
1086
|
+
$input.data(viewKey, new TypeaheadView({
|
|
1087
|
+
input: $input,
|
|
1088
|
+
eventBus: eventBus = new EventBus({
|
|
1089
|
+
el: $input
|
|
1090
|
+
}),
|
|
1091
|
+
datasets: datasets
|
|
1092
|
+
}));
|
|
1093
|
+
$.when.apply($, deferreds).always(function() {
|
|
1094
|
+
utils.defer(function() {
|
|
1095
|
+
eventBus.trigger("initialized");
|
|
1096
|
+
});
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
},
|
|
1100
|
+
destroy: function() {
|
|
1101
|
+
return this.each(destroy);
|
|
1102
|
+
function destroy() {
|
|
1103
|
+
var $this = $(this), view = $this.data(viewKey);
|
|
1104
|
+
if (view) {
|
|
1105
|
+
view.destroy();
|
|
1106
|
+
$this.removeData(viewKey);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
},
|
|
1110
|
+
setQuery: function(query) {
|
|
1111
|
+
return this.each(setQuery);
|
|
1112
|
+
function setQuery() {
|
|
1113
|
+
var view = $(this).data(viewKey);
|
|
1114
|
+
view && view.setQuery(query);
|
|
1115
|
+
}
|
|
971
1116
|
}
|
|
972
1117
|
};
|
|
973
|
-
function
|
|
1118
|
+
jQuery.fn.typeahead = function(method) {
|
|
974
1119
|
if (methods[method]) {
|
|
975
|
-
methods[method].apply(this, [].slice.call(arguments, 1));
|
|
1120
|
+
return methods[method].apply(this, [].slice.call(arguments, 1));
|
|
976
1121
|
} else {
|
|
977
|
-
methods.initialize.apply(this, arguments);
|
|
1122
|
+
return methods.initialize.apply(this, arguments);
|
|
978
1123
|
}
|
|
979
|
-
}
|
|
980
|
-
function configureTransport(o) {
|
|
981
|
-
transportOptions = o;
|
|
982
|
-
}
|
|
1124
|
+
};
|
|
983
1125
|
})();
|
|
984
|
-
})();
|
|
1126
|
+
})(window.jQuery);
|