2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
8 YUI.add('cache-base', function(Y) {
11 * The Cache utility provides a common configurable interface for components to
12 * cache and retrieve data from a local JavaScript struct.
17 isDate = Y.Lang.isDate,
20 * Base class for the YUI Cache utility.
26 Cache.superclass.constructor.apply(this, arguments);
29 /////////////////////////////////////////////////////////////////////////////
31 // Cache static properties
33 /////////////////////////////////////////////////////////////////////////////
48 /////////////////////////////////////////////////////////////////////////////
52 /////////////////////////////////////////////////////////////////////////////
56 * @description Maximum number of entries the Cache can hold.
57 * Set to 0 to turn off caching.
68 * @description Number of entries currently cached.
77 * @attribute uniqueKeys
78 * @description Validate uniqueness of stored keys. Default is false and
88 * @description Absolute Date when data expires or
89 * relative number of milliseconds. Zero disables expiration.
95 validator: function(v) {
96 return Y.Lang.isDate(v) || (Y.Lang.isNumber(v) && v >= 0);
102 * @description Cached entries.
107 getter: "_getEntries"
112 Y.extend(Cache, Y.Base, {
113 /////////////////////////////////////////////////////////////////////////////
115 // Cache private properties
117 /////////////////////////////////////////////////////////////////////////////
120 * Array of request/response objects indexed chronologically.
128 /////////////////////////////////////////////////////////////////////////////
130 // Cache private methods
132 /////////////////////////////////////////////////////////////////////////////
135 * @method initializer
136 * @description Internal init() handler.
137 * @param config {Object} Config object.
140 initializer: function(config) {
144 * @description Fired when an entry is added.
145 * @param e {Event.Facade} Event Facade with the following properties:
147 * <dt>entry (Object)</dt> <dd>The cached entry.</dd>
149 * @preventable _defAddFn
151 this.publish("add", {defaultFn: this._defAddFn});
155 * @description Fired when the cache is flushed.
156 * @param e {Event.Facade} Event Facade object.
157 * @preventable _defFlushFn
159 this.publish("flush", {defaultFn: this._defFlushFn});
163 * @description Fired when an entry is requested from the cache.
164 * @param e {Event.Facade} Event Facade with the following properties:
166 * <dt>request (Object)</dt> <dd>The request object.</dd>
172 * @description Fired when an entry is retrieved from the cache.
173 * @param e {Event.Facade} Event Facade with the following properties:
175 * <dt>entry (Object)</dt> <dd>The retrieved entry.</dd>
179 // Initialize internal values
185 * @description Internal destroy() handler.
188 destructor: function() {
192 /////////////////////////////////////////////////////////////////////////////
194 // Cache protected methods
196 /////////////////////////////////////////////////////////////////////////////
204 _setMax: function(value) {
205 // If the cache is full, make room by removing stalest element (index=0)
206 var entries = this._entries;
209 while(entries.length > value) {
227 _getSize: function() {
228 return this._entries.length;
234 * @method _getEntries
237 _getEntries: function() {
238 return this._entries;
243 * Adds entry to cache.
246 * @param e {Event.Facade} Event Facade with the following properties:
248 * <dt>entry (Object)</dt> <dd>The cached entry.</dd>
252 _defAddFn: function(e) {
253 var entries = this._entries,
254 max = this.get("max"),
257 if(this.get("uniqueKeys") && (this.retrieve(e.entry.request))) {
262 // If the cache at or over capacity, make room by removing stalest element (index=0)
263 while(max && entries.length>=max) {
267 // Add entry to cache in the newest position, at the end of the array
268 entries[entries.length] = entry;
274 * @method _defFlushFn
275 * @param e {Event.Facade} Event Facade object.
278 _defFlushFn: function(e) {
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.
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.
294 _isMatch: function(request, entry) {
295 if(!entry.expires || new Date() < entry.expires) {
296 return (request === entry.request);
301 /////////////////////////////////////////////////////////////////////////////
303 // Cache public methods
305 /////////////////////////////////////////////////////////////////////////////
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.
313 * @param request {Object} Request value.
314 * @param response {Object} Response value.
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: {
324 expires: isDate(expires) ? expires :
325 (expires ? new Date(new Date().getTime() + this.get("expires")) : null)
342 * Retrieves cached object for given request, if available, and refreshes
343 * entry in the cache. Returns null if there is no cache match.
346 * @param request {Object} Request object.
347 * @return {Object} Cached object with the properties request and response, or null.
349 retrieve: function(request) {
350 // If cache is enabled...
351 var entries = this._entries,
352 length = entries.length,
356 if((length > 0) && ((this.get("max") === null) || (this.get("max") > 0))) {
357 this.fire("request", {request: request});
359 // Loop through each cached entry starting from the newest
363 // Execute matching function
364 if(this._isMatch(request, entry)) {
365 this.fire("retrieve", {entry: entry});
367 // Refresh the position of the cache hit
369 // Remove element from its original location
372 entries[entries.length] = entry;
387 }, '3.3.0' ,{requires:['base']});
389 YUI.add('cache-offline', function(Y) {
392 * Extends Cache utility with offline functionality.
393 * @class CacheOffline
397 function CacheOffline() {
398 CacheOffline.superclass.constructor.apply(this, arguments);
401 var localStorage = null,
406 localStorage = Y.config.win.localStorage;
411 /////////////////////////////////////////////////////////////////////////////
413 // CacheOffline events
415 /////////////////////////////////////////////////////////////////////////////
419 * @description Fired when an entry could not be added, most likely due to
420 * exceeded browser quota.
422 * <dt>error (Object)</dt> <dd>The error object.</dd>
426 /////////////////////////////////////////////////////////////////////////////
428 // CacheOffline static
430 /////////////////////////////////////////////////////////////////////////////
431 Y.mix(CacheOffline, {
439 * @value "cacheOffline"
441 NAME: "cacheOffline",
444 /////////////////////////////////////////////////////////////////////////////
446 // CacheOffline Attributes
448 /////////////////////////////////////////////////////////////////////////////
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.
462 writeOnce: "initOnly"
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)
478 * @description Disabled.
488 * @attribute uniqueKeys
489 * @description Always true for CacheOffline.
503 * Removes all items from all sandboxes. Useful if localStorage has
504 * exceeded quota. Only supported on browsers that implement HTML 5
510 flushAll: function() {
511 var store = localStorage, key;
519 if (store.hasOwnProperty(key)) {
520 store.removeItem(key);
531 Y.extend(CacheOffline, Y.Cache, localStorage ? {
532 /////////////////////////////////////////////////////////////////////////////
534 // Offline is supported
536 /////////////////////////////////////////////////////////////////////////////
538 /////////////////////////////////////////////////////////////////////////////
540 // CacheOffline protected methods
542 /////////////////////////////////////////////////////////////////////////////
544 * Always return null.
549 _setMax: function(value) {
559 _getSize: function() {
562 l=localStorage.length;
565 if(localStorage.key(i).indexOf(this.get("sandbox")) === 0) {
575 * @method _getEntries
578 _getEntries: function() {
581 l=localStorage.length,
582 sandbox = this.get("sandbox");
585 if(localStorage.key(i).indexOf(sandbox) === 0) {
586 entries[i] = JSON.parse(localStorage.key(i).substring(sandbox.length));
593 * Adds entry to cache.
596 * @param e {Event.Facade} Event Facade with the following properties:
598 * <dt>entry (Object)</dt> <dd>The cached entry.</dd>
602 _defAddFn: function(e) {
604 request = entry.request,
605 cached = entry.cached,
606 expires = entry.expires;
608 // Convert Dates to msecs on the way into localStorage
609 entry.cached = cached.getTime();
610 entry.expires = expires ? expires.getTime() : expires;
613 localStorage.setItem(this.get("sandbox")+JSON.stringify({"request":request}), JSON.stringify(entry));
616 this.fire("error", {error:error});
623 * @method _defFlushFn
624 * @param e {Event.Facade} Event Facade object.
627 _defFlushFn: function(e) {
629 i=localStorage.length-1;
632 key = localStorage.key(i);
633 if(key.indexOf(this.get("sandbox")) === 0) {
634 localStorage.removeItem(key);
639 /////////////////////////////////////////////////////////////////////////////
641 // CacheOffline public methods
643 /////////////////////////////////////////////////////////////////////////////
645 * Adds a new entry to the cache of the format
646 * {request:request, response:response, cached:cached, expires: expires}.
649 * @param request {Object} Request value must be a String or JSON.
650 * @param response {Object} Response value must be a String or JSON.
654 * Retrieves cached object for given request, if available.
655 * Returns null if there is no cache match.
658 * @param request {Object} Request object.
659 * @return {Object} Cached object with the properties request, response,
660 * and expires, or null.
662 retrieve: function(request) {
663 this.fire("request", {request: request});
665 var entry, expires, sandboxedrequest;
668 sandboxedrequest = this.get("sandbox")+JSON.stringify({"request":request});
670 entry = JSON.parse(localStorage.getItem(sandboxedrequest));
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;
685 if(this._isMatch(request, entry)) {
686 this.fire("retrieve", {entry: entry});
693 /////////////////////////////////////////////////////////////////////////////
695 // Offline is not supported
697 /////////////////////////////////////////////////////////////////////////////
700 * Always return null.
705 _setMax: function(value) {
711 Y.CacheOffline = CacheOffline;
715 }, '3.3.0' ,{requires:['cache-base', 'json']});
717 YUI.add('cache-plugin', function(Y) {
720 * Plugin.Cache adds pluginizability to Cache.
721 * @class Plugin.Cache
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";
735 * The namespace for the plugin. This will be the property on the host which
736 * references the plugin instance.
753 * @value "dataSourceCache"
759 Y.namespace("Plugin").Cache = CachePlugin;
763 }, '3.3.0' ,{requires:['plugin','cache-base']});
767 YUI.add('cache', function(Y){}, '3.3.0' ,{use:['cache-base','cache-offline','cache-plugin']});