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