Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
14217 anikendra 1
/*!
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
 */
6
 
7
(function($) {
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);
40
                }
41
            },
42
            map: $.map,
43
            filter: $.grep,
44
            every: function(obj, test) {
45
                var result = true;
46
                if (!obj) {
47
                    return result;
48
                }
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;
60
                }
61
                $.each(obj, function(key, val) {
62
                    if (result = test.call(null, val, key, obj)) {
63
                        return false;
64
                    }
65
                });
66
                return !!result;
67
            },
68
            mixin: $.extend,
69
            getUniqueId: function() {
70
                var counter = 0;
71
                return function() {
72
                    return counter++;
73
                };
74
            }(),
75
            templatify: function templatify(obj) {
76
                return $.isFunction(obj) ? obj : template;
77
                function template() {
78
                    return String(obj);
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);
92
                        }
93
                    };
94
                    callNow = immediate && !timeout;
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();
108
                    timeout = null;
109
                    result = func.apply(context, args);
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";
132
        return {
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];
176
                }
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++;
185
                }
186
            },
187
            get: function get(key) {
188
                var node = this.hash[key];
189
                if (node) {
190
                    this.list.moveToFront(node);
191
                    return node.val;
192
                }
193
            },
194
            reset: function reset() {
195
                this.size = 0;
196
                this.hash = {};
197
                this.list = new List();
198
            }
199
        });
200
        function List() {
201
            this.head = this.tail = null;
202
        }
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);
219
            }
220
        });
221
        function Node(key, val) {
222
            this.key = key;
223
            this.val = val;
224
            this.prev = this.next = null;
225
        }
226
        return LruCache;
227
    }();
228
    var PersistentStorage = function() {
229
        "use strict";
230
        var ls, methods;
231
        try {
232
            ls = window.localStorage;
233
            ls.setItem("~~~", "!");
234
            ls.removeItem("~~~");
235
        } catch (err) {
236
            ls = null;
237
        }
238
        function PersistentStorage(namespace) {
239
            this.prefix = [ "__", namespace, "__" ].join("");
240
            this.ttlKey = "__ttl__";
241
            this.keyMatcher = new RegExp("^" + _.escapeRegExChars(this.prefix));
242
        }
243
        if (ls && window.JSON) {
244
            methods = {
245
                _prefix: function(key) {
246
                    return this.prefix + key;
247
                },
248
                _ttlKey: function(key) {
249
                    return this._prefix(key) + this.ttlKey;
250
                },
251
                get: function(key) {
252
                    if (this.isExpired(key)) {
253
                        this.remove(key);
254
                    }
255
                    return decode(ls.getItem(this._prefix(key)));
256
                },
257
                set: function(key, val, ttl) {
258
                    if (_.isNumber(ttl)) {
259
                        ls.setItem(this._ttlKey(key), encode(now() + ttl));
260
                    } else {
261
                        ls.removeItem(this._ttlKey(key));
262
                    }
263
                    return ls.setItem(this._prefix(key), encode(val));
264
                },
265
                remove: function(key) {
266
                    ls.removeItem(this._ttlKey(key));
267
                    ls.removeItem(this._prefix(key));
268
                    return this;
269
                },
270
                clear: function() {
271
                    var i, key, keys = [], len = ls.length;
272
                    for (i = 0; i < len; i++) {
273
                        if ((key = ls.key(i)).match(this.keyMatcher)) {
274
                            keys.push(key.replace(this.keyMatcher, ""));
275
                        }
276
                    }
277
                    for (i = keys.length; i--; ) {
278
                        this.remove(keys[i]);
279
                    }
280
                    return this;
281
                },
282
                isExpired: function(key) {
283
                    var ttl = decode(ls.getItem(this._ttlKey(key)));
284
                    return _.isNumber(ttl) && now() > ttl ? true : false;
285
                }
286
            };
287
        } else {
288
            methods = {
289
                get: _.noop,
290
                set: _.noop,
291
                remove: _.noop,
292
                clear: _.noop,
293
                isExpired: _.noop
294
            };
295
        }
296
        _.mixin(PersistentStorage.prototype, methods);
297
        return PersistentStorage;
298
        function now() {
299
            return new Date().getTime();
300
        }
301
        function encode(val) {
302
            return JSON.stringify(_.isUndefined(val) ? null : val);
303
        }
304
        function decode(val) {
305
            return JSON.parse(val);
306
        }
307
    }();
308
    var Transport = function() {
309
        "use strict";
310
        var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, sharedCache = new LruCache(10);
311
        function Transport(o) {
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;
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);
336
                } else {
337
                    this.onDeckRequestArgs = [].slice.call(arguments, 0);
338
                }
339
                function done(resp) {
340
                    cb && cb(null, resp);
341
                    that._cache.set(url, resp);
342
                }
343
                function fail() {
344
                    cb && cb(true);
345
                }
346
                function always() {
347
                    pendingRequestsCount--;
348
                    delete pendingRequests[url];
349
                    if (that.onDeckRequestArgs) {
350
                        that._get.apply(that, that.onDeckRequestArgs);
351
                        that.onDeckRequestArgs = null;
352
                    }
353
                }
354
            },
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);
366
                    });
367
                } else {
368
                    this._get(url, o, cb);
369
                }
370
                return !!resp;
371
            },
372
            cancel: function() {
373
                this.cancelled = true;
374
            }
375
        });
376
        return Transport;
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
            };
393
        }
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();
405
        }
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
            }
511
        }
512
    }();
513
    var oParser = function() {
514
        "use strict";
515
        return {
516
            local: getLocal,
517
            prefetch: getPrefetch,
518
            remote: getRemote
519
        };
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");
541
            }
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) {
594
                $.error("one of local, prefetch, or remote is required");
595
            }
596
            this.limit = o.limit || 5;
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;
608
        }
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);
619
                    deferred = $.Deferred().resolve();
620
                } else {
621
                    deferred = $.ajax(o.url, o.ajax).done(handlePrefetchResponse);
622
                }
623
                return deferred;
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);
628
                }
629
            },
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);
641
                }
642
            },
643
            _cancelLastRemoteRequest: function cancelLastRemoteRequest() {
644
                this.transport && this.transport.cancel();
645
            },
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);
651
                }
652
            },
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);
667
                this.transport = this.remote ? new Transport(this.remote) : null;
668
                return this.initPromise = deferred.promise();
669
                function addLocalToIndex() {
670
                    that.add(_.isFunction(local) ? local() : local);
671
                }
672
            },
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);
686
                }
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);
693
                        });
694
                        !isDuplicate && matchesWithBackfill.push(remoteMatch);
695
                        return matchesWithBackfill.length < that.limit;
696
                    });
697
                    cb && cb(that.sorter(matchesWithBackfill));
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);
711
            }
712
        });
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
})(window.jQuery);