flashgrid-ext 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,645 @@
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);