bootstrap-typeahead-rails 0.9.3.3 → 0.10.5.1
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.
- checksums.yaml +4 -4
- data/Rakefile +2 -11
- data/lib/bootstrap-typeahead-rails/version.rb +1 -1
- data/readme.markdown +6 -2
- data/vendor/assets/javascripts/bootstrap-typeahead-rails/bootstrap-typeahead.js +1449 -806
- data/vendor/assets/stylesheets/bootstrap-typeahead-rails/bootstrap-typeahead.css +4 -3
- metadata +16 -17
- data/.rvmrc +0 -62
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13c19659a56eeab8bdb618e57426cc48cb9e1177
|
4
|
+
data.tar.gz: 7a3814013d8c5804ee5ed2fb4e3b3148a1338f7f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4baa5743e42a04e0f3d29e32e44744abbdd1856bb25ec7a7f87f43c929390e0950b1fb7a31d4b3c7eb178b4276b907f097ffc94b5ba8c80305ccf2b0b4dd6a4e
|
7
|
+
data.tar.gz: dbba902ac180d3f15cb4ca14c8b8231b5d544fdf9fc9060b8d5816a00deeaa02ccb6d58d2f58c5c8ece9d4608170f04d9b2108ce4dbd59ab93209cf4b3bcf5dd
|
data/Rakefile
CHANGED
@@ -11,21 +11,12 @@ task :update do
|
|
11
11
|
system("git clone git@github.com:twitter/typeahead.js.git bootstrap-typeahead-src")
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
system("cd bootstrap-typeahead-css-src && git pull && cd ..")
|
16
|
-
else
|
17
|
-
system("git clone git@github.com:jharding/typeahead.js-bootstrap.css.git bootstrap-typeahead-css-src")
|
18
|
-
end
|
19
|
-
|
20
|
-
system("cp bootstrap-typeahead-src/dist/typeahead.js vendor/assets/javascripts/bootstrap-typeahead-rails/bootstrap-typeahead.js")
|
21
|
-
|
22
|
-
system("cp bootstrap-typeahead-css-src/typeahead.js-bootstrap.css vendor/assets/stylesheets/bootstrap-typeahead-rails/bootstrap-typeahead.css")
|
23
|
-
# system("cp bootstrap-typeahead-css-src/typeahead.js-bootstrap.less vendor/assets/stylesheets/bootstrap-typeahead-rails/bootstrap-typeahead.less")
|
14
|
+
system("cp bootstrap-typeahead-src/dist/typeahead.bundle.js vendor/assets/javascripts/bootstrap-typeahead-rails/bootstrap-typeahead.js")
|
24
15
|
|
25
16
|
system("git status")
|
26
17
|
|
27
18
|
puts "\n"
|
28
|
-
puts "bootstrap-typeahead version: #{JSON.parse(File.read('./bootstrap-typeahead-src/
|
19
|
+
puts "bootstrap-typeahead version: #{JSON.parse(File.read('./bootstrap-typeahead-src/bower.json'))['version']}"
|
29
20
|
puts "bootstrap-typeahead-rails version: #{BootstrapTypeaheadRails::Rails::VERSION}"
|
30
21
|
end
|
31
22
|
|
data/readme.markdown
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Bootstrap Typeahead for Rails
|
2
2
|
|
3
|
+
[](http://badge.fury.io/rb/bootstrap-typeahead-rails)
|
4
|
+
|
5
|
+
[](https://coderwall.com/nerian)
|
6
|
+
|
3
7
|
bootstrap-typeahead-rails project integrates the official typeahead plugin with Rails 3 assets pipeline.
|
4
8
|
|
5
9
|
http://github.com/Nerian/bootstrap-typeahead-rails
|
@@ -24,13 +28,13 @@ and run bundle install.
|
|
24
28
|
|
25
29
|
## Configuration
|
26
30
|
|
27
|
-
Add this line to app/assets/javascripts/application.js
|
31
|
+
Add this line to `app/assets/javascripts/application.js`
|
28
32
|
|
29
33
|
``` javascript
|
30
34
|
//= require bootstrap-typeahead-rails
|
31
35
|
```
|
32
36
|
|
33
|
-
The official Typeahead do not include any styling for it. Nonetheless, you can add this line to app/assets/stylesheets/application.css and you will get a nice one. Or don't, and implement your own – instructions on https://github.com/twitter/typeahead.js/#look-and-feel.
|
37
|
+
The official Typeahead do not include any styling for it. Nonetheless, you can add this line to `app/assets/stylesheets/application.css` and you will get a nice one. Or don't, and implement your own – instructions on https://github.com/twitter/typeahead.js/#look-and-feel.
|
34
38
|
|
35
39
|
``` javascript
|
36
40
|
*= require bootstrap-typeahead-rails
|
@@ -1,192 +1,232 @@
|
|
1
1
|
/*!
|
2
|
-
* typeahead.js 0.
|
3
|
-
* https://github.com/twitter/typeahead
|
4
|
-
* Copyright 2013 Twitter, Inc. and other contributors; Licensed MIT
|
2
|
+
* typeahead.js 0.10.5
|
3
|
+
* https://github.com/twitter/typeahead.js
|
4
|
+
* Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT
|
5
5
|
*/
|
6
6
|
|
7
7
|
(function($) {
|
8
|
-
var
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
for (var i = 0; i < haystack.length; i++) {
|
41
|
-
if (haystack[i] === needle) {
|
42
|
-
return i;
|
8
|
+
var _ = function() {
|
9
|
+
"use strict";
|
10
|
+
return {
|
11
|
+
isMsie: function() {
|
12
|
+
return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false;
|
13
|
+
},
|
14
|
+
isBlankString: function(str) {
|
15
|
+
return !str || /^\s*$/.test(str);
|
16
|
+
},
|
17
|
+
escapeRegExChars: function(str) {
|
18
|
+
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
19
|
+
},
|
20
|
+
isString: function(obj) {
|
21
|
+
return typeof obj === "string";
|
22
|
+
},
|
23
|
+
isNumber: function(obj) {
|
24
|
+
return typeof obj === "number";
|
25
|
+
},
|
26
|
+
isArray: $.isArray,
|
27
|
+
isFunction: $.isFunction,
|
28
|
+
isObject: $.isPlainObject,
|
29
|
+
isUndefined: function(obj) {
|
30
|
+
return typeof obj === "undefined";
|
31
|
+
},
|
32
|
+
toStr: function toStr(s) {
|
33
|
+
return _.isUndefined(s) || s === null ? "" : s + "";
|
34
|
+
},
|
35
|
+
bind: $.proxy,
|
36
|
+
each: function(collection, cb) {
|
37
|
+
$.each(collection, reverseArgs);
|
38
|
+
function reverseArgs(index, value) {
|
39
|
+
return cb(value, index);
|
43
40
|
}
|
44
|
-
}
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
var result = true;
|
52
|
-
if (!obj) {
|
53
|
-
return result;
|
54
|
-
}
|
55
|
-
$.each(obj, function(key, val) {
|
56
|
-
if (!(result = test.call(null, val, key, obj))) {
|
57
|
-
return false;
|
41
|
+
},
|
42
|
+
map: $.map,
|
43
|
+
filter: $.grep,
|
44
|
+
every: function(obj, test) {
|
45
|
+
var result = true;
|
46
|
+
if (!obj) {
|
47
|
+
return result;
|
58
48
|
}
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
if (
|
69
|
-
return
|
49
|
+
$.each(obj, function(key, val) {
|
50
|
+
if (!(result = test.call(null, val, key, obj))) {
|
51
|
+
return false;
|
52
|
+
}
|
53
|
+
});
|
54
|
+
return !!result;
|
55
|
+
},
|
56
|
+
some: function(obj, test) {
|
57
|
+
var result = false;
|
58
|
+
if (!obj) {
|
59
|
+
return result;
|
70
60
|
}
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
mixin: $.extend,
|
75
|
-
getUniqueId: function() {
|
76
|
-
var counter = 0;
|
77
|
-
return function() {
|
78
|
-
return counter++;
|
79
|
-
};
|
80
|
-
}(),
|
81
|
-
defer: function(fn) {
|
82
|
-
setTimeout(fn, 0);
|
83
|
-
},
|
84
|
-
debounce: function(func, wait, immediate) {
|
85
|
-
var timeout, result;
|
86
|
-
return function() {
|
87
|
-
var context = this, args = arguments, later, callNow;
|
88
|
-
later = function() {
|
89
|
-
timeout = null;
|
90
|
-
if (!immediate) {
|
91
|
-
result = func.apply(context, args);
|
61
|
+
$.each(obj, function(key, val) {
|
62
|
+
if (result = test.call(null, val, key, obj)) {
|
63
|
+
return false;
|
92
64
|
}
|
65
|
+
});
|
66
|
+
return !!result;
|
67
|
+
},
|
68
|
+
mixin: $.extend,
|
69
|
+
getUniqueId: function() {
|
70
|
+
var counter = 0;
|
71
|
+
return function() {
|
72
|
+
return counter++;
|
93
73
|
};
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
74
|
+
}(),
|
75
|
+
templatify: function templatify(obj) {
|
76
|
+
return $.isFunction(obj) ? obj : template;
|
77
|
+
function template() {
|
78
|
+
return String(obj);
|
99
79
|
}
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
if (remaining <= 0) {
|
80
|
+
},
|
81
|
+
defer: function(fn) {
|
82
|
+
setTimeout(fn, 0);
|
83
|
+
},
|
84
|
+
debounce: function(func, wait, immediate) {
|
85
|
+
var timeout, result;
|
86
|
+
return function() {
|
87
|
+
var context = this, args = arguments, later, callNow;
|
88
|
+
later = function() {
|
89
|
+
timeout = null;
|
90
|
+
if (!immediate) {
|
91
|
+
result = func.apply(context, args);
|
92
|
+
}
|
93
|
+
};
|
94
|
+
callNow = immediate && !timeout;
|
116
95
|
clearTimeout(timeout);
|
96
|
+
timeout = setTimeout(later, wait);
|
97
|
+
if (callNow) {
|
98
|
+
result = func.apply(context, args);
|
99
|
+
}
|
100
|
+
return result;
|
101
|
+
};
|
102
|
+
},
|
103
|
+
throttle: function(func, wait) {
|
104
|
+
var context, args, timeout, result, previous, later;
|
105
|
+
previous = 0;
|
106
|
+
later = function() {
|
107
|
+
previous = new Date();
|
117
108
|
timeout = null;
|
118
|
-
previous = now;
|
119
109
|
result = func.apply(context, args);
|
120
|
-
}
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
110
|
+
};
|
111
|
+
return function() {
|
112
|
+
var now = new Date(), remaining = wait - (now - previous);
|
113
|
+
context = this;
|
114
|
+
args = arguments;
|
115
|
+
if (remaining <= 0) {
|
116
|
+
clearTimeout(timeout);
|
117
|
+
timeout = null;
|
118
|
+
previous = now;
|
119
|
+
result = func.apply(context, args);
|
120
|
+
} else if (!timeout) {
|
121
|
+
timeout = setTimeout(later, remaining);
|
122
|
+
}
|
123
|
+
return result;
|
124
|
+
};
|
125
|
+
},
|
126
|
+
noop: function() {}
|
127
|
+
};
|
128
|
+
}();
|
129
|
+
var VERSION = "0.10.5";
|
130
|
+
var tokenizers = function() {
|
131
|
+
"use strict";
|
139
132
|
return {
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
133
|
+
nonword: nonword,
|
134
|
+
whitespace: whitespace,
|
135
|
+
obj: {
|
136
|
+
nonword: getObjTokenizer(nonword),
|
137
|
+
whitespace: getObjTokenizer(whitespace)
|
138
|
+
}
|
139
|
+
};
|
140
|
+
function whitespace(str) {
|
141
|
+
str = _.toStr(str);
|
142
|
+
return str ? str.split(/\s+/) : [];
|
143
|
+
}
|
144
|
+
function nonword(str) {
|
145
|
+
str = _.toStr(str);
|
146
|
+
return str ? str.split(/\W+/) : [];
|
147
|
+
}
|
148
|
+
function getObjTokenizer(tokenizer) {
|
149
|
+
return function setKey() {
|
150
|
+
var args = [].slice.call(arguments, 0);
|
151
|
+
return function tokenize(o) {
|
152
|
+
var tokens = [];
|
153
|
+
_.each(args, function(k) {
|
154
|
+
tokens = tokens.concat(tokenizer(_.toStr(o[k])));
|
155
|
+
});
|
156
|
+
return tokens;
|
157
|
+
};
|
158
|
+
};
|
159
|
+
}
|
160
|
+
}();
|
161
|
+
var LruCache = function() {
|
162
|
+
"use strict";
|
163
|
+
function LruCache(maxSize) {
|
164
|
+
this.maxSize = _.isNumber(maxSize) ? maxSize : 100;
|
165
|
+
this.reset();
|
166
|
+
if (this.maxSize <= 0) {
|
167
|
+
this.set = this.get = $.noop;
|
168
|
+
}
|
169
|
+
}
|
170
|
+
_.mixin(LruCache.prototype, {
|
171
|
+
set: function set(key, val) {
|
172
|
+
var tailItem = this.list.tail, node;
|
173
|
+
if (this.size >= this.maxSize) {
|
174
|
+
this.list.remove(tailItem);
|
175
|
+
delete this.hash[tailItem.key];
|
144
176
|
}
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
177
|
+
if (node = this.hash[key]) {
|
178
|
+
node.val = val;
|
179
|
+
this.list.moveToFront(node);
|
180
|
+
} else {
|
181
|
+
node = new Node(key, val);
|
182
|
+
this.list.add(node);
|
183
|
+
this.hash[key] = node;
|
184
|
+
this.size++;
|
150
185
|
}
|
151
|
-
return this;
|
152
186
|
},
|
153
|
-
|
154
|
-
var
|
155
|
-
if (
|
156
|
-
|
157
|
-
|
158
|
-
events = events.split(eventSplitter);
|
159
|
-
while (event = events.shift()) {
|
160
|
-
if (callbacks = this._callbacks[event]) {
|
161
|
-
for (var i = 0; i < callbacks.length; i += 1) {
|
162
|
-
callbacks[i].call(this, {
|
163
|
-
type: event,
|
164
|
-
data: data
|
165
|
-
});
|
166
|
-
}
|
167
|
-
}
|
187
|
+
get: function get(key) {
|
188
|
+
var node = this.hash[key];
|
189
|
+
if (node) {
|
190
|
+
this.list.moveToFront(node);
|
191
|
+
return node.val;
|
168
192
|
}
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
var namespace = "typeahead:";
|
175
|
-
function EventBus(o) {
|
176
|
-
if (!o || !o.el) {
|
177
|
-
$.error("EventBus initialized without el");
|
193
|
+
},
|
194
|
+
reset: function reset() {
|
195
|
+
this.size = 0;
|
196
|
+
this.hash = {};
|
197
|
+
this.list = new List();
|
178
198
|
}
|
179
|
-
|
199
|
+
});
|
200
|
+
function List() {
|
201
|
+
this.head = this.tail = null;
|
180
202
|
}
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
203
|
+
_.mixin(List.prototype, {
|
204
|
+
add: function add(node) {
|
205
|
+
if (this.head) {
|
206
|
+
node.next = this.head;
|
207
|
+
this.head.prev = node;
|
208
|
+
}
|
209
|
+
this.head = node;
|
210
|
+
this.tail = this.tail || node;
|
211
|
+
},
|
212
|
+
remove: function remove(node) {
|
213
|
+
node.prev ? node.prev.next = node.next : this.head = node.next;
|
214
|
+
node.next ? node.next.prev = node.prev : this.tail = node.prev;
|
215
|
+
},
|
216
|
+
moveToFront: function(node) {
|
217
|
+
this.remove(node);
|
218
|
+
this.add(node);
|
185
219
|
}
|
186
220
|
});
|
187
|
-
|
221
|
+
function Node(key, val) {
|
222
|
+
this.key = key;
|
223
|
+
this.val = val;
|
224
|
+
this.prev = this.next = null;
|
225
|
+
}
|
226
|
+
return LruCache;
|
188
227
|
}();
|
189
228
|
var PersistentStorage = function() {
|
229
|
+
"use strict";
|
190
230
|
var ls, methods;
|
191
231
|
try {
|
192
232
|
ls = window.localStorage;
|
@@ -198,7 +238,7 @@
|
|
198
238
|
function PersistentStorage(namespace) {
|
199
239
|
this.prefix = [ "__", namespace, "__" ].join("");
|
200
240
|
this.ttlKey = "__ttl__";
|
201
|
-
this.keyMatcher = new RegExp("^" + this.prefix);
|
241
|
+
this.keyMatcher = new RegExp("^" + _.escapeRegExChars(this.prefix));
|
202
242
|
}
|
203
243
|
if (ls && window.JSON) {
|
204
244
|
methods = {
|
@@ -215,7 +255,7 @@
|
|
215
255
|
return decode(ls.getItem(this._prefix(key)));
|
216
256
|
},
|
217
257
|
set: function(key, val, ttl) {
|
218
|
-
if (
|
258
|
+
if (_.isNumber(ttl)) {
|
219
259
|
ls.setItem(this._ttlKey(key), encode(now() + ttl));
|
220
260
|
} else {
|
221
261
|
ls.removeItem(this._ttlKey(key));
|
@@ -241,416 +281,835 @@
|
|
241
281
|
},
|
242
282
|
isExpired: function(key) {
|
243
283
|
var ttl = decode(ls.getItem(this._ttlKey(key)));
|
244
|
-
return
|
284
|
+
return _.isNumber(ttl) && now() > ttl ? true : false;
|
245
285
|
}
|
246
286
|
};
|
247
287
|
} else {
|
248
288
|
methods = {
|
249
|
-
get:
|
250
|
-
set:
|
251
|
-
remove:
|
252
|
-
clear:
|
253
|
-
isExpired:
|
289
|
+
get: _.noop,
|
290
|
+
set: _.noop,
|
291
|
+
remove: _.noop,
|
292
|
+
clear: _.noop,
|
293
|
+
isExpired: _.noop
|
254
294
|
};
|
255
295
|
}
|
256
|
-
|
296
|
+
_.mixin(PersistentStorage.prototype, methods);
|
257
297
|
return PersistentStorage;
|
258
298
|
function now() {
|
259
299
|
return new Date().getTime();
|
260
300
|
}
|
261
301
|
function encode(val) {
|
262
|
-
return JSON.stringify(
|
302
|
+
return JSON.stringify(_.isUndefined(val) ? null : val);
|
263
303
|
}
|
264
304
|
function decode(val) {
|
265
305
|
return JSON.parse(val);
|
266
306
|
}
|
267
307
|
}();
|
268
|
-
var RequestCache = function() {
|
269
|
-
function RequestCache(o) {
|
270
|
-
utils.bindAll(this);
|
271
|
-
o = o || {};
|
272
|
-
this.sizeLimit = o.sizeLimit || 10;
|
273
|
-
this.cache = {};
|
274
|
-
this.cachedKeysByAge = [];
|
275
|
-
}
|
276
|
-
utils.mixin(RequestCache.prototype, {
|
277
|
-
get: function(url) {
|
278
|
-
return this.cache[url];
|
279
|
-
},
|
280
|
-
set: function(url, resp) {
|
281
|
-
var requestToEvict;
|
282
|
-
if (this.cachedKeysByAge.length === this.sizeLimit) {
|
283
|
-
requestToEvict = this.cachedKeysByAge.shift();
|
284
|
-
delete this.cache[requestToEvict];
|
285
|
-
}
|
286
|
-
this.cache[url] = resp;
|
287
|
-
this.cachedKeysByAge.push(url);
|
288
|
-
}
|
289
|
-
});
|
290
|
-
return RequestCache;
|
291
|
-
}();
|
292
308
|
var Transport = function() {
|
293
|
-
|
309
|
+
"use strict";
|
310
|
+
var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, sharedCache = new LruCache(10);
|
294
311
|
function Transport(o) {
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
this.url = o.url;
|
302
|
-
this.wildcard = o.wildcard || "%QUERY";
|
303
|
-
this.filter = o.filter;
|
304
|
-
this.replace = o.replace;
|
305
|
-
this.ajaxSettings = {
|
306
|
-
type: "get",
|
307
|
-
cache: o.cache,
|
308
|
-
timeout: o.timeout,
|
309
|
-
dataType: o.dataType || "json",
|
310
|
-
beforeSend: o.beforeSend
|
311
|
-
};
|
312
|
-
this._get = (/^throttle$/i.test(o.rateLimitFn) ? utils.throttle : utils.debounce)(this._get, o.rateLimitWait || 300);
|
312
|
+
o = o || {};
|
313
|
+
this.cancelled = false;
|
314
|
+
this.lastUrl = null;
|
315
|
+
this._send = o.transport ? callbackToDeferred(o.transport) : $.ajax;
|
316
|
+
this._get = o.rateLimiter ? o.rateLimiter(this._get) : this._get;
|
317
|
+
this._cache = o.cache === false ? new LruCache(0) : sharedCache;
|
313
318
|
}
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
+
Transport.setMaxPendingRequests = function setMaxPendingRequests(num) {
|
320
|
+
maxPendingRequests = num;
|
321
|
+
};
|
322
|
+
Transport.resetCache = function resetCache() {
|
323
|
+
sharedCache.reset();
|
324
|
+
};
|
325
|
+
_.mixin(Transport.prototype, {
|
326
|
+
_get: function(url, o, cb) {
|
327
|
+
var that = this, jqXhr;
|
328
|
+
if (this.cancelled || url !== this.lastUrl) {
|
329
|
+
return;
|
330
|
+
}
|
331
|
+
if (jqXhr = pendingRequests[url]) {
|
332
|
+
jqXhr.done(done).fail(fail);
|
333
|
+
} else if (pendingRequestsCount < maxPendingRequests) {
|
334
|
+
pendingRequestsCount++;
|
335
|
+
pendingRequests[url] = this._send(url, o).done(done).fail(fail).always(always);
|
319
336
|
} else {
|
320
337
|
this.onDeckRequestArgs = [].slice.call(arguments, 0);
|
321
338
|
}
|
322
339
|
function done(resp) {
|
323
|
-
|
324
|
-
|
325
|
-
requestCache.set(url, resp);
|
340
|
+
cb && cb(null, resp);
|
341
|
+
that._cache.set(url, resp);
|
326
342
|
}
|
327
|
-
|
328
|
-
|
329
|
-
var that = this, jqXhr = pendingRequests[url];
|
330
|
-
if (!jqXhr) {
|
331
|
-
incrementPendingRequests();
|
332
|
-
jqXhr = pendingRequests[url] = $.ajax(url, this.ajaxSettings).always(always);
|
343
|
+
function fail() {
|
344
|
+
cb && cb(true);
|
333
345
|
}
|
334
|
-
return jqXhr;
|
335
346
|
function always() {
|
336
|
-
|
337
|
-
pendingRequests[url]
|
347
|
+
pendingRequestsCount--;
|
348
|
+
delete pendingRequests[url];
|
338
349
|
if (that.onDeckRequestArgs) {
|
339
350
|
that._get.apply(that, that.onDeckRequestArgs);
|
340
351
|
that.onDeckRequestArgs = null;
|
341
352
|
}
|
342
353
|
}
|
343
354
|
},
|
344
|
-
get: function(
|
345
|
-
var
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
355
|
+
get: function(url, o, cb) {
|
356
|
+
var resp;
|
357
|
+
if (_.isFunction(o)) {
|
358
|
+
cb = o;
|
359
|
+
o = {};
|
360
|
+
}
|
361
|
+
this.cancelled = false;
|
362
|
+
this.lastUrl = url;
|
363
|
+
if (resp = this._cache.get(url)) {
|
364
|
+
_.defer(function() {
|
365
|
+
cb && cb(null, resp);
|
351
366
|
});
|
352
367
|
} else {
|
353
|
-
this._get(url, cb);
|
368
|
+
this._get(url, o, cb);
|
354
369
|
}
|
355
370
|
return !!resp;
|
371
|
+
},
|
372
|
+
cancel: function() {
|
373
|
+
this.cancelled = true;
|
356
374
|
}
|
357
375
|
});
|
358
376
|
return Transport;
|
359
|
-
function
|
360
|
-
|
377
|
+
function callbackToDeferred(fn) {
|
378
|
+
return function customSendWrapper(url, o) {
|
379
|
+
var deferred = $.Deferred();
|
380
|
+
fn(url, o, onSuccess, onError);
|
381
|
+
return deferred;
|
382
|
+
function onSuccess(resp) {
|
383
|
+
_.defer(function() {
|
384
|
+
deferred.resolve(resp);
|
385
|
+
});
|
386
|
+
}
|
387
|
+
function onError(err) {
|
388
|
+
_.defer(function() {
|
389
|
+
deferred.reject(err);
|
390
|
+
});
|
391
|
+
}
|
392
|
+
};
|
361
393
|
}
|
362
|
-
|
363
|
-
|
394
|
+
}();
|
395
|
+
var SearchIndex = function() {
|
396
|
+
"use strict";
|
397
|
+
function SearchIndex(o) {
|
398
|
+
o = o || {};
|
399
|
+
if (!o.datumTokenizer || !o.queryTokenizer) {
|
400
|
+
$.error("datumTokenizer and queryTokenizer are both required");
|
401
|
+
}
|
402
|
+
this.datumTokenizer = o.datumTokenizer;
|
403
|
+
this.queryTokenizer = o.queryTokenizer;
|
404
|
+
this.reset();
|
364
405
|
}
|
365
|
-
|
366
|
-
|
406
|
+
_.mixin(SearchIndex.prototype, {
|
407
|
+
bootstrap: function bootstrap(o) {
|
408
|
+
this.datums = o.datums;
|
409
|
+
this.trie = o.trie;
|
410
|
+
},
|
411
|
+
add: function(data) {
|
412
|
+
var that = this;
|
413
|
+
data = _.isArray(data) ? data : [ data ];
|
414
|
+
_.each(data, function(datum) {
|
415
|
+
var id, tokens;
|
416
|
+
id = that.datums.push(datum) - 1;
|
417
|
+
tokens = normalizeTokens(that.datumTokenizer(datum));
|
418
|
+
_.each(tokens, function(token) {
|
419
|
+
var node, chars, ch;
|
420
|
+
node = that.trie;
|
421
|
+
chars = token.split("");
|
422
|
+
while (ch = chars.shift()) {
|
423
|
+
node = node.children[ch] || (node.children[ch] = newNode());
|
424
|
+
node.ids.push(id);
|
425
|
+
}
|
426
|
+
});
|
427
|
+
});
|
428
|
+
},
|
429
|
+
get: function get(query) {
|
430
|
+
var that = this, tokens, matches;
|
431
|
+
tokens = normalizeTokens(this.queryTokenizer(query));
|
432
|
+
_.each(tokens, function(token) {
|
433
|
+
var node, chars, ch, ids;
|
434
|
+
if (matches && matches.length === 0) {
|
435
|
+
return false;
|
436
|
+
}
|
437
|
+
node = that.trie;
|
438
|
+
chars = token.split("");
|
439
|
+
while (node && (ch = chars.shift())) {
|
440
|
+
node = node.children[ch];
|
441
|
+
}
|
442
|
+
if (node && chars.length === 0) {
|
443
|
+
ids = node.ids.slice(0);
|
444
|
+
matches = matches ? getIntersection(matches, ids) : ids;
|
445
|
+
} else {
|
446
|
+
matches = [];
|
447
|
+
return false;
|
448
|
+
}
|
449
|
+
});
|
450
|
+
return matches ? _.map(unique(matches), function(id) {
|
451
|
+
return that.datums[id];
|
452
|
+
}) : [];
|
453
|
+
},
|
454
|
+
reset: function reset() {
|
455
|
+
this.datums = [];
|
456
|
+
this.trie = newNode();
|
457
|
+
},
|
458
|
+
serialize: function serialize() {
|
459
|
+
return {
|
460
|
+
datums: this.datums,
|
461
|
+
trie: this.trie
|
462
|
+
};
|
463
|
+
}
|
464
|
+
});
|
465
|
+
return SearchIndex;
|
466
|
+
function normalizeTokens(tokens) {
|
467
|
+
tokens = _.filter(tokens, function(token) {
|
468
|
+
return !!token;
|
469
|
+
});
|
470
|
+
tokens = _.map(tokens, function(token) {
|
471
|
+
return token.toLowerCase();
|
472
|
+
});
|
473
|
+
return tokens;
|
474
|
+
}
|
475
|
+
function newNode() {
|
476
|
+
return {
|
477
|
+
ids: [],
|
478
|
+
children: {}
|
479
|
+
};
|
480
|
+
}
|
481
|
+
function unique(array) {
|
482
|
+
var seen = {}, uniques = [];
|
483
|
+
for (var i = 0, len = array.length; i < len; i++) {
|
484
|
+
if (!seen[array[i]]) {
|
485
|
+
seen[array[i]] = true;
|
486
|
+
uniques.push(array[i]);
|
487
|
+
}
|
488
|
+
}
|
489
|
+
return uniques;
|
490
|
+
}
|
491
|
+
function getIntersection(arrayA, arrayB) {
|
492
|
+
var ai = 0, bi = 0, intersection = [];
|
493
|
+
arrayA = arrayA.sort(compare);
|
494
|
+
arrayB = arrayB.sort(compare);
|
495
|
+
var lenArrayA = arrayA.length, lenArrayB = arrayB.length;
|
496
|
+
while (ai < lenArrayA && bi < lenArrayB) {
|
497
|
+
if (arrayA[ai] < arrayB[bi]) {
|
498
|
+
ai++;
|
499
|
+
} else if (arrayA[ai] > arrayB[bi]) {
|
500
|
+
bi++;
|
501
|
+
} else {
|
502
|
+
intersection.push(arrayA[ai]);
|
503
|
+
ai++;
|
504
|
+
bi++;
|
505
|
+
}
|
506
|
+
}
|
507
|
+
return intersection;
|
508
|
+
function compare(a, b) {
|
509
|
+
return a - b;
|
510
|
+
}
|
367
511
|
}
|
368
512
|
}();
|
369
|
-
var
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
513
|
+
var oParser = function() {
|
514
|
+
"use strict";
|
515
|
+
return {
|
516
|
+
local: getLocal,
|
517
|
+
prefetch: getPrefetch,
|
518
|
+
remote: getRemote
|
375
519
|
};
|
376
|
-
function
|
377
|
-
|
378
|
-
|
379
|
-
|
520
|
+
function getLocal(o) {
|
521
|
+
return o.local || null;
|
522
|
+
}
|
523
|
+
function getPrefetch(o) {
|
524
|
+
var prefetch, defaults;
|
525
|
+
defaults = {
|
526
|
+
url: null,
|
527
|
+
thumbprint: "",
|
528
|
+
ttl: 24 * 60 * 60 * 1e3,
|
529
|
+
filter: null,
|
530
|
+
ajax: {}
|
531
|
+
};
|
532
|
+
if (prefetch = o.prefetch || null) {
|
533
|
+
prefetch = _.isString(prefetch) ? {
|
534
|
+
url: prefetch
|
535
|
+
} : prefetch;
|
536
|
+
prefetch = _.mixin(defaults, prefetch);
|
537
|
+
prefetch.thumbprint = VERSION + prefetch.thumbprint;
|
538
|
+
prefetch.ajax.type = prefetch.ajax.type || "GET";
|
539
|
+
prefetch.ajax.dataType = prefetch.ajax.dataType || "json";
|
540
|
+
!prefetch.url && $.error("prefetch requires url to be set");
|
380
541
|
}
|
381
|
-
|
542
|
+
return prefetch;
|
543
|
+
}
|
544
|
+
function getRemote(o) {
|
545
|
+
var remote, defaults;
|
546
|
+
defaults = {
|
547
|
+
url: null,
|
548
|
+
cache: true,
|
549
|
+
wildcard: "%QUERY",
|
550
|
+
replace: null,
|
551
|
+
rateLimitBy: "debounce",
|
552
|
+
rateLimitWait: 300,
|
553
|
+
send: null,
|
554
|
+
filter: null,
|
555
|
+
ajax: {}
|
556
|
+
};
|
557
|
+
if (remote = o.remote || null) {
|
558
|
+
remote = _.isString(remote) ? {
|
559
|
+
url: remote
|
560
|
+
} : remote;
|
561
|
+
remote = _.mixin(defaults, remote);
|
562
|
+
remote.rateLimiter = /^throttle$/i.test(remote.rateLimitBy) ? byThrottle(remote.rateLimitWait) : byDebounce(remote.rateLimitWait);
|
563
|
+
remote.ajax.type = remote.ajax.type || "GET";
|
564
|
+
remote.ajax.dataType = remote.ajax.dataType || "json";
|
565
|
+
delete remote.rateLimitBy;
|
566
|
+
delete remote.rateLimitWait;
|
567
|
+
!remote.url && $.error("remote requires url to be set");
|
568
|
+
}
|
569
|
+
return remote;
|
570
|
+
function byDebounce(wait) {
|
571
|
+
return function(fn) {
|
572
|
+
return _.debounce(fn, wait);
|
573
|
+
};
|
574
|
+
}
|
575
|
+
function byThrottle(wait) {
|
576
|
+
return function(fn) {
|
577
|
+
return _.throttle(fn, wait);
|
578
|
+
};
|
579
|
+
}
|
580
|
+
}
|
581
|
+
}();
|
582
|
+
(function(root) {
|
583
|
+
"use strict";
|
584
|
+
var old, keys;
|
585
|
+
old = root.Bloodhound;
|
586
|
+
keys = {
|
587
|
+
data: "data",
|
588
|
+
protocol: "protocol",
|
589
|
+
thumbprint: "thumbprint"
|
590
|
+
};
|
591
|
+
root.Bloodhound = Bloodhound;
|
592
|
+
function Bloodhound(o) {
|
593
|
+
if (!o || !o.local && !o.prefetch && !o.remote) {
|
382
594
|
$.error("one of local, prefetch, or remote is required");
|
383
595
|
}
|
384
|
-
this.name = o.name || utils.getUniqueId();
|
385
596
|
this.limit = o.limit || 5;
|
386
|
-
this.
|
387
|
-
this.
|
388
|
-
this.
|
389
|
-
this.
|
390
|
-
this.
|
391
|
-
this.
|
392
|
-
this.
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
this.storage =
|
597
|
+
this.sorter = getSorter(o.sorter);
|
598
|
+
this.dupDetector = o.dupDetector || ignoreDuplicates;
|
599
|
+
this.local = oParser.local(o);
|
600
|
+
this.prefetch = oParser.prefetch(o);
|
601
|
+
this.remote = oParser.remote(o);
|
602
|
+
this.cacheKey = this.prefetch ? this.prefetch.cacheKey || this.prefetch.url : null;
|
603
|
+
this.index = new SearchIndex({
|
604
|
+
datumTokenizer: o.datumTokenizer,
|
605
|
+
queryTokenizer: o.queryTokenizer
|
606
|
+
});
|
607
|
+
this.storage = this.cacheKey ? new PersistentStorage(this.cacheKey) : null;
|
397
608
|
}
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
storedAdjacencyList = this.storage.get(keys.adjacencyList);
|
409
|
-
}
|
410
|
-
isExpired = storedThumbprint !== thumbprint || storedProtocol !== utils.getProtocol();
|
411
|
-
o = utils.isString(o) ? {
|
412
|
-
url: o
|
413
|
-
} : o;
|
414
|
-
o.ttl = utils.isNumber(o.ttl) ? o.ttl : 24 * 60 * 60 * 1e3;
|
415
|
-
if (storedItemHash && storedAdjacencyList && !isExpired) {
|
416
|
-
this._mergeProcessedData({
|
417
|
-
itemHash: storedItemHash,
|
418
|
-
adjacencyList: storedAdjacencyList
|
419
|
-
});
|
609
|
+
Bloodhound.noConflict = function noConflict() {
|
610
|
+
root.Bloodhound = old;
|
611
|
+
return Bloodhound;
|
612
|
+
};
|
613
|
+
Bloodhound.tokenizers = tokenizers;
|
614
|
+
_.mixin(Bloodhound.prototype, {
|
615
|
+
_loadPrefetch: function loadPrefetch(o) {
|
616
|
+
var that = this, serialized, deferred;
|
617
|
+
if (serialized = this._readFromStorage(o.thumbprint)) {
|
618
|
+
this.index.bootstrap(serialized);
|
420
619
|
deferred = $.Deferred().resolve();
|
421
620
|
} else {
|
422
|
-
deferred = $.
|
621
|
+
deferred = $.ajax(o.url, o.ajax).done(handlePrefetchResponse);
|
423
622
|
}
|
424
623
|
return deferred;
|
425
|
-
function
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
that.storage.set(keys.adjacencyList, adjacencyList, o.ttl);
|
430
|
-
that.storage.set(keys.thumbprint, thumbprint, o.ttl);
|
431
|
-
that.storage.set(keys.protocol, utils.getProtocol(), o.ttl);
|
432
|
-
}
|
433
|
-
that._mergeProcessedData(processedData);
|
624
|
+
function handlePrefetchResponse(resp) {
|
625
|
+
that.clear();
|
626
|
+
that.add(o.filter ? o.filter(resp) : resp);
|
627
|
+
that._saveToStorage(that.index.serialize(), o.thumbprint, o.ttl);
|
434
628
|
}
|
435
629
|
},
|
436
|
-
|
437
|
-
var
|
438
|
-
|
439
|
-
|
440
|
-
}
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
630
|
+
_getFromRemote: function getFromRemote(query, cb) {
|
631
|
+
var that = this, url, uriEncodedQuery;
|
632
|
+
if (!this.transport) {
|
633
|
+
return;
|
634
|
+
}
|
635
|
+
query = query || "";
|
636
|
+
uriEncodedQuery = encodeURIComponent(query);
|
637
|
+
url = this.remote.replace ? this.remote.replace(this.remote.url, query) : this.remote.url.replace(this.remote.wildcard, uriEncodedQuery);
|
638
|
+
return this.transport.get(url, this.remote.ajax, handleRemoteResponse);
|
639
|
+
function handleRemoteResponse(err, resp) {
|
640
|
+
err ? cb([]) : cb(that.remote.filter ? that.remote.filter(resp) : resp);
|
446
641
|
}
|
447
|
-
item.tokens = utils.filter(item.tokens, function(token) {
|
448
|
-
return !utils.isBlankString(token);
|
449
|
-
});
|
450
|
-
item.tokens = utils.map(item.tokens, function(token) {
|
451
|
-
return token.toLowerCase();
|
452
|
-
});
|
453
|
-
return item;
|
454
|
-
},
|
455
|
-
_processData: function(data) {
|
456
|
-
var that = this, itemHash = {}, adjacencyList = {};
|
457
|
-
utils.each(data, function(i, datum) {
|
458
|
-
var item = that._transformDatum(datum), id = utils.getUniqueId(item.value);
|
459
|
-
itemHash[id] = item;
|
460
|
-
utils.each(item.tokens, function(i, token) {
|
461
|
-
var character = token.charAt(0), adjacency = adjacencyList[character] || (adjacencyList[character] = [ id ]);
|
462
|
-
!~utils.indexOf(adjacency, id) && adjacency.push(id);
|
463
|
-
});
|
464
|
-
});
|
465
|
-
return {
|
466
|
-
itemHash: itemHash,
|
467
|
-
adjacencyList: adjacencyList
|
468
|
-
};
|
469
642
|
},
|
470
|
-
|
471
|
-
|
472
|
-
utils.mixin(this.itemHash, processedData.itemHash);
|
473
|
-
utils.each(processedData.adjacencyList, function(character, adjacency) {
|
474
|
-
var masterAdjacency = that.adjacencyList[character];
|
475
|
-
that.adjacencyList[character] = masterAdjacency ? masterAdjacency.concat(adjacency) : adjacency;
|
476
|
-
});
|
643
|
+
_cancelLastRemoteRequest: function cancelLastRemoteRequest() {
|
644
|
+
this.transport && this.transport.cancel();
|
477
645
|
},
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
});
|
484
|
-
utils.each(firstChars, function(i, firstChar) {
|
485
|
-
var list = that.adjacencyList[firstChar];
|
486
|
-
if (!list) {
|
487
|
-
return false;
|
488
|
-
}
|
489
|
-
lists.push(list);
|
490
|
-
if (!shortestList || list.length < shortestList.length) {
|
491
|
-
shortestList = list;
|
492
|
-
}
|
493
|
-
});
|
494
|
-
if (lists.length < firstChars.length) {
|
495
|
-
return [];
|
646
|
+
_saveToStorage: function saveToStorage(data, thumbprint, ttl) {
|
647
|
+
if (this.storage) {
|
648
|
+
this.storage.set(keys.data, data, ttl);
|
649
|
+
this.storage.set(keys.protocol, location.protocol, ttl);
|
650
|
+
this.storage.set(keys.thumbprint, thumbprint, ttl);
|
496
651
|
}
|
497
|
-
utils.each(shortestList, function(i, id) {
|
498
|
-
var item = that.itemHash[id], isCandidate, isMatch;
|
499
|
-
isCandidate = utils.every(lists, function(list) {
|
500
|
-
return ~utils.indexOf(list, id);
|
501
|
-
});
|
502
|
-
isMatch = isCandidate && utils.every(terms, function(term) {
|
503
|
-
return utils.some(item.tokens, function(token) {
|
504
|
-
return token.indexOf(term) === 0;
|
505
|
-
});
|
506
|
-
});
|
507
|
-
isMatch && suggestions.push(item);
|
508
|
-
});
|
509
|
-
return suggestions;
|
510
652
|
},
|
511
|
-
|
512
|
-
var
|
513
|
-
|
653
|
+
_readFromStorage: function readFromStorage(thumbprint) {
|
654
|
+
var stored = {}, isExpired;
|
655
|
+
if (this.storage) {
|
656
|
+
stored.data = this.storage.get(keys.data);
|
657
|
+
stored.protocol = this.storage.get(keys.protocol);
|
658
|
+
stored.thumbprint = this.storage.get(keys.thumbprint);
|
659
|
+
}
|
660
|
+
isExpired = stored.thumbprint !== thumbprint || stored.protocol !== location.protocol;
|
661
|
+
return stored.data && !isExpired ? stored.data : null;
|
662
|
+
},
|
663
|
+
_initialize: function initialize() {
|
664
|
+
var that = this, local = this.local, deferred;
|
665
|
+
deferred = this.prefetch ? this._loadPrefetch(this.prefetch) : $.Deferred().resolve();
|
666
|
+
local && deferred.done(addLocalToIndex);
|
514
667
|
this.transport = this.remote ? new Transport(this.remote) : null;
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
};
|
520
|
-
return deferred;
|
668
|
+
return this.initPromise = deferred.promise();
|
669
|
+
function addLocalToIndex() {
|
670
|
+
that.add(_.isFunction(local) ? local() : local);
|
671
|
+
}
|
521
672
|
},
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
673
|
+
initialize: function initialize(force) {
|
674
|
+
return !this.initPromise || force ? this._initialize() : this.initPromise;
|
675
|
+
},
|
676
|
+
add: function add(data) {
|
677
|
+
this.index.add(data);
|
678
|
+
},
|
679
|
+
get: function get(query, cb) {
|
680
|
+
var that = this, matches = [], cacheHit = false;
|
681
|
+
matches = this.index.get(query);
|
682
|
+
matches = this.sorter(matches).slice(0, this.limit);
|
683
|
+
matches.length < this.limit ? cacheHit = this._getFromRemote(query, returnRemoteMatches) : this._cancelLastRemoteRequest();
|
684
|
+
if (!cacheHit) {
|
685
|
+
(matches.length > 0 || !this.transport) && cb && cb(matches);
|
526
686
|
}
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
function processRemoteData(data) {
|
534
|
-
suggestions = suggestions.slice(0);
|
535
|
-
utils.each(data, function(i, datum) {
|
536
|
-
var item = that._transformDatum(datum), isDuplicate;
|
537
|
-
isDuplicate = utils.some(suggestions, function(suggestion) {
|
538
|
-
return item.value === suggestion.value;
|
687
|
+
function returnRemoteMatches(remoteMatches) {
|
688
|
+
var matchesWithBackfill = matches.slice(0);
|
689
|
+
_.each(remoteMatches, function(remoteMatch) {
|
690
|
+
var isDuplicate;
|
691
|
+
isDuplicate = _.some(matchesWithBackfill, function(match) {
|
692
|
+
return that.dupDetector(remoteMatch, match);
|
539
693
|
});
|
540
|
-
!isDuplicate &&
|
541
|
-
return
|
694
|
+
!isDuplicate && matchesWithBackfill.push(remoteMatch);
|
695
|
+
return matchesWithBackfill.length < that.limit;
|
542
696
|
});
|
543
|
-
cb && cb(
|
697
|
+
cb && cb(that.sorter(matchesWithBackfill));
|
544
698
|
}
|
699
|
+
},
|
700
|
+
clear: function clear() {
|
701
|
+
this.index.reset();
|
702
|
+
},
|
703
|
+
clearPrefetchCache: function clearPrefetchCache() {
|
704
|
+
this.storage && this.storage.clear();
|
705
|
+
},
|
706
|
+
clearRemoteCache: function clearRemoteCache() {
|
707
|
+
this.transport && Transport.resetCache();
|
708
|
+
},
|
709
|
+
ttAdapter: function ttAdapter() {
|
710
|
+
return _.bind(this.get, this);
|
545
711
|
}
|
546
712
|
});
|
547
|
-
return
|
548
|
-
function
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
}
|
553
|
-
|
554
|
-
|
713
|
+
return Bloodhound;
|
714
|
+
function getSorter(sortFn) {
|
715
|
+
return _.isFunction(sortFn) ? sort : noSort;
|
716
|
+
function sort(array) {
|
717
|
+
return array.sort(sortFn);
|
718
|
+
}
|
719
|
+
function noSort(array) {
|
720
|
+
return array;
|
721
|
+
}
|
722
|
+
}
|
723
|
+
function ignoreDuplicates() {
|
724
|
+
return false;
|
725
|
+
}
|
726
|
+
})(this);
|
727
|
+
var html = function() {
|
728
|
+
return {
|
729
|
+
wrapper: '<span class="twitter-typeahead"></span>',
|
730
|
+
dropdown: '<span class="tt-dropdown-menu"></span>',
|
731
|
+
dataset: '<div class="tt-dataset-%CLASS%"></div>',
|
732
|
+
suggestions: '<span class="tt-suggestions"></span>',
|
733
|
+
suggestion: '<div class="tt-suggestion"></div>'
|
734
|
+
};
|
735
|
+
}();
|
736
|
+
var css = function() {
|
737
|
+
"use strict";
|
738
|
+
var css = {
|
739
|
+
wrapper: {
|
740
|
+
position: "relative",
|
741
|
+
display: "inline-block"
|
742
|
+
},
|
743
|
+
hint: {
|
744
|
+
position: "absolute",
|
745
|
+
top: "0",
|
746
|
+
left: "0",
|
747
|
+
borderColor: "transparent",
|
748
|
+
boxShadow: "none",
|
749
|
+
opacity: "1"
|
750
|
+
},
|
751
|
+
input: {
|
752
|
+
position: "relative",
|
753
|
+
verticalAlign: "top",
|
754
|
+
backgroundColor: "transparent"
|
755
|
+
},
|
756
|
+
inputWithNoHint: {
|
757
|
+
position: "relative",
|
758
|
+
verticalAlign: "top"
|
759
|
+
},
|
760
|
+
dropdown: {
|
761
|
+
position: "absolute",
|
762
|
+
top: "100%",
|
763
|
+
left: "0",
|
764
|
+
zIndex: "100",
|
765
|
+
display: "none"
|
766
|
+
},
|
767
|
+
suggestions: {
|
768
|
+
display: "block"
|
769
|
+
},
|
770
|
+
suggestion: {
|
771
|
+
whiteSpace: "nowrap",
|
772
|
+
cursor: "pointer"
|
773
|
+
},
|
774
|
+
suggestionChild: {
|
775
|
+
whiteSpace: "normal"
|
776
|
+
},
|
777
|
+
ltr: {
|
778
|
+
left: "0",
|
779
|
+
right: "auto"
|
780
|
+
},
|
781
|
+
rtl: {
|
782
|
+
left: "auto",
|
783
|
+
right: " 0"
|
784
|
+
}
|
785
|
+
};
|
786
|
+
if (_.isMsie()) {
|
787
|
+
_.mixin(css.input, {
|
788
|
+
backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"
|
789
|
+
});
|
790
|
+
}
|
791
|
+
if (_.isMsie() && _.isMsie() <= 7) {
|
792
|
+
_.mixin(css.input, {
|
793
|
+
marginTop: "-1px"
|
794
|
+
});
|
795
|
+
}
|
796
|
+
return css;
|
797
|
+
}();
|
798
|
+
var EventBus = function() {
|
799
|
+
"use strict";
|
800
|
+
var namespace = "typeahead:";
|
801
|
+
function EventBus(o) {
|
802
|
+
if (!o || !o.el) {
|
803
|
+
$.error("EventBus initialized without el");
|
804
|
+
}
|
805
|
+
this.$el = $(o.el);
|
806
|
+
}
|
807
|
+
_.mixin(EventBus.prototype, {
|
808
|
+
trigger: function(type) {
|
809
|
+
var args = [].slice.call(arguments, 1);
|
810
|
+
this.$el.trigger(namespace + type, args);
|
811
|
+
}
|
812
|
+
});
|
813
|
+
return EventBus;
|
814
|
+
}();
|
815
|
+
var EventEmitter = function() {
|
816
|
+
"use strict";
|
817
|
+
var splitter = /\s+/, nextTick = getNextTick();
|
818
|
+
return {
|
819
|
+
onSync: onSync,
|
820
|
+
onAsync: onAsync,
|
821
|
+
off: off,
|
822
|
+
trigger: trigger
|
823
|
+
};
|
824
|
+
function on(method, types, cb, context) {
|
825
|
+
var type;
|
826
|
+
if (!cb) {
|
827
|
+
return this;
|
828
|
+
}
|
829
|
+
types = types.split(splitter);
|
830
|
+
cb = context ? bindContext(cb, context) : cb;
|
831
|
+
this._callbacks = this._callbacks || {};
|
832
|
+
while (type = types.shift()) {
|
833
|
+
this._callbacks[type] = this._callbacks[type] || {
|
834
|
+
sync: [],
|
835
|
+
async: []
|
836
|
+
};
|
837
|
+
this._callbacks[type][method].push(cb);
|
838
|
+
}
|
839
|
+
return this;
|
840
|
+
}
|
841
|
+
function onAsync(types, cb, context) {
|
842
|
+
return on.call(this, "async", types, cb, context);
|
843
|
+
}
|
844
|
+
function onSync(types, cb, context) {
|
845
|
+
return on.call(this, "sync", types, cb, context);
|
846
|
+
}
|
847
|
+
function off(types) {
|
848
|
+
var type;
|
849
|
+
if (!this._callbacks) {
|
850
|
+
return this;
|
851
|
+
}
|
852
|
+
types = types.split(splitter);
|
853
|
+
while (type = types.shift()) {
|
854
|
+
delete this._callbacks[type];
|
855
|
+
}
|
856
|
+
return this;
|
857
|
+
}
|
858
|
+
function trigger(types) {
|
859
|
+
var type, callbacks, args, syncFlush, asyncFlush;
|
860
|
+
if (!this._callbacks) {
|
861
|
+
return this;
|
862
|
+
}
|
863
|
+
types = types.split(splitter);
|
864
|
+
args = [].slice.call(arguments, 1);
|
865
|
+
while ((type = types.shift()) && (callbacks = this._callbacks[type])) {
|
866
|
+
syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args));
|
867
|
+
asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args));
|
868
|
+
syncFlush() && nextTick(asyncFlush);
|
869
|
+
}
|
870
|
+
return this;
|
871
|
+
}
|
872
|
+
function getFlush(callbacks, context, args) {
|
873
|
+
return flush;
|
874
|
+
function flush() {
|
875
|
+
var cancelled;
|
876
|
+
for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) {
|
877
|
+
cancelled = callbacks[i].apply(context, args) === false;
|
878
|
+
}
|
879
|
+
return !cancelled;
|
880
|
+
}
|
881
|
+
}
|
882
|
+
function getNextTick() {
|
883
|
+
var nextTickFn;
|
884
|
+
if (window.setImmediate) {
|
885
|
+
nextTickFn = function nextTickSetImmediate(fn) {
|
886
|
+
setImmediate(function() {
|
887
|
+
fn();
|
888
|
+
});
|
889
|
+
};
|
555
890
|
} else {
|
556
|
-
|
557
|
-
|
891
|
+
nextTickFn = function nextTickSetTimeout(fn) {
|
892
|
+
setTimeout(function() {
|
893
|
+
fn();
|
894
|
+
}, 0);
|
558
895
|
};
|
559
896
|
}
|
560
|
-
return
|
897
|
+
return nextTickFn;
|
561
898
|
}
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
var that = this;
|
566
|
-
utils.bindAll(this);
|
567
|
-
this.specialKeyCodeMap = {
|
568
|
-
9: "tab",
|
569
|
-
27: "esc",
|
570
|
-
37: "left",
|
571
|
-
39: "right",
|
572
|
-
13: "enter",
|
573
|
-
38: "up",
|
574
|
-
40: "down"
|
899
|
+
function bindContext(fn, context) {
|
900
|
+
return fn.bind ? fn.bind(context) : function() {
|
901
|
+
fn.apply(context, [].slice.call(arguments, 0));
|
575
902
|
};
|
903
|
+
}
|
904
|
+
}();
|
905
|
+
var highlight = function(doc) {
|
906
|
+
"use strict";
|
907
|
+
var defaults = {
|
908
|
+
node: null,
|
909
|
+
pattern: null,
|
910
|
+
tagName: "strong",
|
911
|
+
className: null,
|
912
|
+
wordsOnly: false,
|
913
|
+
caseSensitive: false
|
914
|
+
};
|
915
|
+
return function hightlight(o) {
|
916
|
+
var regex;
|
917
|
+
o = _.mixin({}, defaults, o);
|
918
|
+
if (!o.node || !o.pattern) {
|
919
|
+
return;
|
920
|
+
}
|
921
|
+
o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ];
|
922
|
+
regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly);
|
923
|
+
traverse(o.node, hightlightTextNode);
|
924
|
+
function hightlightTextNode(textNode) {
|
925
|
+
var match, patternNode, wrapperNode;
|
926
|
+
if (match = regex.exec(textNode.data)) {
|
927
|
+
wrapperNode = doc.createElement(o.tagName);
|
928
|
+
o.className && (wrapperNode.className = o.className);
|
929
|
+
patternNode = textNode.splitText(match.index);
|
930
|
+
patternNode.splitText(match[0].length);
|
931
|
+
wrapperNode.appendChild(patternNode.cloneNode(true));
|
932
|
+
textNode.parentNode.replaceChild(wrapperNode, patternNode);
|
933
|
+
}
|
934
|
+
return !!match;
|
935
|
+
}
|
936
|
+
function traverse(el, hightlightTextNode) {
|
937
|
+
var childNode, TEXT_NODE_TYPE = 3;
|
938
|
+
for (var i = 0; i < el.childNodes.length; i++) {
|
939
|
+
childNode = el.childNodes[i];
|
940
|
+
if (childNode.nodeType === TEXT_NODE_TYPE) {
|
941
|
+
i += hightlightTextNode(childNode) ? 1 : 0;
|
942
|
+
} else {
|
943
|
+
traverse(childNode, hightlightTextNode);
|
944
|
+
}
|
945
|
+
}
|
946
|
+
}
|
947
|
+
};
|
948
|
+
function getRegex(patterns, caseSensitive, wordsOnly) {
|
949
|
+
var escapedPatterns = [], regexStr;
|
950
|
+
for (var i = 0, len = patterns.length; i < len; i++) {
|
951
|
+
escapedPatterns.push(_.escapeRegExChars(patterns[i]));
|
952
|
+
}
|
953
|
+
regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")";
|
954
|
+
return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i");
|
955
|
+
}
|
956
|
+
}(window.document);
|
957
|
+
var Input = function() {
|
958
|
+
"use strict";
|
959
|
+
var specialKeyCodeMap;
|
960
|
+
specialKeyCodeMap = {
|
961
|
+
9: "tab",
|
962
|
+
27: "esc",
|
963
|
+
37: "left",
|
964
|
+
39: "right",
|
965
|
+
13: "enter",
|
966
|
+
38: "up",
|
967
|
+
40: "down"
|
968
|
+
};
|
969
|
+
function Input(o) {
|
970
|
+
var that = this, onBlur, onFocus, onKeydown, onInput;
|
971
|
+
o = o || {};
|
972
|
+
if (!o.input) {
|
973
|
+
$.error("input is missing");
|
974
|
+
}
|
975
|
+
onBlur = _.bind(this._onBlur, this);
|
976
|
+
onFocus = _.bind(this._onFocus, this);
|
977
|
+
onKeydown = _.bind(this._onKeydown, this);
|
978
|
+
onInput = _.bind(this._onInput, this);
|
576
979
|
this.$hint = $(o.hint);
|
577
|
-
this.$input = $(o.input).on("blur.tt",
|
578
|
-
if (
|
579
|
-
this
|
980
|
+
this.$input = $(o.input).on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown);
|
981
|
+
if (this.$hint.length === 0) {
|
982
|
+
this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop;
|
983
|
+
}
|
984
|
+
if (!_.isMsie()) {
|
985
|
+
this.$input.on("input.tt", onInput);
|
580
986
|
} else {
|
581
987
|
this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) {
|
582
|
-
if (
|
988
|
+
if (specialKeyCodeMap[$e.which || $e.keyCode]) {
|
583
989
|
return;
|
584
990
|
}
|
585
|
-
|
991
|
+
_.defer(_.bind(that._onInput, that, $e));
|
586
992
|
});
|
587
993
|
}
|
588
994
|
this.query = this.$input.val();
|
589
995
|
this.$overflowHelper = buildOverflowHelper(this.$input);
|
590
996
|
}
|
591
|
-
|
592
|
-
|
997
|
+
Input.normalizeQuery = function(str) {
|
998
|
+
return (str || "").replace(/^\s*/g, "").replace(/\s{2,}/g, " ");
|
999
|
+
};
|
1000
|
+
_.mixin(Input.prototype, EventEmitter, {
|
1001
|
+
_onBlur: function onBlur() {
|
1002
|
+
this.resetInputValue();
|
1003
|
+
this.trigger("blurred");
|
1004
|
+
},
|
1005
|
+
_onFocus: function onFocus() {
|
593
1006
|
this.trigger("focused");
|
594
1007
|
},
|
595
|
-
|
596
|
-
|
1008
|
+
_onKeydown: function onKeydown($e) {
|
1009
|
+
var keyName = specialKeyCodeMap[$e.which || $e.keyCode];
|
1010
|
+
this._managePreventDefault(keyName, $e);
|
1011
|
+
if (keyName && this._shouldTrigger(keyName, $e)) {
|
1012
|
+
this.trigger(keyName + "Keyed", $e);
|
1013
|
+
}
|
597
1014
|
},
|
598
|
-
|
599
|
-
|
600
|
-
keyName && this.trigger(keyName + "Keyed", $e);
|
1015
|
+
_onInput: function onInput() {
|
1016
|
+
this._checkInputValue();
|
601
1017
|
},
|
602
|
-
|
603
|
-
var
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
1018
|
+
_managePreventDefault: function managePreventDefault(keyName, $e) {
|
1019
|
+
var preventDefault, hintValue, inputValue;
|
1020
|
+
switch (keyName) {
|
1021
|
+
case "tab":
|
1022
|
+
hintValue = this.getHint();
|
1023
|
+
inputValue = this.getInputValue();
|
1024
|
+
preventDefault = hintValue && hintValue !== inputValue && !withModifier($e);
|
1025
|
+
break;
|
1026
|
+
|
1027
|
+
case "up":
|
1028
|
+
case "down":
|
1029
|
+
preventDefault = !withModifier($e);
|
1030
|
+
break;
|
1031
|
+
|
1032
|
+
default:
|
1033
|
+
preventDefault = false;
|
612
1034
|
}
|
1035
|
+
preventDefault && $e.preventDefault();
|
613
1036
|
},
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
1037
|
+
_shouldTrigger: function shouldTrigger(keyName, $e) {
|
1038
|
+
var trigger;
|
1039
|
+
switch (keyName) {
|
1040
|
+
case "tab":
|
1041
|
+
trigger = !withModifier($e);
|
1042
|
+
break;
|
1043
|
+
|
1044
|
+
default:
|
1045
|
+
trigger = true;
|
1046
|
+
}
|
1047
|
+
return trigger;
|
1048
|
+
},
|
1049
|
+
_checkInputValue: function checkInputValue() {
|
1050
|
+
var inputValue, areEquivalent, hasDifferentWhitespace;
|
1051
|
+
inputValue = this.getInputValue();
|
1052
|
+
areEquivalent = areQueriesEquivalent(inputValue, this.query);
|
1053
|
+
hasDifferentWhitespace = areEquivalent ? this.query.length !== inputValue.length : false;
|
1054
|
+
this.query = inputValue;
|
1055
|
+
if (!areEquivalent) {
|
1056
|
+
this.trigger("queryChanged", this.query);
|
1057
|
+
} else if (hasDifferentWhitespace) {
|
1058
|
+
this.trigger("whitespaceChanged", this.query);
|
1059
|
+
}
|
618
1060
|
},
|
619
|
-
focus: function() {
|
1061
|
+
focus: function focus() {
|
620
1062
|
this.$input.focus();
|
621
1063
|
},
|
622
|
-
blur: function() {
|
1064
|
+
blur: function blur() {
|
623
1065
|
this.$input.blur();
|
624
1066
|
},
|
625
|
-
getQuery: function() {
|
1067
|
+
getQuery: function getQuery() {
|
626
1068
|
return this.query;
|
627
1069
|
},
|
628
|
-
setQuery: function(query) {
|
1070
|
+
setQuery: function setQuery(query) {
|
629
1071
|
this.query = query;
|
630
1072
|
},
|
631
|
-
getInputValue: function() {
|
1073
|
+
getInputValue: function getInputValue() {
|
632
1074
|
return this.$input.val();
|
633
1075
|
},
|
634
|
-
setInputValue: function(value, silent) {
|
1076
|
+
setInputValue: function setInputValue(value, silent) {
|
635
1077
|
this.$input.val(value);
|
636
|
-
|
1078
|
+
silent ? this.clearHint() : this._checkInputValue();
|
1079
|
+
},
|
1080
|
+
resetInputValue: function resetInputValue() {
|
1081
|
+
this.setInputValue(this.query, true);
|
637
1082
|
},
|
638
|
-
|
1083
|
+
getHint: function getHint() {
|
639
1084
|
return this.$hint.val();
|
640
1085
|
},
|
641
|
-
|
1086
|
+
setHint: function setHint(value) {
|
642
1087
|
this.$hint.val(value);
|
643
1088
|
},
|
644
|
-
|
1089
|
+
clearHint: function clearHint() {
|
1090
|
+
this.setHint("");
|
1091
|
+
},
|
1092
|
+
clearHintIfInvalid: function clearHintIfInvalid() {
|
1093
|
+
var val, hint, valIsPrefixOfHint, isValid;
|
1094
|
+
val = this.getInputValue();
|
1095
|
+
hint = this.getHint();
|
1096
|
+
valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0;
|
1097
|
+
isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow();
|
1098
|
+
!isValid && this.clearHint();
|
1099
|
+
},
|
1100
|
+
getLanguageDirection: function getLanguageDirection() {
|
645
1101
|
return (this.$input.css("direction") || "ltr").toLowerCase();
|
646
1102
|
},
|
647
|
-
|
1103
|
+
hasOverflow: function hasOverflow() {
|
1104
|
+
var constraint = this.$input.width() - 2;
|
648
1105
|
this.$overflowHelper.text(this.getInputValue());
|
649
|
-
return this.$overflowHelper.width()
|
1106
|
+
return this.$overflowHelper.width() >= constraint;
|
650
1107
|
},
|
651
1108
|
isCursorAtEnd: function() {
|
652
|
-
var valueLength
|
653
|
-
|
1109
|
+
var valueLength, selectionStart, range;
|
1110
|
+
valueLength = this.$input.val().length;
|
1111
|
+
selectionStart = this.$input[0].selectionStart;
|
1112
|
+
if (_.isNumber(selectionStart)) {
|
654
1113
|
return selectionStart === valueLength;
|
655
1114
|
} else if (document.selection) {
|
656
1115
|
range = document.selection.createRange();
|
@@ -658,15 +1117,19 @@
|
|
658
1117
|
return valueLength === range.text.length;
|
659
1118
|
}
|
660
1119
|
return true;
|
1120
|
+
},
|
1121
|
+
destroy: function destroy() {
|
1122
|
+
this.$hint.off(".tt");
|
1123
|
+
this.$input.off(".tt");
|
1124
|
+
this.$hint = this.$input = this.$overflowHelper = null;
|
661
1125
|
}
|
662
1126
|
});
|
663
|
-
return
|
1127
|
+
return Input;
|
664
1128
|
function buildOverflowHelper($input) {
|
665
|
-
return $("
|
1129
|
+
return $('<pre aria-hidden="true"></pre>').css({
|
666
1130
|
position: "absolute",
|
667
|
-
left: "-9999px",
|
668
1131
|
visibility: "hidden",
|
669
|
-
whiteSpace: "
|
1132
|
+
whiteSpace: "pre",
|
670
1133
|
fontFamily: $input.css("font-family"),
|
671
1134
|
fontSize: $input.css("font-size"),
|
672
1135
|
fontStyle: $input.css("font-style"),
|
@@ -679,461 +1142,641 @@
|
|
679
1142
|
textTransform: $input.css("text-transform")
|
680
1143
|
}).insertAfter($input);
|
681
1144
|
}
|
682
|
-
function
|
683
|
-
|
684
|
-
|
685
|
-
|
1145
|
+
function areQueriesEquivalent(a, b) {
|
1146
|
+
return Input.normalizeQuery(a) === Input.normalizeQuery(b);
|
1147
|
+
}
|
1148
|
+
function withModifier($e) {
|
1149
|
+
return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey;
|
686
1150
|
}
|
687
1151
|
}();
|
688
|
-
var
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
1152
|
+
var Dataset = function() {
|
1153
|
+
"use strict";
|
1154
|
+
var datasetKey = "ttDataset", valueKey = "ttValue", datumKey = "ttDatum";
|
1155
|
+
function Dataset(o) {
|
1156
|
+
o = o || {};
|
1157
|
+
o.templates = o.templates || {};
|
1158
|
+
if (!o.source) {
|
1159
|
+
$.error("missing source");
|
1160
|
+
}
|
1161
|
+
if (o.name && !isValidName(o.name)) {
|
1162
|
+
$.error("invalid dataset name: " + o.name);
|
1163
|
+
}
|
1164
|
+
this.query = null;
|
1165
|
+
this.highlight = !!o.highlight;
|
1166
|
+
this.name = o.name || _.getUniqueId();
|
1167
|
+
this.source = o.source;
|
1168
|
+
this.displayFn = getDisplayFn(o.display || o.displayKey);
|
1169
|
+
this.templates = getTemplates(o.templates, this.displayFn);
|
1170
|
+
this.$el = $(html.dataset.replace("%CLASS%", this.name));
|
1171
|
+
}
|
1172
|
+
Dataset.extractDatasetName = function extractDatasetName(el) {
|
1173
|
+
return $(el).data(datasetKey);
|
1174
|
+
};
|
1175
|
+
Dataset.extractValue = function extractDatum(el) {
|
1176
|
+
return $(el).data(valueKey);
|
1177
|
+
};
|
1178
|
+
Dataset.extractDatum = function extractDatum(el) {
|
1179
|
+
return $(el).data(datumKey);
|
1180
|
+
};
|
1181
|
+
_.mixin(Dataset.prototype, EventEmitter, {
|
1182
|
+
_render: function render(query, suggestions) {
|
1183
|
+
if (!this.$el) {
|
1184
|
+
return;
|
1185
|
+
}
|
1186
|
+
var that = this, hasSuggestions;
|
1187
|
+
this.$el.empty();
|
1188
|
+
hasSuggestions = suggestions && suggestions.length;
|
1189
|
+
if (!hasSuggestions && this.templates.empty) {
|
1190
|
+
this.$el.html(getEmptyHtml()).prepend(that.templates.header ? getHeaderHtml() : null).append(that.templates.footer ? getFooterHtml() : null);
|
1191
|
+
} else if (hasSuggestions) {
|
1192
|
+
this.$el.html(getSuggestionsHtml()).prepend(that.templates.header ? getHeaderHtml() : null).append(that.templates.footer ? getFooterHtml() : null);
|
1193
|
+
}
|
1194
|
+
this.trigger("rendered");
|
1195
|
+
function getEmptyHtml() {
|
1196
|
+
return that.templates.empty({
|
1197
|
+
query: query,
|
1198
|
+
isEmpty: true
|
1199
|
+
});
|
1200
|
+
}
|
1201
|
+
function getSuggestionsHtml() {
|
1202
|
+
var $suggestions, nodes;
|
1203
|
+
$suggestions = $(html.suggestions).css(css.suggestions);
|
1204
|
+
nodes = _.map(suggestions, getSuggestionNode);
|
1205
|
+
$suggestions.append.apply($suggestions, nodes);
|
1206
|
+
that.highlight && highlight({
|
1207
|
+
className: "tt-highlight",
|
1208
|
+
node: $suggestions[0],
|
1209
|
+
pattern: query
|
1210
|
+
});
|
1211
|
+
return $suggestions;
|
1212
|
+
function getSuggestionNode(suggestion) {
|
1213
|
+
var $el;
|
1214
|
+
$el = $(html.suggestion).append(that.templates.suggestion(suggestion)).data(datasetKey, that.name).data(valueKey, that.displayFn(suggestion)).data(datumKey, suggestion);
|
1215
|
+
$el.children().each(function() {
|
1216
|
+
$(this).css(css.suggestionChild);
|
1217
|
+
});
|
1218
|
+
return $el;
|
1219
|
+
}
|
1220
|
+
}
|
1221
|
+
function getHeaderHtml() {
|
1222
|
+
return that.templates.header({
|
1223
|
+
query: query,
|
1224
|
+
isEmpty: !hasSuggestions
|
1225
|
+
});
|
1226
|
+
}
|
1227
|
+
function getFooterHtml() {
|
1228
|
+
return that.templates.footer({
|
1229
|
+
query: query,
|
1230
|
+
isEmpty: !hasSuggestions
|
1231
|
+
});
|
1232
|
+
}
|
694
1233
|
},
|
695
|
-
|
696
|
-
|
697
|
-
cursor: "pointer"
|
1234
|
+
getRoot: function getRoot() {
|
1235
|
+
return this.$el;
|
698
1236
|
},
|
699
|
-
|
700
|
-
|
1237
|
+
update: function update(query) {
|
1238
|
+
var that = this;
|
1239
|
+
this.query = query;
|
1240
|
+
this.canceled = false;
|
1241
|
+
this.source(query, render);
|
1242
|
+
function render(suggestions) {
|
1243
|
+
if (!that.canceled && query === that.query) {
|
1244
|
+
that._render(query, suggestions);
|
1245
|
+
}
|
1246
|
+
}
|
1247
|
+
},
|
1248
|
+
cancel: function cancel() {
|
1249
|
+
this.canceled = true;
|
1250
|
+
},
|
1251
|
+
clear: function clear() {
|
1252
|
+
this.cancel();
|
1253
|
+
this.$el.empty();
|
1254
|
+
this.trigger("rendered");
|
1255
|
+
},
|
1256
|
+
isEmpty: function isEmpty() {
|
1257
|
+
return this.$el.is(":empty");
|
1258
|
+
},
|
1259
|
+
destroy: function destroy() {
|
1260
|
+
this.$el = null;
|
1261
|
+
}
|
1262
|
+
});
|
1263
|
+
return Dataset;
|
1264
|
+
function getDisplayFn(display) {
|
1265
|
+
display = display || "value";
|
1266
|
+
return _.isFunction(display) ? display : displayFn;
|
1267
|
+
function displayFn(obj) {
|
1268
|
+
return obj[display];
|
1269
|
+
}
|
1270
|
+
}
|
1271
|
+
function getTemplates(templates, displayFn) {
|
1272
|
+
return {
|
1273
|
+
empty: templates.empty && _.templatify(templates.empty),
|
1274
|
+
header: templates.header && _.templatify(templates.header),
|
1275
|
+
footer: templates.footer && _.templatify(templates.footer),
|
1276
|
+
suggestion: templates.suggestion || suggestionTemplate
|
1277
|
+
};
|
1278
|
+
function suggestionTemplate(context) {
|
1279
|
+
return "<p>" + displayFn(context) + "</p>";
|
1280
|
+
}
|
1281
|
+
}
|
1282
|
+
function isValidName(str) {
|
1283
|
+
return /^[_a-zA-Z0-9-]+$/.test(str);
|
1284
|
+
}
|
1285
|
+
}();
|
1286
|
+
var Dropdown = function() {
|
1287
|
+
"use strict";
|
1288
|
+
function Dropdown(o) {
|
1289
|
+
var that = this, onSuggestionClick, onSuggestionMouseEnter, onSuggestionMouseLeave;
|
1290
|
+
o = o || {};
|
1291
|
+
if (!o.menu) {
|
1292
|
+
$.error("menu is required");
|
701
1293
|
}
|
702
|
-
};
|
703
|
-
function DropdownView(o) {
|
704
|
-
utils.bindAll(this);
|
705
1294
|
this.isOpen = false;
|
706
1295
|
this.isEmpty = true;
|
707
|
-
this.
|
708
|
-
|
1296
|
+
this.datasets = _.map(o.datasets, initializeDataset);
|
1297
|
+
onSuggestionClick = _.bind(this._onSuggestionClick, this);
|
1298
|
+
onSuggestionMouseEnter = _.bind(this._onSuggestionMouseEnter, this);
|
1299
|
+
onSuggestionMouseLeave = _.bind(this._onSuggestionMouseLeave, this);
|
1300
|
+
this.$menu = $(o.menu).on("click.tt", ".tt-suggestion", onSuggestionClick).on("mouseenter.tt", ".tt-suggestion", onSuggestionMouseEnter).on("mouseleave.tt", ".tt-suggestion", onSuggestionMouseLeave);
|
1301
|
+
_.each(this.datasets, function(dataset) {
|
1302
|
+
that.$menu.append(dataset.getRoot());
|
1303
|
+
dataset.onSync("rendered", that._onRendered, that);
|
1304
|
+
});
|
709
1305
|
}
|
710
|
-
|
711
|
-
|
712
|
-
this.
|
713
|
-
},
|
714
|
-
|
715
|
-
this.
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
this.
|
720
|
-
|
1306
|
+
_.mixin(Dropdown.prototype, EventEmitter, {
|
1307
|
+
_onSuggestionClick: function onSuggestionClick($e) {
|
1308
|
+
this.trigger("suggestionClicked", $($e.currentTarget));
|
1309
|
+
},
|
1310
|
+
_onSuggestionMouseEnter: function onSuggestionMouseEnter($e) {
|
1311
|
+
this._removeCursor();
|
1312
|
+
this._setCursor($($e.currentTarget), true);
|
1313
|
+
},
|
1314
|
+
_onSuggestionMouseLeave: function onSuggestionMouseLeave() {
|
1315
|
+
this._removeCursor();
|
1316
|
+
},
|
1317
|
+
_onRendered: function onRendered() {
|
1318
|
+
this.isEmpty = _.every(this.datasets, isDatasetEmpty);
|
1319
|
+
this.isEmpty ? this._hide() : this.isOpen && this._show();
|
1320
|
+
this.trigger("datasetRendered");
|
1321
|
+
function isDatasetEmpty(dataset) {
|
1322
|
+
return dataset.isEmpty();
|
1323
|
+
}
|
721
1324
|
},
|
722
|
-
|
723
|
-
|
724
|
-
this.trigger("suggestionSelected", extractSuggestion($suggestion));
|
1325
|
+
_hide: function() {
|
1326
|
+
this.$menu.hide();
|
725
1327
|
},
|
726
1328
|
_show: function() {
|
727
1329
|
this.$menu.css("display", "block");
|
728
1330
|
},
|
729
|
-
|
730
|
-
this.$menu.
|
1331
|
+
_getSuggestions: function getSuggestions() {
|
1332
|
+
return this.$menu.find(".tt-suggestion");
|
1333
|
+
},
|
1334
|
+
_getCursor: function getCursor() {
|
1335
|
+
return this.$menu.find(".tt-cursor").first();
|
1336
|
+
},
|
1337
|
+
_setCursor: function setCursor($el, silent) {
|
1338
|
+
$el.first().addClass("tt-cursor");
|
1339
|
+
!silent && this.trigger("cursorMoved");
|
1340
|
+
},
|
1341
|
+
_removeCursor: function removeCursor() {
|
1342
|
+
this._getCursor().removeClass("tt-cursor");
|
731
1343
|
},
|
732
|
-
_moveCursor: function(increment) {
|
733
|
-
var $suggestions, $
|
734
|
-
if (!this.
|
1344
|
+
_moveCursor: function moveCursor(increment) {
|
1345
|
+
var $suggestions, $oldCursor, newCursorIndex, $newCursor;
|
1346
|
+
if (!this.isOpen) {
|
735
1347
|
return;
|
736
1348
|
}
|
1349
|
+
$oldCursor = this._getCursor();
|
737
1350
|
$suggestions = this._getSuggestions();
|
738
|
-
|
739
|
-
$
|
740
|
-
|
741
|
-
|
742
|
-
if (nextIndex === -1) {
|
1351
|
+
this._removeCursor();
|
1352
|
+
newCursorIndex = $suggestions.index($oldCursor) + increment;
|
1353
|
+
newCursorIndex = (newCursorIndex + 1) % ($suggestions.length + 1) - 1;
|
1354
|
+
if (newCursorIndex === -1) {
|
743
1355
|
this.trigger("cursorRemoved");
|
744
1356
|
return;
|
745
|
-
} else if (
|
746
|
-
|
1357
|
+
} else if (newCursorIndex < -1) {
|
1358
|
+
newCursorIndex = $suggestions.length - 1;
|
747
1359
|
}
|
748
|
-
$
|
749
|
-
this.
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
1360
|
+
this._setCursor($newCursor = $suggestions.eq(newCursorIndex));
|
1361
|
+
this._ensureVisible($newCursor);
|
1362
|
+
},
|
1363
|
+
_ensureVisible: function ensureVisible($el) {
|
1364
|
+
var elTop, elBottom, menuScrollTop, menuHeight;
|
1365
|
+
elTop = $el.position().top;
|
1366
|
+
elBottom = elTop + $el.outerHeight(true);
|
1367
|
+
menuScrollTop = this.$menu.scrollTop();
|
1368
|
+
menuHeight = this.$menu.height() + parseInt(this.$menu.css("paddingTop"), 10) + parseInt(this.$menu.css("paddingBottom"), 10);
|
757
1369
|
if (elTop < 0) {
|
758
1370
|
this.$menu.scrollTop(menuScrollTop + elTop);
|
759
1371
|
} else if (menuHeight < elBottom) {
|
760
1372
|
this.$menu.scrollTop(menuScrollTop + (elBottom - menuHeight));
|
761
1373
|
}
|
762
1374
|
},
|
763
|
-
|
764
|
-
this.$menu.off(".tt");
|
765
|
-
this.$menu = null;
|
766
|
-
},
|
767
|
-
isVisible: function() {
|
768
|
-
return this.isOpen && !this.isEmpty;
|
769
|
-
},
|
770
|
-
closeUnlessMouseIsOverDropdown: function() {
|
771
|
-
if (!this.isMouseOverDropdown) {
|
772
|
-
this.close();
|
773
|
-
}
|
774
|
-
},
|
775
|
-
close: function() {
|
1375
|
+
close: function close() {
|
776
1376
|
if (this.isOpen) {
|
777
1377
|
this.isOpen = false;
|
778
|
-
this.
|
1378
|
+
this._removeCursor();
|
779
1379
|
this._hide();
|
780
|
-
this.$menu.find(".tt-suggestions > .tt-suggestion").removeClass("tt-is-under-cursor");
|
781
1380
|
this.trigger("closed");
|
782
1381
|
}
|
783
1382
|
},
|
784
|
-
open: function() {
|
1383
|
+
open: function open() {
|
785
1384
|
if (!this.isOpen) {
|
786
1385
|
this.isOpen = true;
|
787
1386
|
!this.isEmpty && this._show();
|
788
1387
|
this.trigger("opened");
|
789
1388
|
}
|
790
1389
|
},
|
791
|
-
setLanguageDirection: function(dir) {
|
792
|
-
|
793
|
-
left: "0",
|
794
|
-
right: "auto"
|
795
|
-
}, rtlCss = {
|
796
|
-
left: "auto",
|
797
|
-
right: " 0"
|
798
|
-
};
|
799
|
-
dir === "ltr" ? this.$menu.css(ltrCss) : this.$menu.css(rtlCss);
|
1390
|
+
setLanguageDirection: function setLanguageDirection(dir) {
|
1391
|
+
this.$menu.css(dir === "ltr" ? css.ltr : css.rtl);
|
800
1392
|
},
|
801
|
-
moveCursorUp: function() {
|
1393
|
+
moveCursorUp: function moveCursorUp() {
|
802
1394
|
this._moveCursor(-1);
|
803
1395
|
},
|
804
|
-
moveCursorDown: function() {
|
1396
|
+
moveCursorDown: function moveCursorDown() {
|
805
1397
|
this._moveCursor(+1);
|
806
1398
|
},
|
807
|
-
|
808
|
-
var
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
renderSuggestions: function(dataset, suggestions) {
|
816
|
-
var datasetClassName = "tt-dataset-" + dataset.name, wrapper = '<div class="tt-suggestion">%body</div>', compiledHtml, $suggestionsList, $dataset = this.$menu.find("." + datasetClassName), elBuilder, fragment, $el;
|
817
|
-
if ($dataset.length === 0) {
|
818
|
-
$suggestionsList = $(html.suggestionsList).css(css.suggestionsList);
|
819
|
-
$dataset = $("<div></div>").addClass(datasetClassName).append(dataset.header).append($suggestionsList).append(dataset.footer).appendTo(this.$menu);
|
820
|
-
}
|
821
|
-
if (suggestions.length > 0) {
|
822
|
-
this.isEmpty = false;
|
823
|
-
this.isOpen && this._show();
|
824
|
-
elBuilder = document.createElement("div");
|
825
|
-
fragment = document.createDocumentFragment();
|
826
|
-
utils.each(suggestions, function(i, suggestion) {
|
827
|
-
suggestion.dataset = dataset.name;
|
828
|
-
compiledHtml = dataset.template(suggestion.datum);
|
829
|
-
elBuilder.innerHTML = wrapper.replace("%body", compiledHtml);
|
830
|
-
$el = $(elBuilder.firstChild).css(css.suggestion).data("suggestion", suggestion);
|
831
|
-
$el.children().each(function() {
|
832
|
-
$(this).css(css.suggestionChild);
|
833
|
-
});
|
834
|
-
fragment.appendChild($el[0]);
|
835
|
-
});
|
836
|
-
$dataset.show().find(".tt-suggestions").html(fragment);
|
837
|
-
} else {
|
838
|
-
this.clearSuggestions(dataset.name);
|
1399
|
+
getDatumForSuggestion: function getDatumForSuggestion($el) {
|
1400
|
+
var datum = null;
|
1401
|
+
if ($el.length) {
|
1402
|
+
datum = {
|
1403
|
+
raw: Dataset.extractDatum($el),
|
1404
|
+
value: Dataset.extractValue($el),
|
1405
|
+
datasetName: Dataset.extractDatasetName($el)
|
1406
|
+
};
|
839
1407
|
}
|
840
|
-
|
1408
|
+
return datum;
|
841
1409
|
},
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
1410
|
+
getDatumForCursor: function getDatumForCursor() {
|
1411
|
+
return this.getDatumForSuggestion(this._getCursor().first());
|
1412
|
+
},
|
1413
|
+
getDatumForTopSuggestion: function getDatumForTopSuggestion() {
|
1414
|
+
return this.getDatumForSuggestion(this._getSuggestions().first());
|
1415
|
+
},
|
1416
|
+
update: function update(query) {
|
1417
|
+
_.each(this.datasets, updateDataset);
|
1418
|
+
function updateDataset(dataset) {
|
1419
|
+
dataset.update(query);
|
849
1420
|
}
|
850
|
-
}
|
851
|
-
});
|
852
|
-
return DropdownView;
|
853
|
-
function extractSuggestion($el) {
|
854
|
-
return $el.data("suggestion");
|
855
|
-
}
|
856
|
-
}();
|
857
|
-
var TypeaheadView = function() {
|
858
|
-
var html = {
|
859
|
-
wrapper: '<span class="twitter-typeahead"></span>',
|
860
|
-
hint: '<input class="tt-hint" type="text" autocomplete="off" spellcheck="off" disabled>',
|
861
|
-
dropdown: '<span class="tt-dropdown-menu"></span>'
|
862
|
-
}, css = {
|
863
|
-
wrapper: {
|
864
|
-
position: "relative",
|
865
|
-
display: "inline-block"
|
866
1421
|
},
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
1422
|
+
empty: function empty() {
|
1423
|
+
_.each(this.datasets, clearDataset);
|
1424
|
+
this.isEmpty = true;
|
1425
|
+
function clearDataset(dataset) {
|
1426
|
+
dataset.clear();
|
1427
|
+
}
|
873
1428
|
},
|
874
|
-
|
875
|
-
|
876
|
-
verticalAlign: "top",
|
877
|
-
backgroundColor: "transparent"
|
1429
|
+
isVisible: function isVisible() {
|
1430
|
+
return this.isOpen && !this.isEmpty;
|
878
1431
|
},
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
1432
|
+
destroy: function destroy() {
|
1433
|
+
this.$menu.off(".tt");
|
1434
|
+
this.$menu = null;
|
1435
|
+
_.each(this.datasets, destroyDataset);
|
1436
|
+
function destroyDataset(dataset) {
|
1437
|
+
dataset.destroy();
|
1438
|
+
}
|
885
1439
|
}
|
886
|
-
};
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
});
|
891
|
-
}
|
892
|
-
if (utils.isMsie() && utils.isMsie() <= 7) {
|
893
|
-
utils.mixin(css.wrapper, {
|
894
|
-
display: "inline",
|
895
|
-
zoom: "1"
|
896
|
-
});
|
897
|
-
utils.mixin(css.query, {
|
898
|
-
marginTop: "-1px"
|
899
|
-
});
|
1440
|
+
});
|
1441
|
+
return Dropdown;
|
1442
|
+
function initializeDataset(oDataset) {
|
1443
|
+
return new Dataset(oDataset);
|
900
1444
|
}
|
901
|
-
|
1445
|
+
}();
|
1446
|
+
var Typeahead = function() {
|
1447
|
+
"use strict";
|
1448
|
+
var attrsKey = "ttAttrs";
|
1449
|
+
function Typeahead(o) {
|
902
1450
|
var $menu, $input, $hint;
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
this.
|
1451
|
+
o = o || {};
|
1452
|
+
if (!o.input) {
|
1453
|
+
$.error("missing input");
|
1454
|
+
}
|
1455
|
+
this.isActivated = false;
|
1456
|
+
this.autoselect = !!o.autoselect;
|
1457
|
+
this.minLength = _.isNumber(o.minLength) ? o.minLength : 1;
|
1458
|
+
this.$node = buildDom(o.input, o.withHint);
|
908
1459
|
$menu = this.$node.find(".tt-dropdown-menu");
|
909
|
-
$input = this.$node.find(".tt-
|
1460
|
+
$input = this.$node.find(".tt-input");
|
910
1461
|
$hint = this.$node.find(".tt-hint");
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
1462
|
+
$input.on("blur.tt", function($e) {
|
1463
|
+
var active, isActive, hasActive;
|
1464
|
+
active = document.activeElement;
|
1465
|
+
isActive = $menu.is(active);
|
1466
|
+
hasActive = $menu.has(active).length > 0;
|
1467
|
+
if (_.isMsie() && (isActive || hasActive)) {
|
1468
|
+
$e.preventDefault();
|
1469
|
+
$e.stopImmediatePropagation();
|
1470
|
+
_.defer(function() {
|
1471
|
+
$input.focus();
|
1472
|
+
});
|
1473
|
+
}
|
1474
|
+
});
|
1475
|
+
$menu.on("mousedown.tt", function($e) {
|
1476
|
+
$e.preventDefault();
|
1477
|
+
});
|
1478
|
+
this.eventBus = o.eventBus || new EventBus({
|
1479
|
+
el: $input
|
1480
|
+
});
|
1481
|
+
this.dropdown = new Dropdown({
|
1482
|
+
menu: $menu,
|
1483
|
+
datasets: o.datasets
|
1484
|
+
}).onSync("suggestionClicked", this._onSuggestionClicked, this).onSync("cursorMoved", this._onCursorMoved, this).onSync("cursorRemoved", this._onCursorRemoved, this).onSync("opened", this._onOpened, this).onSync("closed", this._onClosed, this).onAsync("datasetRendered", this._onDatasetRendered, this);
|
1485
|
+
this.input = new Input({
|
915
1486
|
input: $input,
|
916
1487
|
hint: $hint
|
917
|
-
}).
|
1488
|
+
}).onSync("focused", this._onFocused, this).onSync("blurred", this._onBlurred, this).onSync("enterKeyed", this._onEnterKeyed, this).onSync("tabKeyed", this._onTabKeyed, this).onSync("escKeyed", this._onEscKeyed, this).onSync("upKeyed", this._onUpKeyed, this).onSync("downKeyed", this._onDownKeyed, this).onSync("leftKeyed", this._onLeftKeyed, this).onSync("rightKeyed", this._onRightKeyed, this).onSync("queryChanged", this._onQueryChanged, this).onSync("whitespaceChanged", this._onWhitespaceChanged, this);
|
1489
|
+
this._setLanguageDirection();
|
918
1490
|
}
|
919
|
-
|
920
|
-
|
921
|
-
var
|
922
|
-
|
923
|
-
|
924
|
-
hint = this.inputView.getHintValue();
|
925
|
-
inputValue = this.inputView.getInputValue();
|
926
|
-
preventDefault = hint && hint !== inputValue;
|
927
|
-
break;
|
928
|
-
|
929
|
-
case "upKeyed":
|
930
|
-
case "downKeyed":
|
931
|
-
preventDefault = !$e.shiftKey && !$e.ctrlKey && !$e.metaKey;
|
932
|
-
break;
|
1491
|
+
_.mixin(Typeahead.prototype, {
|
1492
|
+
_onSuggestionClicked: function onSuggestionClicked(type, $el) {
|
1493
|
+
var datum;
|
1494
|
+
if (datum = this.dropdown.getDatumForSuggestion($el)) {
|
1495
|
+
this._select(datum);
|
933
1496
|
}
|
934
|
-
preventDefault && $e.preventDefault();
|
935
1497
|
},
|
936
|
-
|
937
|
-
var
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
1498
|
+
_onCursorMoved: function onCursorMoved() {
|
1499
|
+
var datum = this.dropdown.getDatumForCursor();
|
1500
|
+
this.input.setInputValue(datum.value, true);
|
1501
|
+
this.eventBus.trigger("cursorchanged", datum.raw, datum.datasetName);
|
1502
|
+
},
|
1503
|
+
_onCursorRemoved: function onCursorRemoved() {
|
1504
|
+
this.input.resetInputValue();
|
1505
|
+
this._updateHint();
|
1506
|
+
},
|
1507
|
+
_onDatasetRendered: function onDatasetRendered() {
|
1508
|
+
this._updateHint();
|
1509
|
+
},
|
1510
|
+
_onOpened: function onOpened() {
|
1511
|
+
this._updateHint();
|
1512
|
+
this.eventBus.trigger("opened");
|
1513
|
+
},
|
1514
|
+
_onClosed: function onClosed() {
|
1515
|
+
this.input.clearHint();
|
1516
|
+
this.eventBus.trigger("closed");
|
1517
|
+
},
|
1518
|
+
_onFocused: function onFocused() {
|
1519
|
+
this.isActivated = true;
|
1520
|
+
this.dropdown.open();
|
1521
|
+
},
|
1522
|
+
_onBlurred: function onBlurred() {
|
1523
|
+
this.isActivated = false;
|
1524
|
+
this.dropdown.empty();
|
1525
|
+
this.dropdown.close();
|
1526
|
+
},
|
1527
|
+
_onEnterKeyed: function onEnterKeyed(type, $e) {
|
1528
|
+
var cursorDatum, topSuggestionDatum;
|
1529
|
+
cursorDatum = this.dropdown.getDatumForCursor();
|
1530
|
+
topSuggestionDatum = this.dropdown.getDatumForTopSuggestion();
|
1531
|
+
if (cursorDatum) {
|
1532
|
+
this._select(cursorDatum);
|
1533
|
+
$e.preventDefault();
|
1534
|
+
} else if (this.autoselect && topSuggestionDatum) {
|
1535
|
+
this._select(topSuggestionDatum);
|
1536
|
+
$e.preventDefault();
|
942
1537
|
}
|
943
1538
|
},
|
944
|
-
|
945
|
-
var
|
946
|
-
if (
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
match = beginsWithQuery.exec(hint);
|
952
|
-
this.inputView.setHintValue(inputValue + (match ? match[1] : ""));
|
1539
|
+
_onTabKeyed: function onTabKeyed(type, $e) {
|
1540
|
+
var datum;
|
1541
|
+
if (datum = this.dropdown.getDatumForCursor()) {
|
1542
|
+
this._select(datum);
|
1543
|
+
$e.preventDefault();
|
1544
|
+
} else {
|
1545
|
+
this._autocomplete(true);
|
953
1546
|
}
|
954
1547
|
},
|
955
|
-
|
956
|
-
this.
|
1548
|
+
_onEscKeyed: function onEscKeyed() {
|
1549
|
+
this.dropdown.close();
|
1550
|
+
this.input.resetInputValue();
|
957
1551
|
},
|
958
|
-
|
959
|
-
this.
|
1552
|
+
_onUpKeyed: function onUpKeyed() {
|
1553
|
+
var query = this.input.getQuery();
|
1554
|
+
this.dropdown.isEmpty && query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.moveCursorUp();
|
1555
|
+
this.dropdown.open();
|
960
1556
|
},
|
961
|
-
|
962
|
-
this.
|
1557
|
+
_onDownKeyed: function onDownKeyed() {
|
1558
|
+
var query = this.input.getQuery();
|
1559
|
+
this.dropdown.isEmpty && query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.moveCursorDown();
|
1560
|
+
this.dropdown.open();
|
963
1561
|
},
|
964
|
-
|
965
|
-
|
966
|
-
this.inputView.setInputValue(suggestion.value, true);
|
1562
|
+
_onLeftKeyed: function onLeftKeyed() {
|
1563
|
+
this.dir === "rtl" && this._autocomplete();
|
967
1564
|
},
|
968
|
-
|
969
|
-
this.
|
1565
|
+
_onRightKeyed: function onRightKeyed() {
|
1566
|
+
this.dir === "ltr" && this._autocomplete();
|
970
1567
|
},
|
971
|
-
|
972
|
-
this.
|
1568
|
+
_onQueryChanged: function onQueryChanged(e, query) {
|
1569
|
+
this.input.clearHintIfInvalid();
|
1570
|
+
query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.empty();
|
1571
|
+
this.dropdown.open();
|
1572
|
+
this._setLanguageDirection();
|
973
1573
|
},
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
this.dropdownView[e.type === "upKeyed" ? "moveCursorUp" : "moveCursorDown"]();
|
978
|
-
}
|
1574
|
+
_onWhitespaceChanged: function onWhitespaceChanged() {
|
1575
|
+
this._updateHint();
|
1576
|
+
this.dropdown.open();
|
979
1577
|
},
|
980
|
-
|
981
|
-
var
|
982
|
-
if (
|
983
|
-
this.
|
984
|
-
|
985
|
-
|
986
|
-
this.eventBus.trigger("selected", suggestion.datum, suggestion.dataset);
|
1578
|
+
_setLanguageDirection: function setLanguageDirection() {
|
1579
|
+
var dir;
|
1580
|
+
if (this.dir !== (dir = this.input.getLanguageDirection())) {
|
1581
|
+
this.dir = dir;
|
1582
|
+
this.$node.css("direction", dir);
|
1583
|
+
this.dropdown.setLanguageDirection(dir);
|
987
1584
|
}
|
988
1585
|
},
|
989
|
-
|
990
|
-
var
|
991
|
-
|
992
|
-
|
1586
|
+
_updateHint: function updateHint() {
|
1587
|
+
var datum, val, query, escapedQuery, frontMatchRegEx, match;
|
1588
|
+
datum = this.dropdown.getDatumForTopSuggestion();
|
1589
|
+
if (datum && this.dropdown.isVisible() && !this.input.hasOverflow()) {
|
1590
|
+
val = this.input.getInputValue();
|
1591
|
+
query = Input.normalizeQuery(val);
|
1592
|
+
escapedQuery = _.escapeRegExChars(query);
|
1593
|
+
frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i");
|
1594
|
+
match = frontMatchRegEx.exec(datum.value);
|
1595
|
+
match ? this.input.setHint(val + match[1]) : this.input.clearHint();
|
1596
|
+
} else {
|
1597
|
+
this.input.clearHint();
|
993
1598
|
}
|
994
|
-
utils.each(this.datasets, function(i, dataset) {
|
995
|
-
dataset.getSuggestions(query, function(suggestions) {
|
996
|
-
if (query === that.inputView.getQuery()) {
|
997
|
-
that.dropdownView.renderSuggestions(dataset, suggestions);
|
998
|
-
}
|
999
|
-
});
|
1000
|
-
});
|
1001
1599
|
},
|
1002
|
-
_autocomplete: function(
|
1003
|
-
var
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1600
|
+
_autocomplete: function autocomplete(laxCursor) {
|
1601
|
+
var hint, query, isCursorAtEnd, datum;
|
1602
|
+
hint = this.input.getHint();
|
1603
|
+
query = this.input.getQuery();
|
1604
|
+
isCursorAtEnd = laxCursor || this.input.isCursorAtEnd();
|
1605
|
+
if (hint && query !== hint && isCursorAtEnd) {
|
1606
|
+
datum = this.dropdown.getDatumForTopSuggestion();
|
1607
|
+
datum && this.input.setInputValue(datum.value);
|
1608
|
+
this.eventBus.trigger("autocompleted", datum.raw, datum.datasetName);
|
1010
1609
|
}
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1610
|
+
},
|
1611
|
+
_select: function select(datum) {
|
1612
|
+
this.input.setQuery(datum.value);
|
1613
|
+
this.input.setInputValue(datum.value, true);
|
1614
|
+
this._setLanguageDirection();
|
1615
|
+
this.eventBus.trigger("selected", datum.raw, datum.datasetName);
|
1616
|
+
this.dropdown.close();
|
1617
|
+
_.defer(_.bind(this.dropdown.empty, this.dropdown));
|
1618
|
+
},
|
1619
|
+
open: function open() {
|
1620
|
+
this.dropdown.open();
|
1621
|
+
},
|
1622
|
+
close: function close() {
|
1623
|
+
this.dropdown.close();
|
1624
|
+
},
|
1625
|
+
setVal: function setVal(val) {
|
1626
|
+
val = _.toStr(val);
|
1627
|
+
if (this.isActivated) {
|
1628
|
+
this.input.setInputValue(val);
|
1629
|
+
} else {
|
1630
|
+
this.input.setQuery(val);
|
1631
|
+
this.input.setInputValue(val, true);
|
1017
1632
|
}
|
1633
|
+
this._setLanguageDirection();
|
1018
1634
|
},
|
1019
|
-
|
1020
|
-
this.
|
1635
|
+
getVal: function getVal() {
|
1636
|
+
return this.input.getQuery();
|
1021
1637
|
},
|
1022
|
-
destroy: function() {
|
1023
|
-
this.
|
1024
|
-
this.
|
1638
|
+
destroy: function destroy() {
|
1639
|
+
this.input.destroy();
|
1640
|
+
this.dropdown.destroy();
|
1025
1641
|
destroyDomStructure(this.$node);
|
1026
1642
|
this.$node = null;
|
1027
|
-
},
|
1028
|
-
setQuery: function(query) {
|
1029
|
-
this.inputView.setQuery(query);
|
1030
|
-
this.inputView.setInputValue(query);
|
1031
|
-
this._clearHint();
|
1032
|
-
this._clearSuggestions();
|
1033
|
-
this._getSuggestions();
|
1034
1643
|
}
|
1035
1644
|
});
|
1036
|
-
return
|
1037
|
-
function
|
1038
|
-
var $
|
1039
|
-
$
|
1040
|
-
$
|
1041
|
-
$
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
backgroundPosition: $input.css("background-position"),
|
1048
|
-
backgroundRepeat: $input.css("background-repeat"),
|
1049
|
-
backgroundSize: $input.css("background-size")
|
1645
|
+
return Typeahead;
|
1646
|
+
function buildDom(input, withHint) {
|
1647
|
+
var $input, $wrapper, $dropdown, $hint;
|
1648
|
+
$input = $(input);
|
1649
|
+
$wrapper = $(html.wrapper).css(css.wrapper);
|
1650
|
+
$dropdown = $(html.dropdown).css(css.dropdown);
|
1651
|
+
$hint = $input.clone().css(css.hint).css(getBackgroundStyles($input));
|
1652
|
+
$hint.val("").removeData().addClass("tt-hint").removeAttr("id name placeholder required").prop("readonly", true).attr({
|
1653
|
+
autocomplete: "off",
|
1654
|
+
spellcheck: "false",
|
1655
|
+
tabindex: -1
|
1050
1656
|
});
|
1051
|
-
$input.data(
|
1657
|
+
$input.data(attrsKey, {
|
1052
1658
|
dir: $input.attr("dir"),
|
1053
1659
|
autocomplete: $input.attr("autocomplete"),
|
1054
1660
|
spellcheck: $input.attr("spellcheck"),
|
1055
1661
|
style: $input.attr("style")
|
1056
1662
|
});
|
1057
|
-
$input.addClass("tt-
|
1663
|
+
$input.addClass("tt-input").attr({
|
1058
1664
|
autocomplete: "off",
|
1059
1665
|
spellcheck: false
|
1060
|
-
}).css(css.
|
1666
|
+
}).css(withHint ? css.input : css.inputWithNoHint);
|
1061
1667
|
try {
|
1062
1668
|
!$input.attr("dir") && $input.attr("dir", "auto");
|
1063
1669
|
} catch (e) {}
|
1064
|
-
return $input.wrap($wrapper).parent().prepend($hint).append($dropdown);
|
1670
|
+
return $input.wrap($wrapper).parent().prepend(withHint ? $hint : null).append($dropdown);
|
1671
|
+
}
|
1672
|
+
function getBackgroundStyles($el) {
|
1673
|
+
return {
|
1674
|
+
backgroundAttachment: $el.css("background-attachment"),
|
1675
|
+
backgroundClip: $el.css("background-clip"),
|
1676
|
+
backgroundColor: $el.css("background-color"),
|
1677
|
+
backgroundImage: $el.css("background-image"),
|
1678
|
+
backgroundOrigin: $el.css("background-origin"),
|
1679
|
+
backgroundPosition: $el.css("background-position"),
|
1680
|
+
backgroundRepeat: $el.css("background-repeat"),
|
1681
|
+
backgroundSize: $el.css("background-size")
|
1682
|
+
};
|
1065
1683
|
}
|
1066
1684
|
function destroyDomStructure($node) {
|
1067
|
-
var $input = $node.find(".tt-
|
1068
|
-
|
1069
|
-
|
1685
|
+
var $input = $node.find(".tt-input");
|
1686
|
+
_.each($input.data(attrsKey), function(val, key) {
|
1687
|
+
_.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val);
|
1070
1688
|
});
|
1071
|
-
$input.detach().removeData(
|
1689
|
+
$input.detach().removeData(attrsKey).removeClass("tt-input").insertAfter($node);
|
1072
1690
|
$node.remove();
|
1073
1691
|
}
|
1074
1692
|
}();
|
1075
1693
|
(function() {
|
1076
|
-
|
1694
|
+
"use strict";
|
1695
|
+
var old, typeaheadKey, methods;
|
1696
|
+
old = $.fn.typeahead;
|
1697
|
+
typeaheadKey = "ttTypeahead";
|
1077
1698
|
methods = {
|
1078
|
-
initialize: function(
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
if (o.name) {
|
1087
|
-
cache[o.name] = dataset;
|
1088
|
-
}
|
1089
|
-
return dataset;
|
1090
|
-
});
|
1091
|
-
return this.each(initialize);
|
1092
|
-
function initialize() {
|
1093
|
-
var $input = $(this), deferreds, eventBus = new EventBus({
|
1094
|
-
el: $input
|
1699
|
+
initialize: function initialize(o, datasets) {
|
1700
|
+
datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1);
|
1701
|
+
o = o || {};
|
1702
|
+
return this.each(attach);
|
1703
|
+
function attach() {
|
1704
|
+
var $input = $(this), eventBus, typeahead;
|
1705
|
+
_.each(datasets, function(d) {
|
1706
|
+
d.highlight = !!o.highlight;
|
1095
1707
|
});
|
1096
|
-
|
1097
|
-
return dataset.initialize();
|
1098
|
-
});
|
1099
|
-
$input.data(viewKey, new TypeaheadView({
|
1708
|
+
typeahead = new Typeahead({
|
1100
1709
|
input: $input,
|
1101
1710
|
eventBus: eventBus = new EventBus({
|
1102
1711
|
el: $input
|
1103
1712
|
}),
|
1713
|
+
withHint: _.isUndefined(o.hint) ? true : !!o.hint,
|
1714
|
+
minLength: o.minLength,
|
1715
|
+
autoselect: o.autoselect,
|
1104
1716
|
datasets: datasets
|
1105
|
-
}));
|
1106
|
-
$.when.apply($, deferreds).always(function() {
|
1107
|
-
utils.defer(function() {
|
1108
|
-
eventBus.trigger("initialized");
|
1109
|
-
});
|
1110
1717
|
});
|
1718
|
+
$input.data(typeaheadKey, typeahead);
|
1111
1719
|
}
|
1112
1720
|
},
|
1113
|
-
|
1114
|
-
return this.each(
|
1115
|
-
function
|
1116
|
-
var $
|
1117
|
-
if (
|
1118
|
-
|
1119
|
-
$this.removeData(viewKey);
|
1721
|
+
open: function open() {
|
1722
|
+
return this.each(openTypeahead);
|
1723
|
+
function openTypeahead() {
|
1724
|
+
var $input = $(this), typeahead;
|
1725
|
+
if (typeahead = $input.data(typeaheadKey)) {
|
1726
|
+
typeahead.open();
|
1120
1727
|
}
|
1121
1728
|
}
|
1122
1729
|
},
|
1123
|
-
|
1124
|
-
return this.each(
|
1125
|
-
function
|
1126
|
-
var
|
1127
|
-
|
1730
|
+
close: function close() {
|
1731
|
+
return this.each(closeTypeahead);
|
1732
|
+
function closeTypeahead() {
|
1733
|
+
var $input = $(this), typeahead;
|
1734
|
+
if (typeahead = $input.data(typeaheadKey)) {
|
1735
|
+
typeahead.close();
|
1736
|
+
}
|
1737
|
+
}
|
1738
|
+
},
|
1739
|
+
val: function val(newVal) {
|
1740
|
+
return !arguments.length ? getVal(this.first()) : this.each(setVal);
|
1741
|
+
function setVal() {
|
1742
|
+
var $input = $(this), typeahead;
|
1743
|
+
if (typeahead = $input.data(typeaheadKey)) {
|
1744
|
+
typeahead.setVal(newVal);
|
1745
|
+
}
|
1746
|
+
}
|
1747
|
+
function getVal($input) {
|
1748
|
+
var typeahead, query;
|
1749
|
+
if (typeahead = $input.data(typeaheadKey)) {
|
1750
|
+
query = typeahead.getVal();
|
1751
|
+
}
|
1752
|
+
return query;
|
1753
|
+
}
|
1754
|
+
},
|
1755
|
+
destroy: function destroy() {
|
1756
|
+
return this.each(unattach);
|
1757
|
+
function unattach() {
|
1758
|
+
var $input = $(this), typeahead;
|
1759
|
+
if (typeahead = $input.data(typeaheadKey)) {
|
1760
|
+
typeahead.destroy();
|
1761
|
+
$input.removeData(typeaheadKey);
|
1762
|
+
}
|
1128
1763
|
}
|
1129
1764
|
}
|
1130
1765
|
};
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1766
|
+
$.fn.typeahead = function(method) {
|
1767
|
+
var tts;
|
1768
|
+
if (methods[method] && method !== "initialize") {
|
1769
|
+
tts = this.filter(function() {
|
1770
|
+
return !!$(this).data(typeaheadKey);
|
1771
|
+
});
|
1772
|
+
return methods[method].apply(tts, [].slice.call(arguments, 1));
|
1134
1773
|
} else {
|
1135
1774
|
return methods.initialize.apply(this, arguments);
|
1136
1775
|
}
|
1137
1776
|
};
|
1777
|
+
$.fn.typeahead.noConflict = function noConflict() {
|
1778
|
+
$.fn.typeahead = old;
|
1779
|
+
return this;
|
1780
|
+
};
|
1138
1781
|
})();
|
1139
1782
|
})(window.jQuery);
|