3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
7 YUI.add('cache-base', function(Y) {
10 * The Cache utility provides a common configurable interface for components to
11 * cache and retrieve data from a local JavaScript struct.
18 * Provides the base class for the YUI Cache utility.
20 * @submodule cache-base
23 isDate = Y.Lang.isDate,
26 * Base class for the YUI Cache utility.
32 Cache.superclass.constructor.apply(this, arguments);
35 /////////////////////////////////////////////////////////////////////////////
37 // Cache static properties
39 /////////////////////////////////////////////////////////////////////////////
54 /////////////////////////////////////////////////////////////////////////////
58 /////////////////////////////////////////////////////////////////////////////
62 * @description Maximum number of entries the Cache can hold.
63 * Set to 0 to turn off caching.
74 * @description Number of entries currently cached.
83 * @attribute uniqueKeys
84 * @description Validate uniqueness of stored keys. Default is false and
94 * @description Absolute Date when data expires or
95 * relative number of milliseconds. Zero disables expiration.
101 validator: function(v) {
102 return Y.Lang.isDate(v) || (Y.Lang.isNumber(v) && v >= 0);
108 * @description Cached entries.
113 getter: "_getEntries"
118 Y.extend(Cache, Y.Base, {
119 /////////////////////////////////////////////////////////////////////////////
121 // Cache private properties
123 /////////////////////////////////////////////////////////////////////////////
126 * Array of request/response objects indexed chronologically.
134 /////////////////////////////////////////////////////////////////////////////
136 // Cache private methods
138 /////////////////////////////////////////////////////////////////////////////
141 * @method initializer
142 * @description Internal init() handler.
143 * @param config {Object} Config object.
146 initializer: function(config) {
150 * @description Fired when an entry is added.
151 * @param e {Event.Facade} Event Facade with the following properties:
153 * <dt>entry (Object)</dt> <dd>The cached entry.</dd>
155 * @preventable _defAddFn
157 this.publish("add", {defaultFn: this._defAddFn});
161 * @description Fired when the cache is flushed.
162 * @param e {Event.Facade} Event Facade object.
163 * @preventable _defFlushFn
165 this.publish("flush", {defaultFn: this._defFlushFn});
169 * @description Fired when an entry is requested from the cache.
170 * @param e {Event.Facade} Event Facade with the following properties:
172 * <dt>request (Object)</dt> <dd>The request object.</dd>
178 * @description Fired when an entry is retrieved from the cache.
179 * @param e {Event.Facade} Event Facade with the following properties:
181 * <dt>entry (Object)</dt> <dd>The retrieved entry.</dd>
185 // Initialize internal values
191 * @description Internal destroy() handler.
194 destructor: function() {
198 /////////////////////////////////////////////////////////////////////////////
200 // Cache protected methods
202 /////////////////////////////////////////////////////////////////////////////
210 _setMax: function(value) {
211 // If the cache is full, make room by removing stalest element (index=0)
212 var entries = this._entries;
215 while(entries.length > value) {
233 _getSize: function() {
234 return this._entries.length;
240 * @method _getEntries
243 _getEntries: function() {
244 return this._entries;
249 * Adds entry to cache.
252 * @param e {Event.Facade} Event Facade with the following properties:
254 * <dt>entry (Object)</dt> <dd>The cached entry.</dd>
258 _defAddFn: function(e) {
259 var entries = this._entries,
261 max = this.get("max"),
264 // If uniqueKeys is true and item exists with this key, then remove it.
265 if (this.get("uniqueKeys")) {
266 pos = this._position(e.entry.request);
267 if (LANG.isValue(pos)) {
268 entries.splice(pos, 1);
272 // If the cache at or over capacity, make room by removing stalest
273 // element(s) starting at index-0.
274 while (max && entries.length >= max) {
278 // Add entry to cache in the newest position, at the end of the array
279 entries[entries.length] = entry;
285 * @method _defFlushFn
286 * @param e {Event.Facade} Event Facade object.
289 _defFlushFn: function(e) {
290 var entries = this._entries,
291 details = e.details[0],
294 //passed an item, flush only that
295 if(details && LANG.isValue(details.request)) {
296 pos = this._position(details.request);
298 if(LANG.isValue(pos)) {
299 entries.splice(pos,1);
303 //no item, flush everything
310 * Default overridable method compares current request with given cache entry.
311 * Returns true if current request matches the cached request, otherwise
312 * false. Implementers should override this method to customize the
313 * cache-matching algorithm.
316 * @param request {Object} Request object.
317 * @param entry {Object} Cached entry.
318 * @return {Boolean} True if current request matches given cached request, false otherwise.
321 _isMatch: function(request, entry) {
322 if(!entry.expires || new Date() < entry.expires) {
323 return (request === entry.request);
329 * Returns position of a request in the entries array, otherwise null.
332 * @param request {Object} Request object.
333 * @return {Number} Array position if found, null otherwise.
336 _position: function(request) {
337 // If cache is enabled...
338 var entries = this._entries,
339 length = entries.length,
342 if((this.get("max") === null) || this.get("max") > 0) {
343 // Loop through each cached entry starting from the newest
345 // Execute matching function
346 if(this._isMatch(request, entries[i])) {
355 /////////////////////////////////////////////////////////////////////////////
357 // Cache public methods
359 /////////////////////////////////////////////////////////////////////////////
362 * Adds a new entry to the cache of the format
363 * {request:request, response:response, cached:cached, expires:expires}.
364 * If cache is full, evicts the stalest entry before adding the new one.
367 * @param request {Object} Request value.
368 * @param response {Object} Response value.
370 add: function(request, response) {
371 var expires = this.get("expires");
372 if(this.get("initialized") && ((this.get("max") === null) || this.get("max") > 0) &&
373 (LANG.isValue(request) || LANG.isNull(request) || LANG.isUndefined(request))) {
374 this.fire("add", {entry: {
378 expires: isDate(expires) ? expires :
379 (expires ? new Date(new Date().getTime() + this.get("expires")) : null)
391 flush: function(request) {
392 this.fire("flush", { request: (LANG.isValue(request) ? request : null) });
396 * Retrieves cached object for given request, if available, and refreshes
397 * entry in the cache. Returns null if there is no cache match.
400 * @param request {Object} Request object.
401 * @return {Object} Cached object with the properties request and response, or null.
403 retrieve: function(request) {
404 // If cache is enabled...
405 var entries = this._entries,
406 length = entries.length,
410 if((length > 0) && ((this.get("max") === null) || (this.get("max") > 0))) {
411 this.fire("request", {request: request});
413 pos = this._position(request);
415 if(LANG.isValue(pos)) {
416 entry = entries[pos];
418 this.fire("retrieve", {entry: entry});
420 // Refresh the position of the cache hit
422 // Remove element from its original location
423 entries.splice(pos,1);
425 entries[entries.length] = entry;
438 }, '3.5.0' ,{requires:['base']});