Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('recordset-base', function (Y, NAME) {
2
 
3
/**
4
 * Provides a wrapper around a standard javascript object. Can be inserted into a Recordset instance.
5
 *
6
 * @class Record
7
 */
8
var Record = Y.Base.create('record', Y.Base, [], {
9
    _setId: function() {
10
        return Y.guid();
11
    },
12
 
13
    initializer: function() {
14
    },
15
 
16
    destructor: function() {
17
    },
18
 
19
    /**
20
     * Retrieve a particular (or all) values from the object
21
     *
22
     * @param field {string} (optional) The key to retrieve the value from. If not supplied, the entire object is returned.
23
     * @method getValue
24
     * @public
25
     */
26
    getValue: function(field) {
27
        if (field === undefined) {
28
            return this.get("data");
29
        }
30
        else {
31
            return this.get("data")[field];
32
        }
33
        return null;
34
    }
35
},
36
{
37
    ATTRS: {
38
 
39
        /**
40
        * @description Unique ID of the record instance
41
        * @attribute id
42
        * @type string
43
        */
44
        id: {
45
            valueFn: "_setId"
46
        },
47
 
48
        /**
49
        * @description The object stored within the record instance
50
        * @attribute data
51
        * @type object
52
        */
53
        data: {
54
            value: null
55
        }
56
    }
57
});
58
 
59
Y.Record = Record;
60
/**
61
The Recordset utility provides a standard way for dealing with
62
a collection of similar objects.
63
@module recordset
64
@main recordset
65
@submodule recordset-base
66
**/
67
 
68
 
69
var ArrayList = Y.ArrayList,
70
Lang = Y.Lang,
71
 
72
/**
73
The Recordset utility provides a standard way for dealing with
74
a collection of similar objects.
75
 
76
Provides the base Recordset implementation, which can be extended to add
77
additional functionality, such as custom indexing. sorting, and filtering.
78
 
79
@class Recordset
80
@extends Base
81
@uses ArrayList
82
@param config {Object} Configuration object with initial attribute values
83
@constructor
84
**/
85
Recordset = Y.Base.create('recordset', Y.Base, [], {
86
 
87
 
88
    /**
89
     * Publish default functions for events. Create the initial hash table.
90
     *
91
     * @method initializer
92
     * @protected
93
     */
94
    initializer: function() {
95
        // The reason the conditional is needed is because of two scenarios:
96
        // 1. Instantiating new Y.Recordset() will not go into the setter of "records", and so it is necessary to create this._items in the initializer.
97
        // 2. Instantiating new Y.Recordset({records: [{...}]}) will call the setter of "records" and create this._items. In this case, we don't want that to be overwritten by [].
98
        if (!this._items) {
99
            this._items = [];
100
        }
101
 
102
        //set up event listener to fire events when recordset is modified in anyway
103
        this.publish({
104
            /**
105
             * <p>At least one record is being added. Additional properties of
106
             * the event are:</p>
107
             * <dl>
108
             *     <dt>added</dt>
109
             *         <dd>Array of new records to be added</dd>
110
             *     <dt>index</dt>
111
             *         <dd>The insertion index in the Recordset's internal
112
             *         array</dd>
113
             * </dl>
114
             *
115
             * <p>Preventing this event will cause the new records NOT to be
116
             * added to the Recordset's internal collection.</p>
117
             *
118
             * @event add
119
             * @preventable _defAddFn
120
             */
121
            add: { defaultFn: this._defAddFn },
122
 
123
            /**
124
             * <p>At least one record is being removed. Additional properties of
125
             * the event are:</p>
126
             * <dl>
127
             *     <dt>removed</dt>
128
             *         <dd>Array of records to be removed</dd>
129
             *     <dt>range</dt>
130
             *         <dd>Number of records to be removed</dd>
131
             *     <dt>index</dt>
132
             *         <dd>The starting index in the Recordset's internal
133
             *         array from which to remove records</dd>
134
             * </dl>
135
             *
136
             * <p>Preventing this event will cause the records NOT to be
137
             * removed from the Recordset's internal collection.</p>
138
             *
139
             * @event remove
140
             * @preventable _defRemoveFn
141
             */
142
            remove: { defaultFn: this._defRemoveFn },
143
 
144
            /**
145
             * The Recordset is being flushed of all records.
146
             *
147
             * @event empty
148
             * @preventable _defEmptyFn
149
             */
150
            empty: { defaultFn: this._defEmptyFn },
151
 
152
            /**
153
             * <p>At least one record is being updated. Additional properties of
154
             * the event are:</p>
155
             * <dl>
156
             *     <dt>updated</dt>
157
             *         <dd>Array of records with updated values</dd>
158
             *     <dt>overwritten</dt>
159
             *         <dd>Array of current records that will be replaced</dd>
160
             *     <dt>index</dt>
161
             *         <dd>The starting index in the Recordset's internal
162
             *         array from which to update will apply</dd>
163
             * </dl>
164
             *
165
             * <p>Preventing this event will cause the records NOT to be
166
             * updated in the Recordset's internal collection.</p>
167
             *
168
             * @event update
169
             * @preventable _defUpdateFn
170
             */
171
            update: { defaultFn: this._defUpdateFn }
172
        });
173
 
174
        this._buildHashTable(this.get('key'));
175
 
176
        this.after([
177
            'recordsChange',
178
            'add',
179
            'remove',
180
            'update',
181
            'empty'], this._updateHash);
182
    },
183
 
184
    /**
185
     * Returns the record with particular ID or index
186
     *
187
     * @method getRecord
188
     * @param i {String, Number} The ID of the record if a string, or the index if a number.
189
     * @return {Record} A Y.Record instance
190
     */
191
    getRecord: function(i) {
192
 
193
        if (Lang.isString(i)) {
194
            return this.get('table')[i];
195
        }
196
        else if (Lang.isNumber(i)) {
197
            return this._items[i];
198
        }
199
        return null;
200
    },
201
 
202
 
203
    /**
204
     * Returns the record at a particular index
205
     *
206
     * @method getRecordByIndex
207
     * @param i {Number} Index at which the required record resides
208
     * @return {Record} A Y.Record instance
209
     */
210
    getRecordByIndex: function(i) {
211
        return this._items[i];
212
    },
213
 
214
    /**
215
     * Returns a range of records beginning at particular index
216
     *
217
     * @method getRecordsByIndex
218
     * @param index {Number} Index at which the required record resides
219
     * @param range {Number} (Optional) Number of records to retrieve. The default is 1
220
     * @return {Array} An array of Y.Record instances
221
     */
222
    getRecordsByIndex: function(index, range) {
223
        var i = 0,
224
        returnedRecords = [];
225
        //Range cannot take on negative values
226
        range = (Lang.isNumber(range) && (range > 0)) ? range: 1;
227
 
228
        for (; i < range; i++) {
229
            returnedRecords.push(this._items[index + i]);
230
        }
231
        return returnedRecords;
232
    },
233
 
234
    /**
235
     * Returns the length of the recordset
236
     *
237
     * @method getLength
238
     * @return {Number} Number of records in the recordset
239
     */
240
    getLength: function() {
241
        return this.size();
242
    },
243
 
244
    /**
245
    Gets an array of values for a data _key_ in the set's records.  If no _key_
246
    is supplied, the returned array will contain the full data object for each
247
    record.
248
 
249
    @method getValuesByKey
250
    @param {String} [key] Data property to get from all records
251
    @return {Array} An array of values for the given _key_ if supplied.
252
        Otherwise, an array of each record's data hash.
253
    **/
254
    getValuesByKey: function(key) {
255
        var i = 0,
256
        len = this._items.length,
257
        retVals = [];
258
        for (; i < len; i++) {
259
            retVals.push(this._items[i].getValue(key));
260
        }
261
        return retVals;
262
    },
263
 
264
 
265
    /**
266
     * Adds one or more Records to the RecordSet at the given index. If index is null, then adds the Records to the end of the RecordSet.
267
     *
268
     * @method add
269
     * @param {Record|Object|Array} oData A Y.Record instance, An object literal of data or an array of object literals
270
     * @param [index] {Number} [index] Index at which to add the record(s)
271
     * @return {Recordset} The updated recordset instance
272
     */
273
    add: function(oData, index) {
274
 
275
        var newRecords = [],
276
        idx,
277
        i = 0;
278
 
279
        idx = (Lang.isNumber(index) && (index > -1)) ? index: this._items.length;
280
 
281
        //Passing in array of object literals for oData
282
        if (Lang.isArray(oData)) {
283
            for (; i < oData.length; i++) {
284
                newRecords[i] = this._changeToRecord(oData[i]);
285
            }
286
        } else if (Lang.isObject(oData)) {
287
            newRecords[0] = this._changeToRecord(oData);
288
        }
289
 
290
        this.fire('add', {
291
            added: newRecords,
292
            index: idx
293
        });
294
        return this;
295
    },
296
 
297
    /**
298
    Removes one or more Records to the RecordSet at the given index. If index
299
    is null, then removes a single Record from the end of the RecordSet.
300
 
301
    @method remove
302
    @param {Number} [index] Index at which to remove the record(s) from
303
    @param {Number} [range] Number of records to remove (including the one
304
        at the index)
305
    @return {Recordset} The updated recordset instance
306
    **/
307
    remove: function(index, range) {
308
        var remRecords = [];
309
 
310
        //Default is to only remove the last record - the length is always 1 greater than the last index
311
        index = (index > -1) ? index: (this._items.length - 1);
312
        range = (range > 0) ? range: 1;
313
 
314
        remRecords = this._items.slice(index, (index + range));
315
        this.fire('remove', {
316
            removed: remRecords,
317
            range: range,
318
            index: index
319
        });
320
        //this._recordRemoved(remRecords, index);
321
        //return ({data: remRecords, index:index});
322
        return this;
323
    },
324
 
325
    /**
326
     * Empties the recordset
327
     *
328
     * @method empty
329
     * @return {Recordset} The updated recordset instance
330
     */
331
    empty: function() {
332
        this.fire('empty', {});
333
        return this;
334
    },
335
 
336
    /**
337
    Updates the recordset with the new records passed in. Overwrites existing
338
    records when updating the index with the new records.
339
 
340
    @method update
341
    @param {Record|Object|Array} data A Y.Record instance, An object literal of
342
        data or an array of object literals
343
    @param {Number} [index] The index to start updating from.
344
    @return {Recordset} The updated recordset instance
345
    **/
346
    update: function(data, index) {
347
        var rec,
348
            arr,
349
            i = 0;
350
 
351
        // Whatever is passed in, we are changing it to an array so that it can
352
        // be easily iterated in the _defUpdateFn method
353
        arr = (!(Lang.isArray(data))) ? [data] : data;
354
        rec = this._items.slice(index, index + arr.length);
355
 
356
        for (; i < arr.length; i++) {
357
            arr[i] = this._changeToRecord(arr[i]);
358
        }
359
 
360
        this.fire('update', {
361
            updated: arr,
362
            overwritten: rec,
363
            index: index
364
        });
365
 
366
        return this;
367
    },
368
 
369
    /**
370
     * Default behavior for the "add" event. Adds Record instances starting from
371
     * the index specified in `e.index`.
372
     *
373
     * @method _defAddFn
374
     * @param {EventFacade} e The add event
375
     * @private
376
     */
377
    _defAddFn: function(e) {
378
        this._items.splice.apply(this._items, [e.index, 0].concat(e.added));
379
    },
380
 
381
    /**
382
     * Default behavior for the "remove" event. Removes Records from the
383
     * internal array starting from `e.index`.  By default, it will remove one
384
     * Record. But if `e.range` is set, it will remove that many Records.
385
     *
386
     * @method _defRemoveFn
387
     * @param {EventFacade} e The remove event
388
     * @private
389
     */
390
    _defRemoveFn: function(e) {
391
        this._items.splice(e.index, e.range || 1);
392
    },
393
 
394
    /**
395
     * Default behavior for the "update" event. Sets Record instances for each
396
     * item in `e.updated` at indexes starting from `e.index`.
397
     *
398
     * @method _defUpdateFn
399
     * @param {EventFacade} e The update event
400
     * @private
401
     */
402
    _defUpdateFn: function(e) {
403
        for (var i = 0; i < e.updated.length; i++) {
404
            this._items[e.index + i] = this._changeToRecord(e.updated[i]);
405
        }
406
    },
407
 
408
    /**
409
     * Default behavior for the "empty" event. Clears the internal array of
410
     * Records.
411
     *
412
     * @method _defEmptyFn
413
     * @param {EventFacade} e The empty event
414
     * @private
415
     */
416
    _defEmptyFn: function(e) {
417
        this._items = [];
418
        Y.log('empty fired');
419
    },
420
 
421
    /**
422
    Updates the internal hash table.
423
 
424
    @method _defUpdateHash
425
    @param {EventFacade} e Event triggering the hash table update
426
    @private
427
    **/
428
    _updateHash: function (e) {
429
        var handler = "_hash",
430
            type = e.type.replace(/.*:/,''),
431
            newHash;
432
 
433
        // _hashAdd, _hashRemove, _hashEmpty, etc
434
        // Not a switch or else if setup to allow for external expansion.
435
        handler += type.charAt(0).toUpperCase() + type.slice(1);
436
 
437
        newHash = this[handler] &&
438
                    this[handler](this.get('table'), this.get('key'), e);
439
 
440
        if (newHash) {
441
            this.set('table', newHash);
442
        }
443
    },
444
 
445
    /**
446
    Regenerates the hash table from the current internal array of Records.
447
 
448
    @method _hashRecordsChange
449
    @param {Object} hash The hash map before replacement
450
    @param {String} key The key by which to add items to the hash
451
    @param {Object} e The event or object containing the items to be added.
452
                      Items are expected to be stored in an array assigned to
453
                      the `added` property.
454
    @return {Object} The updated hash map
455
    @private
456
    **/
457
    _hashRecordsChange: function (hash, key, e) {
458
        return this._buildHashTable(key);
459
    },
460
 
461
    /**
462
    Builds a hash table from the current internal array of Records.
463
 
464
    @method _buildHashTable
465
    @param {String} key The Record key to hash the items by
466
    @return {Object} A new hash map of Records keyed by each Records' key
467
    @private
468
    **/
469
    _buildHashTable: function (key) {
470
        return this._hashAdd({}, key, { added: this._items });
471
    },
472
 
473
    /**
474
    Adds items to the hash table.  Items are the values, and the keys are the
475
    values of the item's attribute named in the `key` parameter.
476
 
477
    @method _hashAdd
478
    @param {Object} hash The hash map before adding items
479
    @param {String} key The key by which to add the items to the hash
480
    @param {Object} e The event or object containing the items to be added.
481
                      Items are expected to be stored in an array assigned to
482
                      the `added` property.
483
    @return {Object} The updated hash map
484
    @private
485
    **/
486
    _hashAdd: function(hash, key, e) {
487
        var items = e.added,
488
            i, len;
489
 
490
        for (i = 0, len = e.added.length; i < len; ++i) {
491
            hash[items[i].get(key)] = items[i];
492
        }
493
 
494
        return hash;
495
    },
496
 
497
    /**
498
    Removes items from the hash table.
499
 
500
    @method _hashRemove
501
    @param {Object} hash The hash map before removing items
502
    @param {String} key The key by which to remove the items from the hash
503
    @param {Object} e The event or object containing the items to be removed.
504
                      Items are expected to be stored in an array assigned to
505
                      the `removed` property.
506
    @return {Object} The updated hash map
507
    @private
508
    **/
509
    _hashRemove: function(hash, key, e) {
510
        for (var i = e.removed.length - 1; i >= 0; --i) {
511
            delete hash[e.removed[i].get(key)];
512
        }
513
 
514
        return hash;
515
    },
516
 
517
    /**
518
    Updates items in the hash table.
519
 
520
    @method _hashUpdate
521
    @param {Object} hash The hash map before updating items
522
    @param {String} key The key by which to update the items to the hash
523
    @param {Object} e The event or object containing the items to be updated.
524
                      Items are expected to be stored in an array assigned to
525
                      the `updated` property. Optionally, items can be
526
                      identified for being overwritten by including them in an
527
                      array assigned to the `overwritten` property.
528
    @return {Object} The updated hash map
529
    @private
530
    **/
531
    _hashUpdate: function (hash, key, e) {
532
        if (e.overwritten && e.overwritten.length) {
533
            hash = this._hashRemove(hash, key, { removed: e.overwritten });
534
        }
535
 
536
        return this._hashAdd(hash, key, { added: e.updated });
537
    },
538
 
539
    /**
540
    Clears the hash table.
541
 
542
    @method _hashEmpty
543
    @param {Object} hash The hash map before adding items
544
    @param {String} key The key by which to remove the items from the hash
545
    @param {Object} e The event or object containing the items to be removed.
546
                      Items are expected to be stored in an array assigned to
547
                      the `removed` property.
548
    @return {Object} An empty hash
549
    @private
550
    **/
551
    _hashEmpty: function() {
552
        return {};
553
    },
554
 
555
    /**
556
     * Sets up the hashtable with all the records currently in the recordset
557
     *
558
     * @method _initHashTable
559
     * @private
560
     */
561
    _initHashTable: function() {
562
        return this._hashAdd({}, this.get('key'), { added: this._items || [] });
563
    },
564
 
565
    /**
566
     * Helper method - it takes an object bag and converts it to a Y.Record
567
     *
568
     * @method _changeToRecord
569
     * @param obj {Object|Record} Any objet literal or Y.Record instance
570
     * @return {Record} A Record instance.
571
     * @private
572
     */
573
    _changeToRecord: function(obj) {
574
        return (obj instanceof Y.Record) ? obj : new Y.Record({ data: obj });
575
    },
576
 
577
    /**
578
    Ensures the value being set is an array of Record instances. If array items
579
    are raw object data, they are turned into Records.
580
 
581
    @method _setRecords
582
    @param {Record[]|Object[]} items The Records or data Objects to store as
583
                                     Records.
584
    @return {Record[]}
585
    **/
586
    _setRecords: function (items) {
587
        if (!Y.Lang.isArray(items)) {
588
            return Y.Attribute.INVALID_VALUE;
589
        }
590
 
591
        var records = [],
592
            i, len;
593
 
594
        // FIXME: This should use the flyweight pattern if possible
595
        for (i = 0, len = items.length; i < len; ++i) {
596
            records[i] = this._changeToRecord(items[i]);
597
        }
598
 
599
        return (this._items = records);
600
    }
601
}, {
602
    ATTRS: {
603
 
604
        /**
605
        * An array of Records that the Recordset is storing.  Passing an array
606
        * of raw record data is also accepted.  The data for each item will be
607
        * wrapped in a Record instance.
608
        *
609
        * @attribute records
610
        * @type {Record[]}
611
        */
612
        records: {
613
            // TODO: necessary? valueFn?
614
            lazyAdd: false,
615
            getter: function() {
616
                // give them a copy, not the internal object
617
                return Y.Array(this._items);
618
            },
619
            setter: "_setRecords"
620
        },
621
 
622
        /**
623
        A hash table where the ID of the record is the key, and the record
624
        instance is the value.
625
 
626
        @attribute table
627
        @type object
628
        **/
629
        table: {
630
            valueFn: '_initHashTable'
631
        },
632
 
633
        /**
634
        The ID to use as the key in the hash table.
635
 
636
        @attribute key
637
        @type string
638
        **/
639
        key: {
640
            value: 'id',
641
            readOnly: true
642
        }
643
 
644
    }
645
});
646
Y.augment(Recordset, ArrayList);
647
Y.Recordset = Recordset;
648
 
649
 
650
 
651
}, '3.18.1', {"requires": ["base", "arraylist"]});