NOBUG: Fixed file access permissions
[moodle.git] / lib / yuilib / 3.13.0 / cache-base / cache-base.js
blobcc93c5726b50d4819824886ff373200a05912592
1 /*
2 YUI 3.13.0 (build 508226d)
3 Copyright 2013 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
8 YUI.add('cache-base', function (Y, NAME) {
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  * @main
16  */
18 /**
19  * Provides the base class for the YUI Cache utility.
20  *
21  * @submodule cache-base
22  */
23 var LANG = Y.Lang,
24     isDate = Y.Lang.isDate,
26 /**
27  * Base class for the YUI Cache utility.
28  * @class Cache
29  * @extends Base
30  * @constructor
31  */
32 Cache = function() {
33     Cache.superclass.constructor.apply(this, arguments);
36     /////////////////////////////////////////////////////////////////////////////
37     //
38     // Cache static properties
39     //
40     /////////////////////////////////////////////////////////////////////////////
41 Y.mix(Cache, {
42     /**
43      * Class name.
44      *
45      * @property NAME
46      * @type String
47      * @static
48      * @final
49      * @value "cache"
50      */
51     NAME: "cache",
54     ATTRS: {
55         /////////////////////////////////////////////////////////////////////////////
56         //
57         // Cache Attributes
58         //
59         /////////////////////////////////////////////////////////////////////////////
61         /**
62         * @attribute max
63         * @description Maximum number of entries the Cache can hold.
64         * Set to 0 to turn off caching.
65         * @type Number
66         * @default 0
67         */
68         max: {
69             value: 0,
70             setter: "_setMax"
71         },
73         /**
74         * @attribute size
75         * @description Number of entries currently cached.
76         * @type Number
77         */
78         size: {
79             readOnly: true,
80             getter: "_getSize"
81         },
83         /**
84         * @attribute uniqueKeys
85         * @description Validate uniqueness of stored keys. Default is false and
86         * is more performant.
87         * @type Boolean
88         */
89         uniqueKeys: {
90             value: false
91         },
93         /**
94         * @attribute expires
95         * @description Absolute Date when data expires or
96         * relative number of milliseconds. Zero disables expiration.
97         * @type Date | Number
98         * @default 0
99         */
100         expires: {
101             value: 0,
102             validator: function(v) {
103                 return Y.Lang.isDate(v) || (Y.Lang.isNumber(v) && v >= 0);
104             }
105         },
107         /**
108          * @attribute entries
109          * @description Cached entries.
110          * @type Array
111          */
112         entries: {
113             readOnly: true,
114             getter: "_getEntries"
115         }
116     }
119 Y.extend(Cache, Y.Base, {
120     /////////////////////////////////////////////////////////////////////////////
121     //
122     // Cache private properties
123     //
124     /////////////////////////////////////////////////////////////////////////////
126     /**
127      * Array of request/response objects indexed chronologically.
128      *
129      * @property _entries
130      * @type Object[]
131      * @private
132      */
133     _entries: null,
135     /////////////////////////////////////////////////////////////////////////////
136     //
137     // Cache private methods
138     //
139     /////////////////////////////////////////////////////////////////////////////
141     /**
142     * @method initializer
143     * @description Internal init() handler.
144     * @param config {Object} Config object.
145     * @private
146     */
147     initializer: function(config) {
149         /**
150         * @event add
151         * @description Fired when an entry is added.
152         * @param e {Event.Facade} Event Facade with the following properties:
153          * <dl>
154          * <dt>entry (Object)</dt> <dd>The cached entry.</dd>
155          * </dl>
156         * @preventable _defAddFn
157         */
158         this.publish("add", {defaultFn: this._defAddFn});
160         /**
161         * @event flush
162         * @description Fired when the cache is flushed.
163         * @param e {Event.Facade} Event Facade object.
164         * @preventable _defFlushFn
165         */
166         this.publish("flush", {defaultFn: this._defFlushFn});
168         /**
169         * @event request
170         * @description Fired when an entry is requested from the cache.
171         * @param e {Event.Facade} Event Facade with the following properties:
172         * <dl>
173         * <dt>request (Object)</dt> <dd>The request object.</dd>
174         * </dl>
175         */
177         /**
178         * @event retrieve
179         * @description Fired when an entry is retrieved from the cache.
180         * @param e {Event.Facade} Event Facade with the following properties:
181         * <dl>
182         * <dt>entry (Object)</dt> <dd>The retrieved entry.</dd>
183         * </dl>
184         */
186         // Initialize internal values
187         this._entries = [];
188     },
190     /**
191     * @method destructor
192     * @description Internal destroy() handler.
193     * @private
194     */
195     destructor: function() {
196         this._entries = [];
197     },
199     /////////////////////////////////////////////////////////////////////////////
200     //
201     // Cache protected methods
202     //
203     /////////////////////////////////////////////////////////////////////////////
205     /**
206      * Sets max.
207      *
208      * @method _setMax
209      * @protected
210      */
211     _setMax: function(value) {
212         // If the cache is full, make room by removing stalest element (index=0)
213         var entries = this._entries;
214         if(value > 0) {
215             if(entries) {
216                 while(entries.length > value) {
217                     entries.shift();
218                 }
219             }
220         }
221         else {
222             value = 0;
223             this._entries = [];
224         }
225         return value;
226     },
228     /**
229      * Gets size.
230      *
231      * @method _getSize
232      * @protected
233      */
234     _getSize: function() {
235         return this._entries.length;
236     },
238     /**
239      * Gets all entries.
240      *
241      * @method _getEntries
242      * @protected
243      */
244     _getEntries: function() {
245         return this._entries;
246     },
249     /**
250      * Adds entry to cache.
251      *
252      * @method _defAddFn
253      * @param e {Event.Facade} Event Facade with the following properties:
254      * <dl>
255      * <dt>entry (Object)</dt> <dd>The cached entry.</dd>
256      * </dl>
257      * @protected
258      */
259     _defAddFn: function(e) {
260         var entries = this._entries,
261             entry   = e.entry,
262             max     = this.get("max"),
263             pos;
265         // If uniqueKeys is true and item exists with this key, then remove it.
266         if (this.get("uniqueKeys")) {
267             pos = this._position(e.entry.request);
268             if (LANG.isValue(pos)) {
269                 entries.splice(pos, 1);
270             }
271         }
273         // If the cache at or over capacity, make room by removing stalest
274         // element(s) starting at index-0.
275         while (max && entries.length >= max) {
276             entries.shift();
277         }
279         // Add entry to cache in the newest position, at the end of the array
280         entries[entries.length] = entry;
281     },
283     /**
284      * Flushes cache.
285      *
286      * @method _defFlushFn
287      * @param e {Event.Facade} Event Facade object.
288      * @protected
289      */
290     _defFlushFn: function(e) {
291         var entries = this._entries,
292             details = e.details[0],
293             pos;
295         //passed an item, flush only that
296         if(details && LANG.isValue(details.request)) {
297             pos = this._position(details.request);
299             if(LANG.isValue(pos)) {
300                 entries.splice(pos,1);
302             }
303         }
304         //no item, flush everything
305         else {
306             this._entries = [];
307         }
308     },
310     /**
311      * Default overridable method compares current request with given cache entry.
312      * Returns true if current request matches the cached request, otherwise
313      * false. Implementers should override this method to customize the
314      * cache-matching algorithm.
315      *
316      * @method _isMatch
317      * @param request {Object} Request object.
318      * @param entry {Object} Cached entry.
319      * @return {Boolean} True if current request matches given cached request, false otherwise.
320      * @protected
321      */
322     _isMatch: function(request, entry) {
323         if(!entry.expires || new Date() < entry.expires) {
324             return (request === entry.request);
325         }
326         return false;
327     },
329     /**
330      * Returns position of a request in the entries array, otherwise null.
331      *
332      * @method _position
333      * @param request {Object} Request object.
334      * @return {Number} Array position if found, null otherwise.
335      * @protected
336      */
337     _position: function(request) {
338         // If cache is enabled...
339         var entries = this._entries,
340             length = entries.length,
341             i = length-1;
343         if((this.get("max") === null) || this.get("max") > 0) {
344             // Loop through each cached entry starting from the newest
345             for(; i >= 0; i--) {
346                 // Execute matching function
347                 if(this._isMatch(request, entries[i])) {
348                     return i;
349                 }
350             }
351         }
353         return null;
354     },
356     /////////////////////////////////////////////////////////////////////////////
357     //
358     // Cache public methods
359     //
360     /////////////////////////////////////////////////////////////////////////////
362     /**
363      * Adds a new entry to the cache of the format
364      * {request:request, response:response, cached:cached, expires:expires}.
365      * If cache is full, evicts the stalest entry before adding the new one.
366      *
367      * @method add
368      * @param request {Object} Request value.
369      * @param response {Object} Response value.
370      */
371     add: function(request, response) {
372         var expires = this.get("expires");
373         if(this.get("initialized") && ((this.get("max") === null) || this.get("max") > 0) &&
374                 (LANG.isValue(request) || LANG.isNull(request) || LANG.isUndefined(request))) {
375             this.fire("add", {entry: {
376                 request:request,
377                 response:response,
378                 cached: new Date(),
379                 expires: isDate(expires) ? expires :
380             (expires ? new Date(new Date().getTime() + this.get("expires")) : null)
381             }});
382         }
383         else {
384         }
385     },
387     /**
388      * Flushes cache.
389      *
390      * @method flush
391      */
392     flush: function(request) {
393         this.fire("flush", { request: (LANG.isValue(request) ? request : null) });
394     },
396     /**
397      * Retrieves cached object for given request, if available, and refreshes
398      * entry in the cache. Returns null if there is no cache match.
399      *
400      * @method retrieve
401      * @param request {Object} Request object.
402      * @return {Object} Cached object with the properties request and response, or null.
403      */
404     retrieve: function(request) {
405         // If cache is enabled...
406         var entries = this._entries,
407             length = entries.length,
408             entry = null,
409             pos;
411         if((length > 0) && ((this.get("max") === null) || (this.get("max") > 0))) {
412             this.fire("request", {request: request});
414             pos = this._position(request);
416             if(LANG.isValue(pos)) {
417                 entry = entries[pos];
419                 this.fire("retrieve", {entry: entry});
421                 // Refresh the position of the cache hit
422                 if(pos < length-1) {
423                     // Remove element from its original location
424                     entries.splice(pos,1);
425                     // Add as newest
426                     entries[entries.length] = entry;
427                 }
429                 return entry;
430             }
431         }
432         return null;
433     }
436 Y.Cache = Cache;
439 }, '3.13.0', {"requires": ["base"]});