Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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