]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/cache/cache.js
Release 6.5.0
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / cache / cache.js
1 /*
2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 3.3.0
6 build: 3167
7 */
8 YUI.add('cache-base', function(Y) {
9
10 /**
11  * The Cache utility provides a common configurable interface for components to
12  * cache and retrieve data from a local JavaScript struct.
13  *
14  * @module cache
15  */
16 var LANG = Y.Lang,
17     isDate = Y.Lang.isDate,
18
19 /**
20  * Base class for the YUI Cache utility.
21  * @class Cache
22  * @extends Base
23  * @constructor
24  */
25 Cache = function() {
26     Cache.superclass.constructor.apply(this, arguments);
27 };
28
29     /////////////////////////////////////////////////////////////////////////////
30     //
31     // Cache static properties
32     //
33     /////////////////////////////////////////////////////////////////////////////
34 Y.mix(Cache, {
35     /**
36      * Class name.
37      *
38      * @property NAME
39      * @type String
40      * @static
41      * @final
42      * @value "cache"
43      */
44     NAME: "cache",
45
46
47     ATTRS: {
48         /////////////////////////////////////////////////////////////////////////////
49         //
50         // Cache Attributes
51         //
52         /////////////////////////////////////////////////////////////////////////////
53
54         /**
55         * @attribute max
56         * @description Maximum number of entries the Cache can hold.
57         * Set to 0 to turn off caching.
58         * @type Number
59         * @default 0
60         */
61         max: {
62             value: 0,
63             setter: "_setMax"
64         },
65
66         /**
67         * @attribute size
68         * @description Number of entries currently cached.
69         * @type Number
70         */
71         size: {
72             readOnly: true,
73             getter: "_getSize"
74         },
75
76         /**
77         * @attribute uniqueKeys
78         * @description Validate uniqueness of stored keys. Default is false and
79         * is more performant.
80         * @type Boolean
81         */
82         uniqueKeys: {
83             value: false
84         },
85
86         /**
87         * @attribute expires
88         * @description Absolute Date when data expires or
89         * relative number of milliseconds. Zero disables expiration.
90         * @type Date | Number
91         * @default 0
92         */
93         expires: {
94             value: 0,
95             validator: function(v) {
96                 return Y.Lang.isDate(v) || (Y.Lang.isNumber(v) && v >= 0);
97             }
98         },
99
100         /**
101          * @attribute entries
102          * @description Cached entries.
103          * @type Array
104          */
105         entries: {
106             readOnly: true,
107             getter: "_getEntries"
108         }
109     }
110 });
111
112 Y.extend(Cache, Y.Base, {
113     /////////////////////////////////////////////////////////////////////////////
114     //
115     // Cache private properties
116     //
117     /////////////////////////////////////////////////////////////////////////////
118
119     /**
120      * Array of request/response objects indexed chronologically.
121      *
122      * @property _entries
123      * @type Object[]
124      * @private
125      */
126     _entries: null,
127
128     /////////////////////////////////////////////////////////////////////////////
129     //
130     // Cache private methods
131     //
132     /////////////////////////////////////////////////////////////////////////////
133
134     /**
135     * @method initializer
136     * @description Internal init() handler.
137     * @param config {Object} Config object.
138     * @private
139     */
140     initializer: function(config) {
141
142         /**
143         * @event add
144         * @description Fired when an entry is added.
145         * @param e {Event.Facade} Event Facade with the following properties:
146          * <dl>
147          * <dt>entry (Object)</dt> <dd>The cached entry.</dd>
148          * </dl>
149         * @preventable _defAddFn
150         */
151         this.publish("add", {defaultFn: this._defAddFn});
152
153         /**
154         * @event flush
155         * @description Fired when the cache is flushed.
156         * @param e {Event.Facade} Event Facade object.
157         * @preventable _defFlushFn
158         */
159         this.publish("flush", {defaultFn: this._defFlushFn});
160
161         /**
162         * @event request
163         * @description Fired when an entry is requested from the cache.
164         * @param e {Event.Facade} Event Facade with the following properties:
165         * <dl>
166         * <dt>request (Object)</dt> <dd>The request object.</dd>
167         * </dl>
168         */
169
170         /**
171         * @event retrieve
172         * @description Fired when an entry is retrieved from the cache.
173         * @param e {Event.Facade} Event Facade with the following properties:
174         * <dl>
175         * <dt>entry (Object)</dt> <dd>The retrieved entry.</dd>
176         * </dl>
177         */
178
179         // Initialize internal values
180         this._entries = [];
181     },
182
183     /**
184     * @method destructor
185     * @description Internal destroy() handler.
186     * @private
187     */
188     destructor: function() {
189         this._entries = [];
190     },
191
192     /////////////////////////////////////////////////////////////////////////////
193     //
194     // Cache protected methods
195     //
196     /////////////////////////////////////////////////////////////////////////////
197
198     /**
199      * Sets max.
200      *
201      * @method _setMax
202      * @protected
203      */
204     _setMax: function(value) {
205         // If the cache is full, make room by removing stalest element (index=0)
206         var entries = this._entries;
207         if(value > 0) {
208             if(entries) {
209                 while(entries.length > value) {
210                     entries.shift();
211                 }
212             }
213         }
214         else {
215             value = 0;
216             this._entries = [];
217         }
218         return value;
219     },
220
221     /**
222      * Gets size.
223      *
224      * @method _getSize
225      * @protected
226      */
227     _getSize: function() {
228         return this._entries.length;
229     },
230
231     /**
232      * Gets all entries.
233      *
234      * @method _getEntries
235      * @protected
236      */
237     _getEntries: function() {
238         return this._entries;
239     },
240
241
242     /**
243      * Adds entry to cache.
244      *
245      * @method _defAddFn
246      * @param e {Event.Facade} Event Facade with the following properties:
247      * <dl>
248      * <dt>entry (Object)</dt> <dd>The cached entry.</dd>
249      * </dl>
250      * @protected
251      */
252     _defAddFn: function(e) {
253         var entries = this._entries,
254             max = this.get("max"),
255             entry = e.entry;
256
257         if(this.get("uniqueKeys") && (this.retrieve(e.entry.request))) {
258             entries.shift();
259         }
260
261
262         // If the cache at or over capacity, make room by removing stalest element (index=0)
263         while(max && entries.length>=max) {
264             entries.shift();
265         }
266
267         // Add entry to cache in the newest position, at the end of the array
268         entries[entries.length] = entry;
269     },
270
271     /**
272      * Flushes cache.
273      *
274      * @method _defFlushFn
275      * @param e {Event.Facade} Event Facade object.
276      * @protected
277      */
278     _defFlushFn: function(e) {
279         this._entries = [];
280     },
281
282     /**
283      * Default overridable method compares current request with given cache entry.
284      * Returns true if current request matches the cached request, otherwise
285      * false. Implementers should override this method to customize the
286      * cache-matching algorithm.
287      *
288      * @method _isMatch
289      * @param request {Object} Request object.
290      * @param entry {Object} Cached entry.
291      * @return {Boolean} True if current request matches given cached request, false otherwise.
292      * @protected
293      */
294     _isMatch: function(request, entry) {
295         if(!entry.expires || new Date() < entry.expires) {
296             return (request === entry.request);
297         }
298         return false;
299     },
300
301     /////////////////////////////////////////////////////////////////////////////
302     //
303     // Cache public methods
304     //
305     /////////////////////////////////////////////////////////////////////////////
306
307     /**
308      * Adds a new entry to the cache of the format
309      * {request:request, response:response, cached:cached, expires:expires}.
310      * If cache is full, evicts the stalest entry before adding the new one.
311      *
312      * @method add
313      * @param request {Object} Request value.
314      * @param response {Object} Response value.
315      */
316     add: function(request, response) {
317         var expires = this.get("expires");
318         if(this.get("initialized") && ((this.get("max") === null) || this.get("max") > 0) &&
319                 (LANG.isValue(request) || LANG.isNull(request) || LANG.isUndefined(request))) {
320             this.fire("add", {entry: {
321                 request:request,
322                 response:response,
323                 cached: new Date(),
324                 expires: isDate(expires) ? expires :
325             (expires ? new Date(new Date().getTime() + this.get("expires")) : null)
326             }});
327         }
328         else {
329         }
330     },
331
332     /**
333      * Flushes cache.
334      *
335      * @method flush
336      */
337     flush: function() {
338         this.fire("flush");
339     },
340
341     /**
342      * Retrieves cached object for given request, if available, and refreshes
343      * entry in the cache. Returns null if there is no cache match.
344      *
345      * @method retrieve
346      * @param request {Object} Request object.
347      * @return {Object} Cached object with the properties request and response, or null.
348      */
349     retrieve: function(request) {
350         // If cache is enabled...
351         var entries = this._entries,
352             length = entries.length,
353             entry = null,
354             i = length-1;
355
356         if((length > 0) && ((this.get("max") === null) || (this.get("max") > 0))) {
357             this.fire("request", {request: request});
358
359             // Loop through each cached entry starting from the newest
360             for(; i >= 0; i--) {
361                 entry = entries[i];
362
363                 // Execute matching function
364                 if(this._isMatch(request, entry)) {
365                     this.fire("retrieve", {entry: entry});
366
367                     // Refresh the position of the cache hit
368                     if(i < length-1) {
369                         // Remove element from its original location
370                         entries.splice(i,1);
371                         // Add as newest
372                         entries[entries.length] = entry;
373                     }
374
375                     return entry;
376                 }
377             }
378         }
379         return null;
380     }
381 });
382
383 Y.Cache = Cache;
384
385
386
387 }, '3.3.0' ,{requires:['base']});
388
389 YUI.add('cache-offline', function(Y) {
390
391 /**
392  * Extends Cache utility with offline functionality.
393  * @class CacheOffline
394  * @extends Cache
395  * @constructor
396  */
397 function CacheOffline() {
398     CacheOffline.superclass.constructor.apply(this, arguments);
399 }
400
401 var localStorage = null,
402     JSON = Y.JSON;
403
404 // Bug 2529572
405 try {
406     localStorage = Y.config.win.localStorage;
407 }
408 catch(e) {
409 }
410
411 /////////////////////////////////////////////////////////////////////////////
412 //
413 // CacheOffline events
414 //
415 /////////////////////////////////////////////////////////////////////////////
416
417 /**
418 * @event error
419 * @description Fired when an entry could not be added, most likely due to
420 * exceeded browser quota.
421 * <dl>
422 * <dt>error (Object)</dt> <dd>The error object.</dd>
423 * </dl>
424 */
425
426 /////////////////////////////////////////////////////////////////////////////
427 //
428 // CacheOffline static
429 //
430 /////////////////////////////////////////////////////////////////////////////
431 Y.mix(CacheOffline, {
432     /**
433      * Class name.
434      *
435      * @property NAME
436      * @type String
437      * @static
438      * @final
439      * @value "cacheOffline"
440      */
441     NAME: "cacheOffline",
442
443     ATTRS: {
444         /////////////////////////////////////////////////////////////////////////////
445         //
446         // CacheOffline Attributes
447         //
448         /////////////////////////////////////////////////////////////////////////////
449
450         /**
451         * @attribute sandbox
452         * @description A string that must be passed in via the constructor.
453         * This identifier is used to sandbox one cache instance's entries
454         * from another. Calling the cache instance's flush and length methods
455         * or get("entries") will apply to only these sandboxed entries.
456         * @type String
457         * @default "default"
458         * @initOnly
459         */
460         sandbox: {
461             value: "default",
462             writeOnce: "initOnly"
463         },
464
465         /**
466         * @attribute expires
467         * @description Absolute Date when data expires or
468         * relative number of milliseconds. Zero disables expiration.
469         * @type Date | Number
470         * @default 86400000 (one day)
471         */
472         expires: {
473             value: 86400000
474         },
475
476         /**
477         * @attribute max
478         * @description Disabled.
479         * @readOnly
480         * @default null
481         */
482         max: {
483             value: null,
484             readOnly: true
485         },
486
487         /**
488         * @attribute uniqueKeys
489         * @description Always true for CacheOffline.
490         * @readOnly
491         * @default true
492         */
493         uniqueKeys: {
494             value: true,
495             readOnly: true,
496             setter: function() {
497                 return true;
498             }
499         }
500     },
501
502     /**
503      * Removes all items from all sandboxes. Useful if localStorage has
504      * exceeded quota. Only supported on browsers that implement HTML 5
505      * localStorage.
506      *
507      * @method flushAll
508      * @static
509      */
510     flushAll: function() {
511         var store = localStorage, key;
512         if(store) {
513             if(store.clear) {
514                 store.clear();
515             }
516             // FF2.x and FF3.0.x
517             else {
518                 for (key in store) {
519                     if (store.hasOwnProperty(key)) {
520                         store.removeItem(key);
521                         delete store[key];
522                     }
523                 }
524             }
525         }
526         else {
527         }
528     }
529 });
530
531 Y.extend(CacheOffline, Y.Cache, localStorage ? {
532 /////////////////////////////////////////////////////////////////////////////
533 //
534 // Offline is supported
535 //
536 /////////////////////////////////////////////////////////////////////////////
537
538     /////////////////////////////////////////////////////////////////////////////
539     //
540     // CacheOffline protected methods
541     //
542     /////////////////////////////////////////////////////////////////////////////
543     /**
544      * Always return null.
545      *
546      * @method _setMax
547      * @protected
548      */
549     _setMax: function(value) {
550         return null;
551     },
552
553     /**
554      * Gets size.
555      *
556      * @method _getSize
557      * @protected
558      */
559     _getSize: function() {
560         var count = 0,
561             i=0,
562             l=localStorage.length;
563         for(; i<l; ++i) {
564             // Match sandbox id
565             if(localStorage.key(i).indexOf(this.get("sandbox")) === 0) {
566                 count++;
567             }
568         }
569         return count;
570     },
571
572     /**
573      * Gets all entries.
574      *
575      * @method _getEntries
576      * @protected
577      */
578     _getEntries: function() {
579         var entries = [],
580             i=0,
581             l=localStorage.length,
582             sandbox = this.get("sandbox");
583         for(; i<l; ++i) {
584             // Match sandbox id
585             if(localStorage.key(i).indexOf(sandbox) === 0) {
586                 entries[i] = JSON.parse(localStorage.key(i).substring(sandbox.length));
587             }
588         }
589         return entries;
590     },
591
592     /**
593      * Adds entry to cache.
594      *
595      * @method _defAddFn
596      * @param e {Event.Facade} Event Facade with the following properties:
597      * <dl>
598      * <dt>entry (Object)</dt> <dd>The cached entry.</dd>
599      * </dl>
600      * @protected
601      */
602     _defAddFn: function(e) {
603         var entry = e.entry,
604             request = entry.request,
605             cached = entry.cached,
606             expires = entry.expires;
607
608         // Convert Dates to msecs on the way into localStorage
609         entry.cached = cached.getTime();
610         entry.expires = expires ? expires.getTime() : expires;
611
612         try {
613             localStorage.setItem(this.get("sandbox")+JSON.stringify({"request":request}), JSON.stringify(entry));
614         }
615         catch(error) {
616             this.fire("error", {error:error});
617         }
618     },
619
620     /**
621      * Flushes cache.
622      *
623      * @method _defFlushFn
624      * @param e {Event.Facade} Event Facade object.
625      * @protected
626      */
627     _defFlushFn: function(e) {
628         var key,
629             i=localStorage.length-1;
630         for(; i>-1; --i) {
631             // Match sandbox id
632             key = localStorage.key(i);
633             if(key.indexOf(this.get("sandbox")) === 0) {
634                 localStorage.removeItem(key);
635             }
636         }
637     },
638
639     /////////////////////////////////////////////////////////////////////////////
640     //
641     // CacheOffline public methods
642     //
643     /////////////////////////////////////////////////////////////////////////////
644     /**
645      * Adds a new entry to the cache of the format
646      * {request:request, response:response, cached:cached, expires: expires}.
647      *
648      * @method add
649      * @param request {Object} Request value must be a String or JSON.
650      * @param response {Object} Response value must be a String or JSON.
651      */
652
653     /**
654      * Retrieves cached object for given request, if available.
655      * Returns null if there is no cache match.
656      *
657      * @method retrieve
658      * @param request {Object} Request object.
659      * @return {Object} Cached object with the properties request, response,
660      * and expires, or null.
661      */
662     retrieve: function(request) {
663         this.fire("request", {request: request});
664
665         var entry, expires, sandboxedrequest;
666
667         try {
668             sandboxedrequest = this.get("sandbox")+JSON.stringify({"request":request});
669             try {
670                 entry = JSON.parse(localStorage.getItem(sandboxedrequest));
671             }
672             catch(e) {
673             }
674         }
675         catch(e2) {
676         }
677
678         if(entry) {
679             // Convert msecs to Dates on the way out of localStorage
680             entry.cached = new Date(entry.cached);
681             expires = entry.expires;
682             expires = !expires ? null : new Date(expires);
683             entry.expires = expires;
684
685             if(this._isMatch(request, entry)) {
686                 this.fire("retrieve", {entry: entry});
687                 return entry;
688             }
689         }
690         return null;
691     }
692 } :
693 /////////////////////////////////////////////////////////////////////////////
694 //
695 // Offline is not supported
696 //
697 /////////////////////////////////////////////////////////////////////////////
698 {
699     /**
700      * Always return null.
701      *
702      * @method _setMax
703      * @protected
704      */
705     _setMax: function(value) {
706         return null;
707     }
708 });
709
710
711 Y.CacheOffline = CacheOffline;
712
713
714
715 }, '3.3.0' ,{requires:['cache-base', 'json']});
716
717 YUI.add('cache-plugin', function(Y) {
718
719 /**
720  * Plugin.Cache adds pluginizability to Cache.
721  * @class Plugin.Cache
722  * @extends Cache
723  * @uses Plugin.Base
724  */
725 function CachePlugin(config) {
726     var cache = config && config.cache ? config.cache : Y.Cache,
727         tmpclass = Y.Base.create("dataSourceCache", cache, [Y.Plugin.Base]),
728         tmpinstance = new tmpclass(config);
729     tmpclass.NS = "tmpClass";
730     return tmpinstance;
731 }
732
733 Y.mix(CachePlugin, {
734     /**
735      * The namespace for the plugin. This will be the property on the host which
736      * references the plugin instance.
737      *
738      * @property NS
739      * @type String
740      * @static
741      * @final
742      * @value "cache"
743      */
744     NS: "cache",
745
746     /**
747      * Class name.
748      *
749      * @property NAME
750      * @type String
751      * @static
752      * @final
753      * @value "dataSourceCache"
754      */
755     NAME: "cachePlugin"
756 });
757
758
759 Y.namespace("Plugin").Cache = CachePlugin;
760
761
762
763 }, '3.3.0' ,{requires:['plugin','cache-base']});
764
765
766
767 YUI.add('cache', function(Y){}, '3.3.0' ,{use:['cache-base','cache-offline','cache-plugin']});
768