You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
439 lines
11 KiB
439 lines
11 KiB
/*
|
|
YUI 3.17.2 (build 9c3c78e)
|
|
Copyright 2014 Yahoo! Inc. All rights reserved.
|
|
Licensed under the BSD License.
|
|
http://yuilibrary.com/license/
|
|
*/
|
|
|
|
YUI.add('cache-base', function (Y, NAME) {
|
|
|
|
/**
|
|
* The Cache utility provides a common configurable interface for components to
|
|
* cache and retrieve data from a local JavaScript struct.
|
|
*
|
|
* @module cache
|
|
* @main
|
|
*/
|
|
|
|
/**
|
|
* Provides the base class for the YUI Cache utility.
|
|
*
|
|
* @submodule cache-base
|
|
*/
|
|
var LANG = Y.Lang,
|
|
isDate = Y.Lang.isDate,
|
|
|
|
/**
|
|
* Base class for the YUI Cache utility.
|
|
* @class Cache
|
|
* @extends Base
|
|
* @constructor
|
|
*/
|
|
Cache = function() {
|
|
Cache.superclass.constructor.apply(this, arguments);
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Cache static properties
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
Y.mix(Cache, {
|
|
/**
|
|
* Class name.
|
|
*
|
|
* @property NAME
|
|
* @type String
|
|
* @static
|
|
* @final
|
|
* @value "cache"
|
|
*/
|
|
NAME: "cache",
|
|
|
|
|
|
ATTRS: {
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Cache Attributes
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* @attribute max
|
|
* @description Maximum number of entries the Cache can hold.
|
|
* Set to 0 to turn off caching.
|
|
* @type Number
|
|
* @default 0
|
|
*/
|
|
max: {
|
|
value: 0,
|
|
setter: "_setMax"
|
|
},
|
|
|
|
/**
|
|
* @attribute size
|
|
* @description Number of entries currently cached.
|
|
* @type Number
|
|
*/
|
|
size: {
|
|
readOnly: true,
|
|
getter: "_getSize"
|
|
},
|
|
|
|
/**
|
|
* @attribute uniqueKeys
|
|
* @description Validate uniqueness of stored keys. Default is false and
|
|
* is more performant.
|
|
* @type Boolean
|
|
*/
|
|
uniqueKeys: {
|
|
value: false
|
|
},
|
|
|
|
/**
|
|
* @attribute expires
|
|
* @description Absolute Date when data expires or
|
|
* relative number of milliseconds. Zero disables expiration.
|
|
* @type Date | Number
|
|
* @default 0
|
|
*/
|
|
expires: {
|
|
value: 0,
|
|
validator: function(v) {
|
|
return Y.Lang.isDate(v) || (Y.Lang.isNumber(v) && v >= 0);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @attribute entries
|
|
* @description Cached entries.
|
|
* @type Array
|
|
*/
|
|
entries: {
|
|
readOnly: true,
|
|
getter: "_getEntries"
|
|
}
|
|
}
|
|
});
|
|
|
|
Y.extend(Cache, Y.Base, {
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Cache private properties
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Array of request/response objects indexed chronologically.
|
|
*
|
|
* @property _entries
|
|
* @type Object[]
|
|
* @private
|
|
*/
|
|
_entries: null,
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Cache private methods
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* @method initializer
|
|
* @description Internal init() handler.
|
|
* @param config {Object} Config object.
|
|
* @private
|
|
*/
|
|
initializer: function(config) {
|
|
|
|
/**
|
|
* @event add
|
|
* @description Fired when an entry is added.
|
|
* @param e {EventFacade} Event Facade with the following properties:
|
|
* <dl>
|
|
* <dt>entry (Object)</dt> <dd>The cached entry.</dd>
|
|
* </dl>
|
|
* @preventable _defAddFn
|
|
*/
|
|
this.publish("add", {defaultFn: this._defAddFn});
|
|
|
|
/**
|
|
* @event flush
|
|
* @description Fired when the cache is flushed.
|
|
* @param e {EventFacade} Event Facade object.
|
|
* @preventable _defFlushFn
|
|
*/
|
|
this.publish("flush", {defaultFn: this._defFlushFn});
|
|
|
|
/**
|
|
* @event request
|
|
* @description Fired when an entry is requested from the cache.
|
|
* @param e {EventFacade} Event Facade with the following properties:
|
|
* <dl>
|
|
* <dt>request (Object)</dt> <dd>The request object.</dd>
|
|
* </dl>
|
|
*/
|
|
|
|
/**
|
|
* @event retrieve
|
|
* @description Fired when an entry is retrieved from the cache.
|
|
* @param e {EventFacade} Event Facade with the following properties:
|
|
* <dl>
|
|
* <dt>entry (Object)</dt> <dd>The retrieved entry.</dd>
|
|
* </dl>
|
|
*/
|
|
|
|
// Initialize internal values
|
|
this._entries = [];
|
|
},
|
|
|
|
/**
|
|
* @method destructor
|
|
* @description Internal destroy() handler.
|
|
* @private
|
|
*/
|
|
destructor: function() {
|
|
this._entries = [];
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Cache protected methods
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Sets max.
|
|
*
|
|
* @method _setMax
|
|
* @protected
|
|
*/
|
|
_setMax: function(value) {
|
|
// If the cache is full, make room by removing stalest element (index=0)
|
|
var entries = this._entries;
|
|
if(value > 0) {
|
|
if(entries) {
|
|
while(entries.length > value) {
|
|
entries.shift();
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
value = 0;
|
|
this._entries = [];
|
|
}
|
|
return value;
|
|
},
|
|
|
|
/**
|
|
* Gets size.
|
|
*
|
|
* @method _getSize
|
|
* @protected
|
|
*/
|
|
_getSize: function() {
|
|
return this._entries.length;
|
|
},
|
|
|
|
/**
|
|
* Gets all entries.
|
|
*
|
|
* @method _getEntries
|
|
* @protected
|
|
*/
|
|
_getEntries: function() {
|
|
return this._entries;
|
|
},
|
|
|
|
|
|
/**
|
|
* Adds entry to cache.
|
|
*
|
|
* @method _defAddFn
|
|
* @param e {EventFacade} Event Facade with the following properties:
|
|
* <dl>
|
|
* <dt>entry (Object)</dt> <dd>The cached entry.</dd>
|
|
* </dl>
|
|
* @protected
|
|
*/
|
|
_defAddFn: function(e) {
|
|
var entries = this._entries,
|
|
entry = e.entry,
|
|
max = this.get("max"),
|
|
pos;
|
|
|
|
// If uniqueKeys is true and item exists with this key, then remove it.
|
|
if (this.get("uniqueKeys")) {
|
|
pos = this._position(e.entry.request);
|
|
if (LANG.isValue(pos)) {
|
|
entries.splice(pos, 1);
|
|
}
|
|
}
|
|
|
|
// If the cache at or over capacity, make room by removing stalest
|
|
// element(s) starting at index-0.
|
|
while (max && entries.length >= max) {
|
|
entries.shift();
|
|
}
|
|
|
|
// Add entry to cache in the newest position, at the end of the array
|
|
entries[entries.length] = entry;
|
|
},
|
|
|
|
/**
|
|
* Flushes cache.
|
|
*
|
|
* @method _defFlushFn
|
|
* @param e {EventFacade} Event Facade object.
|
|
* @protected
|
|
*/
|
|
_defFlushFn: function(e) {
|
|
var entries = this._entries,
|
|
details = e.details[0],
|
|
pos;
|
|
|
|
//passed an item, flush only that
|
|
if(details && LANG.isValue(details.request)) {
|
|
pos = this._position(details.request);
|
|
|
|
if(LANG.isValue(pos)) {
|
|
entries.splice(pos,1);
|
|
|
|
}
|
|
}
|
|
//no item, flush everything
|
|
else {
|
|
this._entries = [];
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Default overridable method compares current request with given cache entry.
|
|
* Returns true if current request matches the cached request, otherwise
|
|
* false. Implementers should override this method to customize the
|
|
* cache-matching algorithm.
|
|
*
|
|
* @method _isMatch
|
|
* @param request {Object} Request object.
|
|
* @param entry {Object} Cached entry.
|
|
* @return {Boolean} True if current request matches given cached request, false otherwise.
|
|
* @protected
|
|
*/
|
|
_isMatch: function(request, entry) {
|
|
if(!entry.expires || new Date() < entry.expires) {
|
|
return (request === entry.request);
|
|
}
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* Returns position of a request in the entries array, otherwise null.
|
|
*
|
|
* @method _position
|
|
* @param request {Object} Request object.
|
|
* @return {Number} Array position if found, null otherwise.
|
|
* @protected
|
|
*/
|
|
_position: function(request) {
|
|
// If cache is enabled...
|
|
var entries = this._entries,
|
|
length = entries.length,
|
|
i = length-1;
|
|
|
|
if((this.get("max") === null) || this.get("max") > 0) {
|
|
// Loop through each cached entry starting from the newest
|
|
for(; i >= 0; i--) {
|
|
// Execute matching function
|
|
if(this._isMatch(request, entries[i])) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Cache public methods
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Adds a new entry to the cache of the format
|
|
* {request:request, response:response, cached:cached, expires:expires}.
|
|
* If cache is full, evicts the stalest entry before adding the new one.
|
|
*
|
|
* @method add
|
|
* @param request {Object} Request value.
|
|
* @param response {Object} Response value.
|
|
*/
|
|
add: function(request, response) {
|
|
var expires = this.get("expires");
|
|
if(this.get("initialized") && ((this.get("max") === null) || this.get("max") > 0) &&
|
|
(LANG.isValue(request) || LANG.isNull(request) || LANG.isUndefined(request))) {
|
|
this.fire("add", {entry: {
|
|
request:request,
|
|
response:response,
|
|
cached: new Date(),
|
|
expires: isDate(expires) ? expires :
|
|
(expires ? new Date(new Date().getTime() + this.get("expires")) : null)
|
|
}});
|
|
}
|
|
else {
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Flushes cache.
|
|
*
|
|
* @method flush
|
|
*/
|
|
flush: function(request) {
|
|
this.fire("flush", { request: (LANG.isValue(request) ? request : null) });
|
|
},
|
|
|
|
/**
|
|
* Retrieves cached object for given request, if available, and refreshes
|
|
* entry in the cache. Returns null if there is no cache match.
|
|
*
|
|
* @method retrieve
|
|
* @param request {Object} Request object.
|
|
* @return {Object} Cached object with the properties request and response, or null.
|
|
*/
|
|
retrieve: function(request) {
|
|
// If cache is enabled...
|
|
var entries = this._entries,
|
|
length = entries.length,
|
|
entry = null,
|
|
pos;
|
|
|
|
if((length > 0) && ((this.get("max") === null) || (this.get("max") > 0))) {
|
|
this.fire("request", {request: request});
|
|
|
|
pos = this._position(request);
|
|
|
|
if(LANG.isValue(pos)) {
|
|
entry = entries[pos];
|
|
|
|
this.fire("retrieve", {entry: entry});
|
|
|
|
// Refresh the position of the cache hit
|
|
if(pos < length-1) {
|
|
// Remove element from its original location
|
|
entries.splice(pos,1);
|
|
// Add as newest
|
|
entries[entries.length] = entry;
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
});
|
|
|
|
Y.Cache = Cache;
|
|
|
|
|
|
}, '3.17.2', {"requires": ["base"]});
|
|
|