flashgrid-ext 1.0.4 → 1.0.5
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/README.md +0 -1
- data/lib/flashgrid/ext/version.rb +1 -1
- data/vendor/assets/javascripts/typeahead.js +364 -1120
- data/vendor/assets/stylesheets/typeahead.css.scss +2 -35
- metadata +3 -4
- data/vendor/assets/javascripts/typeahead_search.js +0 -645
@@ -4,38 +4,5 @@
|
|
4
4
|
|
5
5
|
/* #Typeahead
|
6
6
|
================================================== */
|
7
|
-
.typeahead
|
8
|
-
.typeahead
|
9
|
-
background-clip: padding-box;
|
10
|
-
background-color: rgba(246,247,249,1);
|
11
|
-
border-radius: 3px;
|
12
|
-
box-shadow: 0 1px 3px rgba(0,0,0,0.3);
|
13
|
-
color: rgba(71,74,84,1);
|
14
|
-
display: none;
|
15
|
-
float: left;
|
16
|
-
list-style: none;
|
17
|
-
margin: 2px 0 0;
|
18
|
-
min-width: 160px;
|
19
|
-
padding: 5px 0;
|
20
|
-
position: absolute;
|
21
|
-
top: 100%;
|
22
|
-
z-index: 1000;
|
23
|
-
}
|
24
|
-
.typeahead-suggestion {
|
25
|
-
clear: both;
|
26
|
-
color: rgba(71,74,84,1);
|
27
|
-
display: block;
|
28
|
-
font-size: 12px;
|
29
|
-
font-weight: bold;
|
30
|
-
line-height: 20px;
|
31
|
-
padding: 5px 15px;
|
32
|
-
text-decoration: none;
|
33
|
-
white-space: nowrap;
|
34
|
-
}
|
35
|
-
.typeahead-suggestion.typeahead-cursor { color: rgba(58,144,216,1); }
|
36
|
-
.typeahead-suggestion p {
|
37
|
-
font-size: 12px;
|
38
|
-
font-weight: bold;
|
39
|
-
line-height: 20px;
|
40
|
-
margin: 0;
|
41
|
-
}
|
7
|
+
.typeahead { z-index: 1051; }
|
8
|
+
.typeahead b { color: rgba(122,179,23,1); }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flashgrid-ext
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Juan Gomez
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -65,7 +65,6 @@ files:
|
|
65
65
|
- vendor/assets/javascripts/sort.js
|
66
66
|
- vendor/assets/javascripts/time_ago.js
|
67
67
|
- vendor/assets/javascripts/typeahead.js
|
68
|
-
- vendor/assets/javascripts/typeahead_search.js
|
69
68
|
- vendor/assets/stylesheets/animate.css.scss
|
70
69
|
- vendor/assets/stylesheets/calendar.css.scss
|
71
70
|
- vendor/assets/stylesheets/carousel.css.scss
|
@@ -93,7 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
93
92
|
version: '0'
|
94
93
|
requirements: []
|
95
94
|
rubyforge_project:
|
96
|
-
rubygems_version: 2.2.
|
95
|
+
rubygems_version: 2.2.2
|
97
96
|
signing_key:
|
98
97
|
specification_version: 4
|
99
98
|
summary: Flashgrid Extensions
|
@@ -1,645 +0,0 @@
|
|
1
|
-
(function($) {
|
2
|
-
var _ = {
|
3
|
-
isMsie: function() {
|
4
|
-
return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false;
|
5
|
-
},
|
6
|
-
isBlankString: function(str) {
|
7
|
-
return !str || /^\s*$/.test(str);
|
8
|
-
},
|
9
|
-
escapeRegExChars: function(str) {
|
10
|
-
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
11
|
-
},
|
12
|
-
isString: function(obj) {
|
13
|
-
return typeof obj === "string";
|
14
|
-
},
|
15
|
-
isNumber: function(obj) {
|
16
|
-
return typeof obj === "number";
|
17
|
-
},
|
18
|
-
isArray: $.isArray,
|
19
|
-
isFunction: $.isFunction,
|
20
|
-
isObject: $.isPlainObject,
|
21
|
-
isUndefined: function(obj) {
|
22
|
-
return typeof obj === "undefined";
|
23
|
-
},
|
24
|
-
bind: $.proxy,
|
25
|
-
each: function(collection, cb) {
|
26
|
-
$.each(collection, reverseArgs);
|
27
|
-
function reverseArgs(index, value) {
|
28
|
-
return cb(value, index);
|
29
|
-
}
|
30
|
-
},
|
31
|
-
map: $.map,
|
32
|
-
filter: $.grep,
|
33
|
-
every: function(obj, test) {
|
34
|
-
var result = true;
|
35
|
-
if (!obj) {
|
36
|
-
return result;
|
37
|
-
}
|
38
|
-
$.each(obj, function(key, val) {
|
39
|
-
if (!(result = test.call(null, val, key, obj))) {
|
40
|
-
return false;
|
41
|
-
}
|
42
|
-
});
|
43
|
-
return !!result;
|
44
|
-
},
|
45
|
-
some: function(obj, test) {
|
46
|
-
var result = false;
|
47
|
-
if (!obj) {
|
48
|
-
return result;
|
49
|
-
}
|
50
|
-
$.each(obj, function(key, val) {
|
51
|
-
if (result = test.call(null, val, key, obj)) {
|
52
|
-
return false;
|
53
|
-
}
|
54
|
-
});
|
55
|
-
return !!result;
|
56
|
-
},
|
57
|
-
mixin: $.extend,
|
58
|
-
getUniqueId: function() {
|
59
|
-
var counter = 0;
|
60
|
-
return function() {
|
61
|
-
return counter++;
|
62
|
-
};
|
63
|
-
}(),
|
64
|
-
templatify: function templatify(obj) {
|
65
|
-
return $.isFunction(obj) ? obj : template;
|
66
|
-
function template() {
|
67
|
-
return String(obj);
|
68
|
-
}
|
69
|
-
},
|
70
|
-
defer: function(fn) {
|
71
|
-
setTimeout(fn, 0);
|
72
|
-
},
|
73
|
-
debounce: function(func, wait, immediate) {
|
74
|
-
var timeout, result;
|
75
|
-
return function() {
|
76
|
-
var context = this, args = arguments, later, callNow;
|
77
|
-
later = function() {
|
78
|
-
timeout = null;
|
79
|
-
if (!immediate) {
|
80
|
-
result = func.apply(context, args);
|
81
|
-
}
|
82
|
-
};
|
83
|
-
callNow = immediate && !timeout;
|
84
|
-
clearTimeout(timeout);
|
85
|
-
timeout = setTimeout(later, wait);
|
86
|
-
if (callNow) {
|
87
|
-
result = func.apply(context, args);
|
88
|
-
}
|
89
|
-
return result;
|
90
|
-
};
|
91
|
-
},
|
92
|
-
throttle: function(func, wait) {
|
93
|
-
var context, args, timeout, result, previous, later;
|
94
|
-
previous = 0;
|
95
|
-
later = function() {
|
96
|
-
previous = new Date();
|
97
|
-
timeout = null;
|
98
|
-
result = func.apply(context, args);
|
99
|
-
};
|
100
|
-
return function() {
|
101
|
-
var now = new Date(), remaining = wait - (now - previous);
|
102
|
-
context = this;
|
103
|
-
args = arguments;
|
104
|
-
if (remaining <= 0) {
|
105
|
-
clearTimeout(timeout);
|
106
|
-
timeout = null;
|
107
|
-
previous = now;
|
108
|
-
result = func.apply(context, args);
|
109
|
-
} else if (!timeout) {
|
110
|
-
timeout = setTimeout(later, remaining);
|
111
|
-
}
|
112
|
-
return result;
|
113
|
-
};
|
114
|
-
},
|
115
|
-
noop: function() {}
|
116
|
-
};
|
117
|
-
var VERSION = "0.10.1";
|
118
|
-
var LruCache = function(root, undefined) {
|
119
|
-
function LruCache(maxSize) {
|
120
|
-
this.maxSize = maxSize || 100;
|
121
|
-
this.size = 0;
|
122
|
-
this.hash = {};
|
123
|
-
this.list = new List();
|
124
|
-
}
|
125
|
-
_.mixin(LruCache.prototype, {
|
126
|
-
set: function set(key, val) {
|
127
|
-
var tailItem = this.list.tail, node;
|
128
|
-
if (this.size >= this.maxSize) {
|
129
|
-
this.list.remove(tailItem);
|
130
|
-
delete this.hash[tailItem.key];
|
131
|
-
}
|
132
|
-
if (node = this.hash[key]) {
|
133
|
-
node.val = val;
|
134
|
-
this.list.moveToFront(node);
|
135
|
-
} else {
|
136
|
-
node = new Node(key, val);
|
137
|
-
this.list.add(node);
|
138
|
-
this.hash[key] = node;
|
139
|
-
this.size++;
|
140
|
-
}
|
141
|
-
},
|
142
|
-
get: function get(key) {
|
143
|
-
var node = this.hash[key];
|
144
|
-
if (node) {
|
145
|
-
this.list.moveToFront(node);
|
146
|
-
return node.val;
|
147
|
-
}
|
148
|
-
}
|
149
|
-
});
|
150
|
-
function List() {
|
151
|
-
this.head = this.tail = null;
|
152
|
-
}
|
153
|
-
_.mixin(List.prototype, {
|
154
|
-
add: function add(node) {
|
155
|
-
if (this.head) {
|
156
|
-
node.next = this.head;
|
157
|
-
this.head.prev = node;
|
158
|
-
}
|
159
|
-
this.head = node;
|
160
|
-
this.tail = this.tail || node;
|
161
|
-
},
|
162
|
-
remove: function remove(node) {
|
163
|
-
node.prev ? node.prev.next = node.next : this.head = node.next;
|
164
|
-
node.next ? node.next.prev = node.prev : this.tail = node.prev;
|
165
|
-
},
|
166
|
-
moveToFront: function(node) {
|
167
|
-
this.remove(node);
|
168
|
-
this.add(node);
|
169
|
-
}
|
170
|
-
});
|
171
|
-
function Node(key, val) {
|
172
|
-
this.key = key;
|
173
|
-
this.val = val;
|
174
|
-
this.prev = this.next = null;
|
175
|
-
}
|
176
|
-
return LruCache;
|
177
|
-
}(this);
|
178
|
-
var PersistentStorage = function() {
|
179
|
-
var ls, methods;
|
180
|
-
try {
|
181
|
-
ls = window.localStorage;
|
182
|
-
ls.setItem("~~~", "!");
|
183
|
-
ls.removeItem("~~~");
|
184
|
-
} catch (err) {
|
185
|
-
ls = null;
|
186
|
-
}
|
187
|
-
function PersistentStorage(namespace) {
|
188
|
-
this.prefix = [ "__", namespace, "__" ].join("");
|
189
|
-
this.ttlKey = "__ttl__";
|
190
|
-
this.keyMatcher = new RegExp("^" + this.prefix);
|
191
|
-
}
|
192
|
-
if (ls && window.JSON) {
|
193
|
-
methods = {
|
194
|
-
_prefix: function(key) {
|
195
|
-
return this.prefix + key;
|
196
|
-
},
|
197
|
-
_ttlKey: function(key) {
|
198
|
-
return this._prefix(key) + this.ttlKey;
|
199
|
-
},
|
200
|
-
get: function(key) {
|
201
|
-
if (this.isExpired(key)) {
|
202
|
-
this.remove(key);
|
203
|
-
}
|
204
|
-
return decode(ls.getItem(this._prefix(key)));
|
205
|
-
},
|
206
|
-
set: function(key, val, ttl) {
|
207
|
-
if (_.isNumber(ttl)) {
|
208
|
-
ls.setItem(this._ttlKey(key), encode(now() + ttl));
|
209
|
-
} else {
|
210
|
-
ls.removeItem(this._ttlKey(key));
|
211
|
-
}
|
212
|
-
return ls.setItem(this._prefix(key), encode(val));
|
213
|
-
},
|
214
|
-
remove: function(key) {
|
215
|
-
ls.removeItem(this._ttlKey(key));
|
216
|
-
ls.removeItem(this._prefix(key));
|
217
|
-
return this;
|
218
|
-
},
|
219
|
-
clear: function() {
|
220
|
-
var i, key, keys = [], len = ls.length;
|
221
|
-
for (i = 0; i < len; i++) {
|
222
|
-
if ((key = ls.key(i)).match(this.keyMatcher)) {
|
223
|
-
keys.push(key.replace(this.keyMatcher, ""));
|
224
|
-
}
|
225
|
-
}
|
226
|
-
for (i = keys.length; i--; ) {
|
227
|
-
this.remove(keys[i]);
|
228
|
-
}
|
229
|
-
return this;
|
230
|
-
},
|
231
|
-
isExpired: function(key) {
|
232
|
-
var ttl = decode(ls.getItem(this._ttlKey(key)));
|
233
|
-
return _.isNumber(ttl) && now() > ttl ? true : false;
|
234
|
-
}
|
235
|
-
};
|
236
|
-
} else {
|
237
|
-
methods = {
|
238
|
-
get: _.noop,
|
239
|
-
set: _.noop,
|
240
|
-
remove: _.noop,
|
241
|
-
clear: _.noop,
|
242
|
-
isExpired: _.noop
|
243
|
-
};
|
244
|
-
}
|
245
|
-
_.mixin(PersistentStorage.prototype, methods);
|
246
|
-
return PersistentStorage;
|
247
|
-
function now() {
|
248
|
-
return new Date().getTime();
|
249
|
-
}
|
250
|
-
function encode(val) {
|
251
|
-
return JSON.stringify(_.isUndefined(val) ? null : val);
|
252
|
-
}
|
253
|
-
function decode(val) {
|
254
|
-
return JSON.parse(val);
|
255
|
-
}
|
256
|
-
}();
|
257
|
-
var Transport = function() {
|
258
|
-
var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, requestCache = new LruCache(10);
|
259
|
-
function Transport(o) {
|
260
|
-
o = o || {};
|
261
|
-
this._send = o.transport ? callbackToDeferred(o.transport) : $.ajax;
|
262
|
-
this._get = o.rateLimiter ? o.rateLimiter(this._get) : this._get;
|
263
|
-
}
|
264
|
-
Transport.setMaxPendingRequests = function setMaxPendingRequests(num) {
|
265
|
-
maxPendingRequests = num;
|
266
|
-
};
|
267
|
-
Transport.resetCache = function clearCache() {
|
268
|
-
requestCache = new LruCache(10);
|
269
|
-
};
|
270
|
-
_.mixin(Transport.prototype, {
|
271
|
-
_get: function(url, o, cb) {
|
272
|
-
var that = this, jqXhr;
|
273
|
-
if (jqXhr = pendingRequests[url]) {
|
274
|
-
jqXhr.done(done);
|
275
|
-
} else if (pendingRequestsCount < maxPendingRequests) {
|
276
|
-
pendingRequestsCount++;
|
277
|
-
pendingRequests[url] = this._send(url, o).done(done).always(always);
|
278
|
-
} else {
|
279
|
-
this.onDeckRequestArgs = [].slice.call(arguments, 0);
|
280
|
-
}
|
281
|
-
function done(resp) {
|
282
|
-
cb && cb(resp);
|
283
|
-
requestCache.set(url, resp);
|
284
|
-
}
|
285
|
-
function always() {
|
286
|
-
pendingRequestsCount--;
|
287
|
-
delete pendingRequests[url];
|
288
|
-
if (that.onDeckRequestArgs) {
|
289
|
-
that._get.apply(that, that.onDeckRequestArgs);
|
290
|
-
that.onDeckRequestArgs = null;
|
291
|
-
}
|
292
|
-
}
|
293
|
-
},
|
294
|
-
get: function(url, o, cb) {
|
295
|
-
var that = this, resp;
|
296
|
-
if (_.isFunction(o)) {
|
297
|
-
cb = o;
|
298
|
-
o = {};
|
299
|
-
}
|
300
|
-
if (resp = requestCache.get(url)) {
|
301
|
-
_.defer(function() {
|
302
|
-
cb && cb(resp);
|
303
|
-
});
|
304
|
-
} else {
|
305
|
-
this._get(url, o, cb);
|
306
|
-
}
|
307
|
-
return !!resp;
|
308
|
-
}
|
309
|
-
});
|
310
|
-
return Transport;
|
311
|
-
function callbackToDeferred(fn) {
|
312
|
-
return function customSendWrapper(url, o) {
|
313
|
-
var deferred = $.Deferred();
|
314
|
-
fn(url, o, onSuccess, onError);
|
315
|
-
return deferred;
|
316
|
-
function onSuccess(resp) {
|
317
|
-
_.defer(function() {
|
318
|
-
deferred.resolve(resp);
|
319
|
-
});
|
320
|
-
}
|
321
|
-
function onError(err) {
|
322
|
-
_.defer(function() {
|
323
|
-
deferred.reject(err);
|
324
|
-
});
|
325
|
-
}
|
326
|
-
};
|
327
|
-
}
|
328
|
-
}();
|
329
|
-
var SearchIndex = function() {
|
330
|
-
function SearchIndex(o) {
|
331
|
-
o = o || {};
|
332
|
-
if (!o.datumTokenizer || !o.queryTokenizer) {
|
333
|
-
$.error("datumTokenizer and queryTokenizer are both required");
|
334
|
-
}
|
335
|
-
this.datumTokenizer = o.datumTokenizer;
|
336
|
-
this.queryTokenizer = o.queryTokenizer;
|
337
|
-
this.datums = [];
|
338
|
-
this.trie = newNode();
|
339
|
-
}
|
340
|
-
_.mixin(SearchIndex.prototype, {
|
341
|
-
bootstrap: function bootstrap(o) {
|
342
|
-
this.datums = o.datums;
|
343
|
-
this.trie = o.trie;
|
344
|
-
},
|
345
|
-
add: function(data) {
|
346
|
-
var that = this;
|
347
|
-
data = _.isArray(data) ? data : [ data ];
|
348
|
-
_.each(data, function(datum) {
|
349
|
-
var id, tokens;
|
350
|
-
id = that.datums.push(datum) - 1;
|
351
|
-
tokens = normalizeTokens(that.datumTokenizer(datum));
|
352
|
-
_.each(tokens, function(token) {
|
353
|
-
var node, chars, ch, ids;
|
354
|
-
node = that.trie;
|
355
|
-
chars = token.split("");
|
356
|
-
while (ch = chars.shift()) {
|
357
|
-
node = node.children[ch] || (node.children[ch] = newNode());
|
358
|
-
node.ids.push(id);
|
359
|
-
}
|
360
|
-
});
|
361
|
-
});
|
362
|
-
},
|
363
|
-
get: function get(query) {
|
364
|
-
var that = this, tokens, matches;
|
365
|
-
tokens = normalizeTokens(this.queryTokenizer(query));
|
366
|
-
_.each(tokens, function(token) {
|
367
|
-
var node, chars, ch, ids;
|
368
|
-
if (matches && matches.length === 0) {
|
369
|
-
return false;
|
370
|
-
}
|
371
|
-
node = that.trie;
|
372
|
-
chars = token.split("");
|
373
|
-
while (node && (ch = chars.shift())) {
|
374
|
-
node = node.children[ch];
|
375
|
-
}
|
376
|
-
if (node && chars.length === 0) {
|
377
|
-
ids = node.ids.slice(0);
|
378
|
-
matches = matches ? getIntersection(matches, ids) : ids;
|
379
|
-
} else {
|
380
|
-
matches = [];
|
381
|
-
return false;
|
382
|
-
}
|
383
|
-
});
|
384
|
-
return matches ? _.map(unique(matches), function(id) {
|
385
|
-
return that.datums[id];
|
386
|
-
}) : [];
|
387
|
-
},
|
388
|
-
serialize: function serialize() {
|
389
|
-
return {
|
390
|
-
datums: this.datums,
|
391
|
-
trie: this.trie
|
392
|
-
};
|
393
|
-
}
|
394
|
-
});
|
395
|
-
return SearchIndex;
|
396
|
-
function normalizeTokens(tokens) {
|
397
|
-
tokens = _.filter(tokens, function(token) {
|
398
|
-
return !!token;
|
399
|
-
});
|
400
|
-
tokens = _.map(tokens, function(token) {
|
401
|
-
return token.toLowerCase();
|
402
|
-
});
|
403
|
-
return tokens;
|
404
|
-
}
|
405
|
-
function newNode() {
|
406
|
-
return {
|
407
|
-
ids: [],
|
408
|
-
children: {}
|
409
|
-
};
|
410
|
-
}
|
411
|
-
function unique(array) {
|
412
|
-
var seen = {}, uniques = [];
|
413
|
-
for (var i = 0; i < array.length; i++) {
|
414
|
-
if (!seen[array[i]]) {
|
415
|
-
seen[array[i]] = true;
|
416
|
-
uniques.push(array[i]);
|
417
|
-
}
|
418
|
-
}
|
419
|
-
return uniques;
|
420
|
-
}
|
421
|
-
function getIntersection(arrayA, arrayB) {
|
422
|
-
var ai = 0, bi = 0, intersection = [];
|
423
|
-
arrayA = arrayA.sort(compare);
|
424
|
-
arrayB = arrayB.sort(compare);
|
425
|
-
while (ai < arrayA.length && bi < arrayB.length) {
|
426
|
-
if (arrayA[ai] < arrayB[bi]) {
|
427
|
-
ai++;
|
428
|
-
} else if (arrayA[ai] > arrayB[bi]) {
|
429
|
-
bi++;
|
430
|
-
} else {
|
431
|
-
intersection.push(arrayA[ai]);
|
432
|
-
ai++;
|
433
|
-
bi++;
|
434
|
-
}
|
435
|
-
}
|
436
|
-
return intersection;
|
437
|
-
function compare(a, b) {
|
438
|
-
return a - b;
|
439
|
-
}
|
440
|
-
}
|
441
|
-
}();
|
442
|
-
var oParser = function() {
|
443
|
-
return {
|
444
|
-
local: getLocal,
|
445
|
-
prefetch: getPrefetch,
|
446
|
-
remote: getRemote
|
447
|
-
};
|
448
|
-
function getLocal(o) {
|
449
|
-
var local = o.local || null;
|
450
|
-
if (_.isFunction(local)) {
|
451
|
-
local = local.call(null);
|
452
|
-
}
|
453
|
-
return local;
|
454
|
-
}
|
455
|
-
function getPrefetch(o) {
|
456
|
-
var prefetch, defaults;
|
457
|
-
defaults = {
|
458
|
-
url: null,
|
459
|
-
thumbprint: "",
|
460
|
-
ttl: 24 * 60 * 60 * 1e3,
|
461
|
-
filter: null,
|
462
|
-
ajax: {}
|
463
|
-
};
|
464
|
-
if (prefetch = o.prefetch || null) {
|
465
|
-
prefetch = _.isString(prefetch) ? {
|
466
|
-
url: prefetch
|
467
|
-
} : prefetch;
|
468
|
-
prefetch = _.mixin(defaults, prefetch);
|
469
|
-
prefetch.thumbprint = VERSION + prefetch.thumbprint;
|
470
|
-
prefetch.ajax.type = prefetch.ajax.type || "GET";
|
471
|
-
prefetch.ajax.dataType = prefetch.ajax.dataType || "json";
|
472
|
-
!prefetch.url && $.error("prefetch requires url to be set");
|
473
|
-
}
|
474
|
-
return prefetch;
|
475
|
-
}
|
476
|
-
function getRemote(o) {
|
477
|
-
var remote, defaults;
|
478
|
-
defaults = {
|
479
|
-
url: null,
|
480
|
-
wildcard: "%QUERY",
|
481
|
-
replace: null,
|
482
|
-
rateLimitBy: "debounce",
|
483
|
-
rateLimitWait: 300,
|
484
|
-
send: null,
|
485
|
-
filter: null,
|
486
|
-
ajax: {}
|
487
|
-
};
|
488
|
-
if (remote = o.remote || null) {
|
489
|
-
remote = _.isString(remote) ? {
|
490
|
-
url: remote
|
491
|
-
} : remote;
|
492
|
-
remote = _.mixin(defaults, remote);
|
493
|
-
remote.rateLimiter = /^throttle$/i.test(remote.rateLimitBy) ? byThrottle(remote.rateLimitWait) : byDebounce(remote.rateLimitWait);
|
494
|
-
remote.ajax.type = remote.ajax.type || "GET";
|
495
|
-
remote.ajax.dataType = remote.ajax.dataType || "json";
|
496
|
-
delete remote.rateLimitBy;
|
497
|
-
delete remote.rateLimitWait;
|
498
|
-
!remote.url && $.error("remote requires url to be set");
|
499
|
-
}
|
500
|
-
return remote;
|
501
|
-
function byDebounce(wait) {
|
502
|
-
return function(fn) {
|
503
|
-
return _.debounce(fn, wait);
|
504
|
-
};
|
505
|
-
}
|
506
|
-
function byThrottle(wait) {
|
507
|
-
return function(fn) {
|
508
|
-
return _.throttle(fn, wait);
|
509
|
-
};
|
510
|
-
}
|
511
|
-
}
|
512
|
-
}();
|
513
|
-
var Bloodhound = window.Bloodhound = function() {
|
514
|
-
var keys;
|
515
|
-
keys = {
|
516
|
-
data: "data",
|
517
|
-
protocol: "protocol",
|
518
|
-
thumbprint: "thumbprint"
|
519
|
-
};
|
520
|
-
function Bloodhound(o) {
|
521
|
-
if (!o || !o.local && !o.prefetch && !o.remote) {
|
522
|
-
$.error("one of local, prefetch, or remote is required");
|
523
|
-
}
|
524
|
-
this.limit = o.limit || 5;
|
525
|
-
this.sorter = getSorter(o.sorter);
|
526
|
-
this.dupDetector = o.dupDetector || ignoreDuplicates;
|
527
|
-
this.local = oParser.local(o);
|
528
|
-
this.prefetch = oParser.prefetch(o);
|
529
|
-
this.remote = oParser.remote(o);
|
530
|
-
this.cacheKey = this.prefetch ? this.prefetch.cacheKey || this.prefetch.url : null;
|
531
|
-
this.index = new SearchIndex({
|
532
|
-
datumTokenizer: o.datumTokenizer,
|
533
|
-
queryTokenizer: o.queryTokenizer
|
534
|
-
});
|
535
|
-
this.storage = this.cacheKey ? new PersistentStorage(this.cacheKey) : null;
|
536
|
-
}
|
537
|
-
Bloodhound.tokenizers = {
|
538
|
-
whitespace: function whitespaceTokenizer(s) {
|
539
|
-
return s.split(/\s+/);
|
540
|
-
},
|
541
|
-
nonword: function nonwordTokenizer(s) {
|
542
|
-
return s.split(/\W+/);
|
543
|
-
}
|
544
|
-
};
|
545
|
-
_.mixin(Bloodhound.prototype, {
|
546
|
-
_loadPrefetch: function loadPrefetch(o) {
|
547
|
-
var that = this, serialized, deferred;
|
548
|
-
if (serialized = this._readFromStorage(o.thumbprint)) {
|
549
|
-
this.index.bootstrap(serialized);
|
550
|
-
deferred = $.Deferred().resolve();
|
551
|
-
} else {
|
552
|
-
deferred = $.ajax(o.url, o.ajax).done(handlePrefetchResponse);
|
553
|
-
}
|
554
|
-
return deferred;
|
555
|
-
function handlePrefetchResponse(resp) {
|
556
|
-
var filtered;
|
557
|
-
filtered = o.filter ? o.filter(resp) : resp;
|
558
|
-
that.add(filtered);
|
559
|
-
that._saveToStorage(that.index.serialize(), o.thumbprint, o.ttl);
|
560
|
-
}
|
561
|
-
},
|
562
|
-
_getFromRemote: function getFromRemote(query, cb) {
|
563
|
-
var that = this, url, uriEncodedQuery;
|
564
|
-
query = query || "";
|
565
|
-
uriEncodedQuery = encodeURIComponent(query);
|
566
|
-
url = this.remote.replace ? this.remote.replace(this.remote.url, query) : this.remote.url.replace(this.remote.wildcard, uriEncodedQuery);
|
567
|
-
return this.transport.get(url, this.remote.ajax, handleRemoteResponse);
|
568
|
-
function handleRemoteResponse(resp) {
|
569
|
-
var filtered = that.remote.filter ? that.remote.filter(resp) : resp;
|
570
|
-
cb(filtered);
|
571
|
-
}
|
572
|
-
},
|
573
|
-
_saveToStorage: function saveToStorage(data, thumbprint, ttl) {
|
574
|
-
if (this.storage) {
|
575
|
-
this.storage.set(keys.data, data, ttl);
|
576
|
-
this.storage.set(keys.protocol, location.protocol, ttl);
|
577
|
-
this.storage.set(keys.thumbprint, thumbprint, ttl);
|
578
|
-
}
|
579
|
-
},
|
580
|
-
_readFromStorage: function readFromStorage(thumbprint) {
|
581
|
-
var stored = {}, isExpired;
|
582
|
-
if (this.storage) {
|
583
|
-
stored.data = this.storage.get(keys.data);
|
584
|
-
stored.protocol = this.storage.get(keys.protocol);
|
585
|
-
stored.thumbprint = this.storage.get(keys.thumbprint);
|
586
|
-
}
|
587
|
-
isExpired = stored.thumbprint !== thumbprint || stored.protocol !== location.protocol;
|
588
|
-
return stored.data && !isExpired ? stored.data : null;
|
589
|
-
},
|
590
|
-
initialize: function initialize() {
|
591
|
-
var that = this, deferred;
|
592
|
-
deferred = this.prefetch ? this._loadPrefetch(this.prefetch) : $.Deferred().resolve();
|
593
|
-
this.local && deferred.done(addLocalToIndex);
|
594
|
-
this.transport = this.remote ? new Transport(this.remote) : null;
|
595
|
-
this.initialize = function initialize() {
|
596
|
-
return deferred.promise();
|
597
|
-
};
|
598
|
-
return deferred.promise();
|
599
|
-
function addLocalToIndex() {
|
600
|
-
that.add(that.local);
|
601
|
-
}
|
602
|
-
},
|
603
|
-
add: function add(data) {
|
604
|
-
this.index.add(data);
|
605
|
-
},
|
606
|
-
get: function get(query, cb) {
|
607
|
-
var that = this, matches, cacheHit = false;
|
608
|
-
matches = this.index.get(query);
|
609
|
-
matches = this.sorter(matches).slice(0, this.limit);
|
610
|
-
if (matches.length < this.limit && this.transport) {
|
611
|
-
cacheHit = this._getFromRemote(query, returnRemoteMatches);
|
612
|
-
}
|
613
|
-
!cacheHit && cb && cb(matches);
|
614
|
-
function returnRemoteMatches(remoteMatches) {
|
615
|
-
var matchesWithBackfill = matches.slice(0);
|
616
|
-
_.each(remoteMatches, function(remoteMatch) {
|
617
|
-
var isDuplicate;
|
618
|
-
isDuplicate = _.some(matchesWithBackfill, function(match) {
|
619
|
-
return that.dupDetector(remoteMatch, match);
|
620
|
-
});
|
621
|
-
!isDuplicate && matchesWithBackfill.push(remoteMatch);
|
622
|
-
return matchesWithBackfill.length < that.limit;
|
623
|
-
});
|
624
|
-
cb && cb(that.sorter(matchesWithBackfill));
|
625
|
-
}
|
626
|
-
},
|
627
|
-
ttAdapter: function ttAdapter() {
|
628
|
-
return _.bind(this.get, this);
|
629
|
-
}
|
630
|
-
});
|
631
|
-
return Bloodhound;
|
632
|
-
function getSorter(sortFn) {
|
633
|
-
return _.isFunction(sortFn) ? sort : noSort;
|
634
|
-
function sort(array) {
|
635
|
-
return array.sort(sortFn);
|
636
|
-
}
|
637
|
-
function noSort(array) {
|
638
|
-
return array;
|
639
|
-
}
|
640
|
-
}
|
641
|
-
function ignoreDuplicates() {
|
642
|
-
return false;
|
643
|
-
}
|
644
|
-
}();
|
645
|
-
})(window.jQuery);
|