Merge branch 'MDL-32509' of git://github.com/danpoltawski/moodle
[moodle.git] / lib / yui / 3.5.0 / build / cache-base / cache-base.js
blobb4833637bc96bf7b78a52ec29141f88686b22166
1 /*
2 YUI 3.5.0 (build 5089)
3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
7 YUI.add('cache-base', function(Y) {
9 /**
10  * The Cache utility provides a common configurable interface for components to
11  * cache and retrieve data from a local JavaScript struct.
12  *
13  * @module cache
14  * @main
15  */
17 /**
18  * Provides the base class for the YUI Cache utility.
19  *
20  * @submodule cache-base
21  */
22 var LANG = Y.Lang,
23     isDate = Y.Lang.isDate,
25 /**
26  * Base class for the YUI Cache utility.
27  * @class Cache
28  * @extends Base
29  * @constructor
30  */
31 Cache = function() {
32     Cache.superclass.constructor.apply(this, arguments);
35     /////////////////////////////////////////////////////////////////////////////
36     //
37     // Cache static properties
38     //
39     /////////////////////////////////////////////////////////////////////////////
40 Y.mix(Cache, {
41     /**
42      * Class name.
43      *
44      * @property NAME
45      * @type String
46      * @static
47      * @final
48      * @value "cache"
49      */
50     NAME: "cache",
53     ATTRS: {
54         /////////////////////////////////////////////////////////////////////////////
55         //
56         // Cache Attributes
57         //
58         /////////////////////////////////////////////////////////////////////////////
60         /**
61         * @attribute max
62         * @description Maximum number of entries the Cache can hold.
63         * Set to 0 to turn off caching.
64         * @type Number
65         * @default 0
66         */
67         max: {
68             value: 0,
69             setter: "_setMax"
70         },
72         /**
73         * @attribute size
74         * @description Number of entries currently cached.
75         * @type Number
76         */
77         size: {
78             readOnly: true,
79             getter: "_getSize"
80         },
82         /**
83         * @attribute uniqueKeys
84         * @description Validate uniqueness of stored keys. Default is false and
85         * is more performant.
86         * @type Boolean
87         */
88         uniqueKeys: {
89             value: false
90         },
92         /**
93         * @attribute expires
94         * @description Absolute Date when data expires or
95         * relative number of milliseconds. Zero disables expiration.
96         * @type Date | Number
97         * @default 0
98         */
99         expires: {
100             value: 0,
101             validator: function(v) {
102                 return Y.Lang.isDate(v) || (Y.Lang.isNumber(v) && v >= 0);
103             }
104         },
106         /**
107          * @attribute entries
108          * @description Cached entries.
109          * @type Array
110          */
111         entries: {
112             readOnly: true,
113             getter: "_getEntries"
114         }
115     }
118 Y.extend(Cache, Y.Base, {
119     /////////////////////////////////////////////////////////////////////////////
120     //
121     // Cache private properties
122     //
123     /////////////////////////////////////////////////////////////////////////////
125     /**
126      * Array of request/response objects indexed chronologically.
127      *
128      * @property _entries
129      * @type Object[]
130      * @private
131      */
132     _entries: null,
134     /////////////////////////////////////////////////////////////////////////////
135     //
136     // Cache private methods
137     //
138     /////////////////////////////////////////////////////////////////////////////
140     /**
141     * @method initializer
142     * @description Internal init() handler.
143     * @param config {Object} Config object.
144     * @private
145     */
146     initializer: function(config) {
148         /**
149         * @event add
150         * @description Fired when an entry is added.
151         * @param e {Event.Facade} Event Facade with the following properties:
152          * <dl>
153          * <dt>entry (Object)</dt> <dd>The cached entry.</dd>
154          * </dl>
155         * @preventable _defAddFn
156         */
157         this.publish("add", {defaultFn: this._defAddFn});
159         /**
160         * @event flush
161         * @description Fired when the cache is flushed.
162         * @param e {Event.Facade} Event Facade object.
163         * @preventable _defFlushFn
164         */
165         this.publish("flush", {defaultFn: this._defFlushFn});
167         /**
168         * @event request
169         * @description Fired when an entry is requested from the cache.
170         * @param e {Event.Facade} Event Facade with the following properties:
171         * <dl>
172         * <dt>request (Object)</dt> <dd>The request object.</dd>
173         * </dl>
174         */
176         /**
177         * @event retrieve
178         * @description Fired when an entry is retrieved from the cache.
179         * @param e {Event.Facade} Event Facade with the following properties:
180         * <dl>
181         * <dt>entry (Object)</dt> <dd>The retrieved entry.</dd>
182         * </dl>
183         */
185         // Initialize internal values
186         this._entries = [];
187     },
189     /**
190     * @method destructor
191     * @description Internal destroy() handler.
192     * @private
193     */
194     destructor: function() {
195         this._entries = [];
196     },
198     /////////////////////////////////////////////////////////////////////////////
199     //
200     // Cache protected methods
201     //
202     /////////////////////////////////////////////////////////////////////////////
204     /**
205      * Sets max.
206      *
207      * @method _setMax
208      * @protected
209      */
210     _setMax: function(value) {
211         // If the cache is full, make room by removing stalest element (index=0)
212         var entries = this._entries;
213         if(value > 0) {
214             if(entries) {
215                 while(entries.length > value) {
216                     entries.shift();
217                 }
218             }
219         }
220         else {
221             value = 0;
222             this._entries = [];
223         }
224         return value;
225     },
227     /**
228      * Gets size.
229      *
230      * @method _getSize
231      * @protected
232      */
233     _getSize: function() {
234         return this._entries.length;
235     },
237     /**
238      * Gets all entries.
239      *
240      * @method _getEntries
241      * @protected
242      */
243     _getEntries: function() {
244         return this._entries;
245     },
248     /**
249      * Adds entry to cache.
250      *
251      * @method _defAddFn
252      * @param e {Event.Facade} Event Facade with the following properties:
253      * <dl>
254      * <dt>entry (Object)</dt> <dd>The cached entry.</dd>
255      * </dl>
256      * @protected
257      */
258     _defAddFn: function(e) {
259         var entries = this._entries,
260             entry   = e.entry,
261             max     = this.get("max"),
262             pos;
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);
269             }
270         }
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) {
275             entries.shift();
276         }
278         // Add entry to cache in the newest position, at the end of the array
279         entries[entries.length] = entry;
280     },
282     /**
283      * Flushes cache.
284      *
285      * @method _defFlushFn
286      * @param e {Event.Facade} Event Facade object.
287      * @protected
288      */
289     _defFlushFn: function(e) {
290         var entries = this._entries,
291             details = e.details[0],
292             pos;
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);
301             }
302         }
303         //no item, flush everything
304         else {
305             this._entries = [];
306         }
307     },
309     /**
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.
314      *
315      * @method _isMatch
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.
319      * @protected
320      */
321     _isMatch: function(request, entry) {
322         if(!entry.expires || new Date() < entry.expires) {
323             return (request === entry.request);
324         }
325         return false;
326     },
328     /**
329      * Returns position of a request in the entries array, otherwise null.
330      *
331      * @method _position
332      * @param request {Object} Request object.
333      * @return {Number} Array position if found, null otherwise.
334      * @protected
335      */
336     _position: function(request) {
337         // If cache is enabled...
338         var entries = this._entries,
339             length = entries.length,
340             i = length-1;
342         if((this.get("max") === null) || this.get("max") > 0) {
343             // Loop through each cached entry starting from the newest
344             for(; i >= 0; i--) {
345                 // Execute matching function
346                 if(this._isMatch(request, entries[i])) {
347                     return i;
348                 }
349             }
350         }
352         return null;
353     },
355     /////////////////////////////////////////////////////////////////////////////
356     //
357     // Cache public methods
358     //
359     /////////////////////////////////////////////////////////////////////////////
361     /**
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.
365      *
366      * @method add
367      * @param request {Object} Request value.
368      * @param response {Object} Response value.
369      */
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: {
375                 request:request,
376                 response:response,
377                 cached: new Date(),
378                 expires: isDate(expires) ? expires :
379             (expires ? new Date(new Date().getTime() + this.get("expires")) : null)
380             }});
381         }
382         else {
383         }
384     },
386     /**
387      * Flushes cache.
388      *
389      * @method flush
390      */
391     flush: function(request) {
392         this.fire("flush", { request: (LANG.isValue(request) ? request : null) });
393     },
395     /**
396      * Retrieves cached object for given request, if available, and refreshes
397      * entry in the cache. Returns null if there is no cache match.
398      *
399      * @method retrieve
400      * @param request {Object} Request object.
401      * @return {Object} Cached object with the properties request and response, or null.
402      */
403     retrieve: function(request) {
404         // If cache is enabled...
405         var entries = this._entries,
406             length = entries.length,
407             entry = null,
408             pos;
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
421                 if(pos < length-1) {
422                     // Remove element from its original location
423                     entries.splice(pos,1);
424                     // Add as newest
425                     entries[entries.length] = entry;
426                 }
428                 return entry;
429             }
430         }
431         return null;
432     }
435 Y.Cache = Cache;
438 }, '3.5.0' ,{requires:['base']});