2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
8 YUI.add('recordset-base', function(Y) {
11 * Provides a wrapper around a standard javascript object. Can be inserted into a Recordset instance.
15 var Record = Y.Base.create('record', Y.Base, [], {
20 initializer: function() {
23 destructor: function() {
27 * Retrieve a particular (or all) values from the object
29 * @param field {string} (optional) The key to retrieve the value from. If not supplied, the entire object is returned.
33 getValue: function(field) {
34 if (field === undefined) {
35 return this.get("data");
38 return this.get("data")[field];
47 * @description Unique ID of the record instance
56 * @description The object stored within the record instance
69 * The Recordset utility provides a standard way for dealing with
70 * a collection of similar objects.
72 * @submodule recordset-base
76 var ArrayList = Y.ArrayList,
80 * The Recordset utility provides a standard way for dealing with
81 * a collection of similar objects.
83 * Provides the base Recordset implementation, which can be extended to add
84 * additional functionality, such as custom indexing. sorting, and filtering.
89 * @param config {Object} Configuration object literal with initial attribute values
93 Recordset = Y.Base.create('recordset', Y.Base, [], {
97 * @description Publish default functions for events. Create the initial hash table.
102 initializer: function() {
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.
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 [].
118 //set up event listener to fire events when recordset is modified in anyway
119 this.publish('add', {
120 defaultFn: this._defAddFn
122 this.publish('remove', {
123 defaultFn: this._defRemoveFn
125 this.publish('empty', {
126 defaultFn: this._defEmptyFn
128 this.publish('update', {
129 defaultFn: this._defUpdateFn
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
138 destructor: function() {
142 * @description Helper method called upon by add() - it is used to create a new record(s) in the recordset
145 * @return {Y.Record} A Record instance.
148 _defAddFn: function(e) {
149 var len = this._items.length,
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
157 this._items.push(recs[i]);
160 this._items.splice(index, 0, recs[i]);
169 * @description Helper method called upon by remove() - it is used to remove record(s) from the recordset
171 * @method _defRemoveFn
174 _defRemoveFn: function(e) {
176 //remove from beginning
182 else if (e.index === this._items.length - 1) {
188 this._items.splice(e.index, e.range);
193 * Helper method called upon by empty() - it is used to empty the recordset
195 * @method _defEmptyFn
198 _defEmptyFn: function(e) {
203 * @description Helper method called upon by update() - it is used to update the recordset
205 * @method _defUpdateFn
208 _defUpdateFn: function(e) {
210 for (var i = 0; i < e.updated.length; i++) {
211 this._items[e.index + i] = this._changeToRecord(e.updated[i]);
216 //---------------------------------------------
217 // Hash Table Methods
218 //---------------------------------------------
221 * @description Method called whenever "recordset:add" event is fired. It adds the new record(s) to the hashtable.
223 * @method _defAddHash
226 _defAddHash: function(e) {
227 var obj = this.get('table'),
228 key = this.get('key'),
230 for (; i < e.added.length; i++) {
231 obj[e.added[i].get(key)] = e.added[i];
233 this.set('table', obj);
237 * @description Method called whenever "recordset:remove" event is fired. It removes the record(s) from the recordset.
239 * @method _defRemoveHash
242 _defRemoveHash: function(e) {
243 var obj = this.get('table'),
244 key = this.get('key'),
246 for (; i < e.removed.length; i++) {
247 delete obj[e.removed[i].get(key)];
249 this.set('table', obj);
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.
256 * @method _defUpdateHash
259 _defUpdateHash: function(e) {
260 var obj = this.get('table'),
261 key = this.get('key'),
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)];
270 obj[e.updated[i].get(key)] = e.updated[i];
272 this.set('table', obj);
276 * @description Method called whenever "recordset:empty" event is fired. It empties the hash table.
278 * @method _defEmptyHash
281 _defEmptyHash: function() {
282 this.set('table', {});
286 * @description Sets up the hashtable with all the records currently in the recordset
288 * @method _setHashTable
291 _setHashTable: function() {
293 key = this.get('key'),
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];
308 * @description Helper method - it takes an object bag and converts it to a Y.Record
310 * @method _changeToRecord
311 * @param obj {Object || Y.Record} Any objet literal or Y.Record instance
312 * @return {Y.Record} A Record instance.
315 _changeToRecord: function(obj) {
317 if (obj instanceof Y.Record) {
321 oRec = new Y.Record({
329 //---------------------------------------------
331 //---------------------------------------------
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)
335 * @method _recordSetUpdated
338 _recordsetChanged: function() {
340 this.on(['update', 'add', 'remove', 'empty'],
342 this.fire('change', {});
348 * @description Syncs up the private hash methods with their appropriate triggering events.
350 * @method _syncHashTable
353 _syncHashTable: function() {
361 this._defRemoveHash(e);
365 this._defUpdateHash(e);
369 this._defEmptyHash();
374 //---------------------------------------------
376 //---------------------------------------------
378 * @description Returns the record with particular ID or index
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
385 getRecord: function(i) {
387 if (Lang.isString(i)) {
388 return this.get('table')[i];
390 else if (Lang.isNumber(i)) {
391 return this._items[i];
398 * @description Returns the record at a particular index
400 * @method getRecordByIndex
401 * @param i {Number} Index at which the required record resides
402 * @return {Y.Record} An Y.Record instance
405 getRecordByIndex: function(i) {
406 return this._items[i];
410 * @description Returns a range of records beginning at particular index
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
418 getRecordsByIndex: function(index, range) {
420 returnedRecords = [];
421 //Range cannot take on negative values
422 range = (Lang.isNumber(range) && (range > 0)) ? range: 1;
424 for (; i < range; i++) {
425 returnedRecords.push(this._items[index + i]);
427 return returnedRecords;
431 * @description Returns the length of the recordset
434 * @return {Number} Number of records in the recordset
437 getLength: function() {
442 * @description Returns an array of values for a specified key in the recordset
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
449 getValuesByKey: function(key) {
451 len = this._items.length,
453 for (; i < len; i++) {
454 retVals.push(this._items[i].getValue(key));
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.
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
469 add: function(oData, index) {
475 idx = (Lang.isNumber(index) && (index > -1)) ? index: this._items.length;
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]);
486 else if (Lang.isObject(oData)) {
487 newRecords[0] = this._changeToRecord(oData);
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.
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
506 remove: function(index, range) {
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;
513 remRecords = this._items.slice(index, (index + range));
514 this.fire('remove', {
519 //this._recordRemoved(remRecords, index);
520 //return ({data: remRecords, index:index});
525 * @description Empties the recordset
528 * @return {Y.Recordset} The updated recordset instance
532 this.fire('empty', {});
537 * @description Updates the recordset with the new records passed in. Overwrites existing records when updating the index with the new records.
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
545 update: function(data, index) {
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);
554 for (; i < arr.length; i++) {
555 arr[i] = this._changeToRecord(arr[i]);
558 this.fire('update', {
573 * @description An array of records that the recordset is storing
580 validator: Lang.isArray,
582 // give them a copy, not the internal object
583 return new Y.Array(this._items);
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.
590 function initRecord(oneData) {
593 if (oneData instanceof Y.Record) {
594 records.push(oneData);
604 //This conditional statement handles creating empty recordsets
606 Y.Array.each(allData, initRecord);
607 this._items = Y.Array(records);
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
620 * @description A hash table where the ID of the record is the key, and the record instance is the value.
627 //Initially, create the hash table with all records currently in the recordset
628 valueFn: '_setHashTable'
632 * @description The ID to use as the key in the hash table.
640 //set to readonly true. If you want custom hash tables, you should use the recordset-indexer plugin.
646 Y.augment(Recordset, ArrayList);
647 Y.Recordset = Recordset;
652 }, '3.3.0' ,{requires:['base','arraylist']});