recordset.js revision 7c27e5753536379eb04779844dfe64f0f3063248
0N/AYUI.add('recordset-base', function(Y) {
0N/A
0N/A/**
0N/A * Provides a wrapper around a standard javascript object. Can be inserted into a Recordset instance.
407N/A *
0N/A * @class Record
0N/A */
0N/Avar Record = Y.Base.create('record', Y.Base, [], {
0N/A _setId: function() {
0N/A return Y.guid();
0N/A },
0N/A
0N/A initializer: function() {
0N/A },
0N/A
0N/A destructor: function() {
0N/A },
0N/A
1220N/A /**
0N/A * Retrieve a particular (or all) values from the object
1297N/A *
1185N/A * @param field {string} (optional) The key to retrieve the value from. If not supplied, the entire object is returned.
0N/A * @method getValue
0N/A * @public
0N/A */
1185N/A getValue: function(field) {
1185N/A if (field === undefined) {
1185N/A return this.get("data");
154N/A }
1185N/A else {
1185N/A return this.get("data")[field];
159N/A }
154N/A return null;
159N/A }
1340N/A},
154N/A{
154N/A ATTRS: {
1124N/A
1185N/A /**
1340N/A * @description Unique ID of the record instance
434N/A * @attribute id
1219N/A * @type string
1297N/A */
1297N/A id: {
1185N/A valueFn: "_setId"
1185N/A },
1185N/A
434N/A /**
259N/A * @description The object stored within the record instance
89N/A * @attribute data
1124N/A * @type object
1124N/A */
1195N/A data: {
0N/A value: null
0N/A }
1185N/A }
0N/A});
456N/A
1185N/AY.Record = Record;
1185N/A/**
1185N/A * The Recordset utility provides a standard way for dealing with
1185N/A * a collection of similar objects.
1340N/A * @module recordset
1340N/A * @submodule recordset-base
92N/A */
92N/A
1190N/A
1185N/Avar ArrayList = Y.ArrayList,
1185N/ALang = Y.Lang,
92N/A
92N/A/**
345N/A * The Recordset utility provides a standard way for dealing with
0N/A * a collection of similar objects.
345N/A *
0N/A * Provides the base Recordset implementation, which can be extended to add
0N/A * additional functionality, such as custom indexing. sorting, and filtering.
92N/A *
92N/A * @class Recordset
1185N/A * @extends Base
1185N/A * @augments ArrayList
1190N/A * @param config {Object} Configuration object literal with initial attribute values
1185N/A * @constructor
1185N/A */
92N/A
1185N/ARecordset = Y.Base.create('recordset', Y.Base, [], {
1185N/A
1185N/A
1185N/A /**
1185N/A * @description Publish default functions for events. Create the initial hash table.
1185N/A *
1185N/A * @method initializer
1185N/A * @protected
1185N/A */
1190N/A initializer: function() {
1185N/A
1185N/A /*
1185N/A If this._items does not exist, then create it and set it to an empty array.
1185N/A The reason the conditional is needed is because of two scenarios:
1185N/A Instantiating new Y.Recordset() will not go into the setter of "records", and so
1205N/A it is necessary to create this._items in the initializer.
1185N/A
1205N/A Instantiating new Y.Recordset({records: [{...}]}) will call the setter of "records" and create
1185N/A this._items. In this case, we don't want that to be overwritten by [].
1205N/A */
1185N/A
0N/A if (!this._items) {
0N/A this._items = [];
92N/A }
92N/A
1190N/A //set up event listener to fire events when recordset is modified in anyway
1185N/A this.publish('add', {
1190N/A defaultFn: this._defAddFn
1185N/A });
1185N/A this.publish('remove', {
92N/A defaultFn: this._defRemoveFn
1205N/A });
1185N/A this.publish('empty', {
1185N/A defaultFn: this._defEmptyFn
1185N/A });
1185N/A this.publish('update', {
1185N/A defaultFn: this._defUpdateFn
1185N/A });
1185N/A
1185N/A this._recordsetChanged();
1185N/A //Fires recordset changed event when any updates are made to the recordset
1185N/A this._syncHashTable();
1185N/A //Fires appropriate hashTable methods on "add", "remove", "update" and "empty" events
1185N/A },
1185N/A
1185N/A destructor: function() {
1185N/A },
92N/A
92N/A /**
92N/A * @description Helper method called upon by add() - it is used to create a new record(s) in the recordset
1185N/A *
1185N/A * @method _defAddFn
92N/A * @return {Y.Record} A Record instance.
1185N/A * @private
1185N/A */
1185N/A _defAddFn: function(e) {
1190N/A var len = this._items.length,
990N/A recs = e.added,
990N/A index = e.index,
990N/A i = 0;
990N/A //index = (Lang.isNumber(index) && (index > -1)) ? index : len;
990N/A for (; i < recs.length; i++) {
990N/A //if records are to be added one at a time, push them in one at a time
604N/A if (index === len) {
1185N/A this._items.push(recs[i]);
1185N/A }
1185N/A else {
1185N/A this._items.splice(index, 0, recs[i]);
1190N/A index++;
604N/A }
604N/A }
1185N/A
1185N/A
0N/A },
154N/A
583N/A /**
583N/A * @description Helper method called upon by remove() - it is used to remove record(s) from the recordset
583N/A *
583N/A * @method _defRemoveFn
604N/A * @private
604N/A */
1205N/A _defRemoveFn: function(e) {
1185N/A
1190N/A //remove from beginning
1185N/A if (e.index === 0) {
1185N/A this._items.shift();
1185N/A }
1190N/A
1185N/A //remove from end
1185N/A else if (e.index === this._items.length - 1) {
604N/A this._items.pop();
1185N/A }
1185N/A
1185N/A //remove from middle
1185N/A else {
1185N/A this._items.splice(e.index, e.range);
1185N/A }
1205N/A },
1185N/A
1190N/A /**
1185N/A * Helper method called upon by empty() - it is used to empty the recordset
1185N/A *
1185N/A * @method _defEmptyFn
1185N/A * @private
1185N/A */
1190N/A _defEmptyFn: function(e) {
1190N/A this._items = [];
1185N/A },
1185N/A
1185N/A /**
1185N/A * @description Helper method called upon by update() - it is used to update the recordset
1185N/A *
1185N/A * @method _defUpdateFn
1185N/A * @private
1185N/A */
1185N/A _defUpdateFn: function(e) {
1185N/A
1185N/A for (var i = 0; i < e.updated.length; i++) {
1185N/A this._items[e.index + i] = this._changeToRecord(e.updated[i]);
1185N/A }
604N/A },
604N/A
604N/A
604N/A //---------------------------------------------
1185N/A // Hash Table Methods
1185N/A //---------------------------------------------
1185N/A
1185N/A /**
1185N/A * @description Method called whenever "recordset:add" event is fired. It adds the new record(s) to the hashtable.
1185N/A *
1190N/A * @method _defAddHash
1185N/A * @private
1185N/A */
1185N/A _defAddHash: function(e) {
1185N/A var obj = this.get('table'),
1185N/A key = this.get('key'),
1185N/A i = 0;
1185N/A for (; i < e.added.length; i++) {
1185N/A obj[e.added[i].get(key)] = e.added[i];
1185N/A }
1185N/A this.set('table', obj);
1185N/A },
1185N/A
1185N/A /**
1185N/A * @description Method called whenever "recordset:remove" event is fired. It removes the record(s) from the recordset.
1185N/A *
1190N/A * @method _defRemoveHash
1185N/A * @private
604N/A */
1185N/A _defRemoveHash: function(e) {
1185N/A var obj = this.get('table'),
1185N/A key = this.get('key'),
1185N/A i = 0;
1185N/A for (; i < e.removed.length; i++) {
1185N/A delete obj[e.removed[i].get(key)];
1185N/A }
1185N/A this.set('table', obj);
1185N/A },
1185N/A
1206N/A
1206N/A /**
1185N/A * @description Method called whenever "recordset:update" event is fired. It updates the record(s) by adding the new ones and removing the overwritten ones.
1185N/A *
1185N/A * @method _defUpdateHash
1185N/A * @private
1205N/A */
1185N/A _defUpdateHash: function(e) {
1185N/A var obj = this.get('table'),
1185N/A key = this.get('key'),
1185N/A i = 0;
604N/A
1185N/A //deletes the object key that held on to an overwritten record and
1185N/A //creates an object key to hold on to the updated record
1185N/A for (; i < e.updated.length; i++) {
1185N/A if (e.overwritten[i]) {
1185N/A delete obj[e.overwritten[i].get(key)];
1205N/A }
1205N/A obj[e.updated[i].get(key)] = e.updated[i];
1185N/A }
1185N/A this.set('table', obj);
1185N/A },
1185N/A
1185N/A /**
1185N/A * @description Method called whenever "recordset:empty" event is fired. It empties the hash table.
604N/A *
604N/A * @method _defEmptyHash
604N/A * @private
1190N/A */
1190N/A _defEmptyHash: function() {
1185N/A this.set('table', {});
1190N/A },
1190N/A
1185N/A /**
1185N/A * @description Sets up the hashtable with all the records currently in the recordset
1185N/A *
1185N/A * @method _setHashTable
1190N/A * @private
1190N/A */
1190N/A _setHashTable: function() {
1190N/A var obj = {},
1190N/A key = this.get('key'),
1185N/A i = 0;
1185N/A
1185N/A //If it is not an empty recordset - go through and set up the hash table.
1185N/A if (this._items && this._items.length > 0) {
604N/A var len = this._items.length;
1185N/A for (; i < len; i++) {
1185N/A obj[this._items[i].get(key)] = this._items[i];
1185N/A }
1185N/A }
1185N/A return obj;
1185N/A },
1185N/A
604N/A
1185N/A /**
1185N/A * @description Helper method - it takes an object bag and converts it to a Y.Record
1185N/A *
1185N/A * @method _changeToRecord
0N/A * @param obj {Object || Y.Record} Any objet literal or Y.Record instance
1185N/A * @return {Y.Record} A Record instance.
1190N/A * @private
1185N/A */
1185N/A _changeToRecord: function(obj) {
1185N/A var oRec;
1185N/A if (obj instanceof Y.Record) {
1185N/A oRec = obj;
604N/A }
604N/A else {
1297N/A oRec = new Y.Record({
1297N/A data: obj
1297N/A });
1297N/A }
1297N/A
1297N/A return oRec;
1297N/A },
1297N/A
1297N/A //---------------------------------------------
1297N/A // Events
1297N/A //---------------------------------------------
1297N/A /**
1297N/A * @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)
1297N/A *
1297N/A * @method _recordSetUpdated
1297N/A * @private
1297N/A */
1297N/A _recordsetChanged: function() {
1297N/A
1297N/A this.on(['update', 'add', 'remove', 'empty'],
1297N/A function() {
1297N/A this.fire('change', {});
604N/A });
1185N/A },
1185N/A
1185N/A
1190N/A /**
1185N/A * @description Syncs up the private hash methods with their appropriate triggering events.
1185N/A *
1185N/A * @method _syncHashTable
1185N/A * @private
1185N/A */
604N/A _syncHashTable: function() {
1185N/A
1185N/A this.after('add',
1185N/A function(e) {
1185N/A this._defAddHash(e);
1185N/A });
1185N/A this.after('remove',
1185N/A function(e) {
1185N/A this._defRemoveHash(e);
1185N/A });
1185N/A this.after('update',
1185N/A function(e) {
1233N/A this._defUpdateHash(e);
1185N/A });
605N/A this.after('update',
1185N/A function(e) {
1185N/A this._defEmptyHash();
1185N/A });
1185N/A
605N/A },
605N/A
1185N/A //---------------------------------------------
605N/A // Public Methods
605N/A //---------------------------------------------
1185N/A /**
1185N/A * @description Returns the record with particular ID or index
604N/A *
604N/A * @method getRecord
604N/A * @param i {String, Number} The ID of the record if a string, or the index if a number.
1185N/A * @return {Y.Record} An Y.Record instance
1185N/A * @public
1190N/A */
1185N/A getRecord: function(i) {
1185N/A
604N/A if (Lang.isString(i)) {
604N/A return this.get('table')[i];
604N/A }
604N/A else if (Lang.isNumber(i)) {
604N/A return this._items[i];
1185N/A }
1185N/A return null;
604N/A },
0N/A
604N/A
604N/A /**
0N/A * @description Returns the record at a particular index
154N/A *
1205N/A * @method getRecordByIndex
1185N/A * @param i {Number} Index at which the required record resides
1185N/A * @return {Y.Record} An Y.Record instance
1185N/A * @public
1185N/A */
1185N/A getRecordByIndex: function(i) {
1185N/A return this._items[i];
1185N/A },
1185N/A
1185N/A /**
154N/A * @description Returns a range of records beginning at particular index
1185N/A *
154N/A * @method getRecordsByIndex
1185N/A * @param index {Number} Index at which the required record resides
0N/A * @param range {Number} (Optional) Number of records to retrieve. The default is 1
1185N/A * @return {Array} An array of Y.Record instances
0N/A * @public
0N/A */
154N/A getRecordsByIndex: function(index, range) {
878N/A var i = 0,
1185N/A returnedRecords = [];
1185N/A //Range cannot take on negative values
1190N/A range = (Lang.isNumber(range) && (range > 0)) ? range: 1;
1185N/A
1185N/A for (; i < range; i++) {
878N/A returnedRecords.push(this._items[index + i]);
878N/A }
878N/A return returnedRecords;
1205N/A },
1185N/A
972N/A /**
972N/A * @description Returns the length of the recordset
972N/A *
972N/A * @method getLength
972N/A * @return {Number} Number of records in the recordset
972N/A * @public
972N/A */
972N/A getLength: function() {
972N/A return this.size();
972N/A },
972N/A
972N/A /**
972N/A * @description Returns an array of values for a specified key in the recordset
972N/A *
972N/A * @method getValuesByKey
972N/A * @param index {Number} (optional) Index at which the required record resides
972N/A * @return {Array} An array of values for the given key
972N/A * @public
972N/A */
972N/A getValuesByKey: function(key) {
972N/A var i = 0,
1185N/A len = this._items.length,
972N/A retVals = [];
972N/A for (; i < len; i++) {
972N/A retVals.push(this._items[i].getValue(key));
972N/A }
972N/A return retVals;
972N/A },
972N/A
972N/A
972N/A /**
972N/A * @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.
878N/A *
1185N/A * @method add
1190N/A * @param oData {Y.Record, Object Literal, Array} A Y.Record instance, An object literal of data or an array of object literals
1185N/A * @param index {Number} (optional) Index at which to add the record(s)
1190N/A * @return {Y.Recordset} The updated recordset instance
1185N/A * @public
1185N/A */
1190N/A add: function(oData, index) {
1185N/A
1185N/A var newRecords = [],
1185N/A idx,
1185N/A i = 0;
1185N/A
1185N/A idx = (Lang.isNumber(index) && (index > -1)) ? index: this._items.length;
1185N/A
1185N/A
1185N/A
1185N/A //Passing in array of object literals for oData
0N/A if (Lang.isArray(oData)) {
54N/A for (; i < oData.length; i++) {
54N/A newRecords[i] = this._changeToRecord(oData[i]);
54N/A }
583N/A
1185N/A }
0N/A else if (Lang.isObject(oData)) {
0N/A newRecords[0] = this._changeToRecord(oData);
847N/A }
847N/A
583N/A this.fire('add', {
0N/A added: newRecords,
583N/A index: idx
89N/A });
89N/A return this;
168N/A },
1185N/A
168N/A /**
1185N/A * @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.
1185N/A *
168N/A * @method remove
850N/A * @param index {Number} (optional) Index at which to remove the record(s) from
168N/A * @param range {Number} (optional) Number of records to remove (including the one at the index)
1185N/A * @return {Y.Recordset} The updated recordset instance
1185N/A * @public
1185N/A */
1185N/A remove: function(index, range) {
878N/A var remRecords = [];
583N/A
168N/A //Default is to only remove the last record - the length is always 1 greater than the last index
1185N/A index = (index > -1) ? index: (this._items.length - 1);
1185N/A range = (range > 0) ? range: 1;
1185N/A
1185N/A remRecords = this._items.slice(index, (index + range));
168N/A this.fire('remove', {
583N/A removed: remRecords,
168N/A range: range,
89N/A index: index
1229N/A });
1229N/A //this._recordRemoved(remRecords, index);
1229N/A //return ({data: remRecords, index:index});
1229N/A return this;
1229N/A },
1229N/A
1229N/A /**
1185N/A * @description Empties the recordset
1185N/A *
1185N/A * @method empty
259N/A * @return {Y.Recordset} The updated recordset instance
1185N/A * @public
1185N/A */
1100N/A empty: function() {
583N/A this.fire('empty', {});
1185N/A return this;
1185N/A },
1185N/A
583N/A /**
259N/A * @description Updates the recordset with the new records passed in. Overwrites existing records when updating the index with the new records.
1185N/A *
89N/A * @method update
0N/A * @param data {Y.Record, Object Literal, Array} A Y.Record instance, An object literal of data or an array of object literals
154N/A * @param index {Number} (optional) The index to start updating from.
0N/A * @return {Y.Recordset} The updated recordset instance
1185N/A * @public
1190N/A */
1190N/A update: function(data, index) {
1185N/A var rec,
1185N/A arr,
1185N/A i = 0;
1185N/A
0N/A //Whatever is passed in, we are changing it to an array so that it can be easily iterated in the _defUpdateFn method
1185N/A arr = (!(Lang.isArray(data))) ? [data] : data;
153N/A rec = this._items.slice(index, index + arr.length);
0N/A
154N/A for (; i < arr.length; i++) {
1185N/A arr[i] = this._changeToRecord(arr[i]);
1190N/A }
1185N/A
1185N/A this.fire('update', {
1185N/A updated: arr,
1185N/A overwritten: rec,
0N/A index: index
1185N/A });
0N/A
0N/A return this;
154N/A }
849N/A
849N/A
1190N/A},
1185N/A{
1185N/A ATTRS: {
849N/A
1185N/A /**
849N/A * @description An array of records that the recordset is storing
0N/A *
1185N/A * @attribute records
1185N/A * @public
159N/A * @type array
830N/A */
1219N/A records: {
0N/A validator: Lang.isArray,
1185N/A getter: function() {
0N/A // give them a copy, not the internal object
154N/A return new Y.Array(this._items);
1185N/A },
1185N/A setter: function(allData) {
1185N/A //For allData passed in here, see if each instance is a Record.
1190N/A //If its not, change it to a record.
1185N/A //Then push it into the array.
1185N/A var records = [];
1185N/A function initRecord(oneData) {
1185N/A var o;
1185N/A
1185N/A if (oneData instanceof Y.Record) {
1185N/A records.push(oneData);
1205N/A }
1190N/A else {
1185N/A o = new Y.Record({
1185N/A data: oneData
1185N/A });
1185N/A records.push(o);
1185N/A }
1190N/A }
1185N/A
1185N/A //This conditional statement handles creating empty recordsets
1185N/A if (allData) {
1185N/A Y.Array.each(allData, initRecord);
1185N/A this._items = Y.Array(records);
1185N/A }
158N/A },
1340N/A
1340N/A //value: [],
1340N/A //initialization of the attribute must be done before the first call to get('records') is made.
1340N/A //if lazyAdd were set to true, then instantiating using new Y.Recordset({records:[..]}) would
1340N/A //not call the setter.
1340N/A //see http://developer.yahoo.com/yui/3/api/Attribute.html#method_addAttr for details on this
1340N/A lazyAdd: false
1340N/A },
1340N/A
1340N/A /**
1340N/A * @description A hash table where the ID of the record is the key, and the record instance is the value.
1340N/A *
1340N/A * @attribute table
1340N/A * @public
1340N/A * @type object
1340N/A */
1340N/A table: {
1340N/A //Initially, create the hash table with all records currently in the recordset
1340N/A valueFn: '_setHashTable'
1340N/A },
1340N/A
1340N/A /**
1340N/A * @description The ID to use as the key in the hash table.
1340N/A *
1340N/A * @attribute key
1340N/A * @public
1340N/A * @type string
1340N/A */
1340N/A key: {
1340N/A value: 'id',
1340N/A //set to readonly true. If you want custom hash tables, you should use the recordset-indexer plugin.
1340N/A readOnly: true
1340N/A }
158N/A
1340N/A }
158N/A});
158N/AY.augment(Recordset, ArrayList);
1185N/AY.Recordset = Recordset;
1185N/A
1190N/A
1185N/A
1190N/A}, '@VERSION@' ,{requires:['base','arraylist']});
1185N/AYUI.add('recordset-sort', function(Y) {
1185N/A
0N/A/**
1185N/A * Adds default and custom sorting functionality to the Recordset utility
58N/A * @module recordset
58N/A * @submodule recordset-sort
0N/A */
0N/A
1185N/Avar Compare = Y.ArraySort.compare,
0N/AisValue = Y.Lang.isValue;
154N/A
0N/A/**
0N/A * Plugin that adds default and custom sorting functionality to the Recordset utility
0N/A * @class RecordsetSort
0N/A */
0N/A
0N/Afunction RecordsetSort(field, desc, sorter) {
0N/A RecordsetSort.superclass.constructor.apply(this, arguments);
1025N/A}
1185N/A
1185N/AY.mix(RecordsetSort, {
1185N/A NS: "sort",
1185N/A
1190N/A NAME: "recordsetSort",
1025N/A
1185N/A ATTRS: {
1185N/A
1185N/A /**
1185N/A * @description The last properties used to sort. Consists of an object literal with the keys "field", "desc", and "sorter"
1190N/A *
1185N/A * @attribute lastSortProperties
1185N/A * @public
1185N/A * @type object
1190N/A */
1025N/A lastSortProperties: {
1185N/A value: {
1185N/A field: undefined,
1185N/A desc: true,
1185N/A sorter: undefined
1248N/A },
1185N/A validator: function(v) {
1025N/A return (isValue(v.field) && isValue(v.desc) && isValue(v.sorter));
1185N/A }
1185N/A },
1185N/A
1185N/A /**
1185N/A * @description Default sort function to use if none is specified by the user.
1185N/A * Takes two records, the key to sort by, and whether sorting direction is descending or not (boolean).
1185N/A * If two records have the same value for a given key, the ID is used as the tie-breaker.
1185N/A *
1185N/A * @attribute defaultSorter
1185N/A * @public
1185N/A * @type function
1185N/A */
1025N/A defaultSorter: {
1185N/A value: function(recA, recB, field, desc) {
1185N/A var sorted = Compare(recA.getValue(field), recB.getValue(field), desc);
1185N/A if (sorted === 0) {
1185N/A return Compare(recA.get("id"), recB.get("id"), desc);
1185N/A }
1185N/A else {
1185N/A return sorted;
1185N/A }
1185N/A }
1185N/A },
1185N/A
1185N/A /**
1185N/A * @description A boolean telling if the recordset is in a sorted state.
1185N/A *
1185N/A * @attribute defaultSorter
1185N/A * @public
1185N/A * @type function
1185N/A */
1185N/A isSorted: {
1185N/A value: false
1185N/A }
1185N/A }
1185N/A});
1185N/A
1025N/AY.extend(RecordsetSort, Y.Plugin.Base, {
1124N/A
1124N/A /**
1124N/A * @description Sets up the default function to use when the "sort" event is fired.
1190N/A *
1185N/A * @method initializer
1185N/A * @protected
1185N/A */
1185N/A initializer: function(config) {
1185N/A
1185N/A var self = this,
1124N/A host = this.get('host');
1185N/A
1185N/A
1185N/A this.publish("sort", {
1185N/A defaultFn: Y.bind("_defSortFn", this)
1124N/A });
1124N/A
1124N/A //Toggle the isSorted ATTR based on events.
1124N/A //Remove events dont affect isSorted, as they are just popped/sliced out
1124N/A this.on("sort",
1124N/A function() {
1124N/A self.set('isSorted', true);
1124N/A });
1124N/A
1262N/A this.onHostEvent('add',
1124N/A function() {
1124N/A self.set('isSorted', false);
1124N/A },
1124N/A host);
1124N/A this.onHostEvent('update',
1124N/A function() {
1124N/A self.set('isSorted', false);
1124N/A },
1185N/A host);
1185N/A
1185N/A },
1124N/A
1124N/A destructor: function(config) {
1124N/A },
1124N/A
1185N/A /**
1190N/A * @description Method that all sort calls go through.
1185N/A * Sets up the lastSortProperties object with the details of the sort, and passes in parameters
1185N/A * to the "defaultSorter" or a custom specified sort function.
1185N/A *
1185N/A * @method _defSortFn
1185N/A * @private
1205N/A */
1185N/A _defSortFn: function(e) {
1185N/A this.set('lastSortProperties', e);
1185N/A
1185N/A //have to work directly with _items here - changing the recordset.
1185N/A this.get("host")._items.sort(function(a, b) {
1185N/A return (e.sorter)(a, b, e.field, e.desc);
1185N/A });
1185N/A },
1185N/A
1190N/A /**
1185N/A * @description Sorts the recordset.
1190N/A *
1185N/A * @method sort
1190N/A * @param field {string} A key to sort by.
1185N/A * @param desc {boolean} True if you want sort order to be descending, false if you want sort order to be ascending
1185N/A * @public
1185N/A */
1185N/A sort: function(field, desc, sorter) {
1190N/A this.fire("sort", {
1185N/A field: field,
1185N/A desc: desc,
1185N/A sorter: sorter || this.get("defaultSorter")
1190N/A });
1190N/A },
1185N/A
1185N/A /**
1185N/A * @description Resorts the recordset based on the last-used sort parameters (stored in 'lastSortProperties' ATTR)
1185N/A *
1185N/A * @method resort
1190N/A * @public
1185N/A */
1190N/A resort: function() {
1185N/A var p = this.get('lastSortProperties');
1185N/A this.fire("sort", {
1185N/A field: p.field,
1190N/A desc: p.desc,
1185N/A sorter: p.sorter || this.get("defaultSorter")
1185N/A });
1185N/A },
1185N/A
1185N/A /**
1185N/A * @description Reverses the recordset calling the standard array.reverse() method.
1185N/A *
1185N/A * @method reverse
1185N/A * @public
1185N/A */
1185N/A reverse: function() {
1185N/A this.get('host')._items.reverse();
1185N/A },
1185N/A
1185N/A /**
1185N/A * @description Sorts the recordset based on the last-used sort parameters, but flips the order. (ie: Descending becomes ascending, and vice versa).
1185N/A *
1185N/A * @method flip
1185N/A * @public
1185N/A */
1185N/A flip: function() {
1185N/A var p = this.get('lastSortProperties');
1185N/A
1185N/A //If a predefined field is not provided by which to sort by, throw an error
1195N/A if (isValue(p.field)) {
1195N/A this.fire("sort", {
1195N/A field: p.field,
1185N/A desc: !p.desc,
1185N/A sorter: p.sorter || this.get("defaultSorter")
1185N/A });
1185N/A }
1185N/A else {
1124N/A }
1190N/A }
1185N/A});
1185N/A
1185N/AY.namespace("Plugin").RecordsetSort = RecordsetSort;
1185N/A
1185N/A
1185N/A
1124N/A}, '@VERSION@' ,{requires:['recordset-base','arraysort','plugin']});
1124N/AYUI.add('recordset-filter', function(Y) {
1185N/A
1185N/A/**
1124N/A * Plugin that provides the ability to filter through a recordset.
1185N/A * Uses the filter methods available on Y.Array (see arrayextras submodule) to filter the recordset.
1124N/A * @module recordset
1124N/A * @submodule recordset-filter
1124N/A */
1185N/A
1185N/A
1185N/Avar YArray = Y.Array,
1124N/ALang = Y.Lang;
1124N/A
1124N/A
1124N/A/**
1124N/A * Plugin that provides the ability to filter through a recordset.
1124N/A * Uses the filter methods available on Y.Array (see arrayextras submodule) to filter the recordset.
1124N/A * @class RecordsetFilter
1190N/A */
1185N/Afunction RecordsetFilter(config) {
1185N/A RecordsetFilter.superclass.constructor.apply(this, arguments);
1185N/A}
1185N/A
1185N/AY.mix(RecordsetFilter, {
1185N/A NS: "filter",
1124N/A
1185N/A NAME: "recordsetFilter",
1185N/A
1185N/A ATTRS: {
1124N/A }
1185N/A
1124N/A});
1124N/A
1185N/A
1185N/AY.extend(RecordsetFilter, Y.Plugin.Base, {
1185N/A
1124N/A
1124N/A initializer: function(config) {
1124N/A },
1124N/A
1145N/A destructor: function(config) {
1145N/A },
1145N/A
1190N/A /**
1185N/A * @description Filter through the recordset with a custom filter function, or a key-value pair.
1185N/A *
1145N/A * @method filter
1145N/A * @param f {Function, String} A custom filter function or a string representing the key to filter by.
1145N/A * @param v {any} (optional) If a string is passed into f, this represents the value that key should take in order to be accepted by the filter. Do not pass in anything if 'f' is a custom function
1145N/A * @return recordset {Y.Recordset} A new filtered recordset instance
1145N/A * @public
1185N/A */
1145N/A filter: function(f, v) {
1145N/A var recs = this.get('host').get('records'),
1145N/A oRecs = [],
1145N/A func = f;
1145N/A
1145N/A //If a key-value pair is passed in, generate a custom function
1145N/A if (Lang.isString(f) && Lang.isValue(v)) {
1145N/A
1145N/A func = function(item) {
1145N/A if (item.getValue(f) === v) {
1145N/A return true;
1145N/A }
1145N/A else {
1145N/A return false;
1145N/A }
1145N/A };
1145N/A }
1145N/A
1145N/A oRecs = YArray.filter(recs, func);
1145N/A
1145N/A
0N/A //TODO: PARENT CHILD RELATIONSHIP
return new Y.Recordset({
records: oRecs
});
//return new host.constructor({records:arr});
},
/**
* @description The inverse of filter. Executes the supplied function on each item. Returns a new Recordset containing the items that the supplied function returned *false* for.
* @method reject
* @param {Function} f is the function to execute on each item.
* @return {Y.Recordset} A new Recordset instance containing the items on which the supplied function returned false.
*/
reject: function(f) {
return new Y.Recordset({
records: YArray.reject(this.get('host').get('records'), f)
});
},
/**
* @description Iterates over the Recordset, returning a new Recordset of all the elements that match the supplied regular expression
* @method grep
* @param {pattern} pattern The regular expression to test against
* each record.
* @return {Y.Recordset} A Recordset instance containing all the items in the collection that produce a match against the supplied regular expression. If no items match, an empty Recordset instance is returned.
*/
grep: function(pattern) {
return new Y.Recordset({
records: YArray.grep(this.get('host').get('records'), pattern)
});
}
//TODO: Add more pass-through methods to arrayextras
});
Y.namespace("Plugin").RecordsetFilter = RecordsetFilter;
}, '@VERSION@' ,{requires:['recordset-base','plugin','array-extras']});
YUI.add('recordset-indexer', function(Y) {
/**
* Provides the ability to store multiple custom hash tables referencing records in the recordset.
* @module recordset
* @submodule recordset-indexer
*/
/**
* Plugin that provides the ability to store multiple custom hash tables referencing records in the recordset.
* This utility does not support any collision handling. New hash table entries with a used key overwrite older ones.
* @class RecordsetIndexer
*/
function RecordsetIndexer(config) {
RecordsetIndexer.superclass.constructor.apply(this, arguments);
}
Y.mix(RecordsetIndexer, {
NS: "indexer",
NAME: "recordsetIndexer",
ATTRS: {
/**
* @description Collection of all the hashTables created by the plugin.
* The individual tables can be accessed by the key they are hashing against.
*
* @attribute hashTables
* @public
* @type object
*/
hashTables: {
value: {
}
},
keys: {
value: {
}
}
}
});
Y.extend(RecordsetIndexer, Y.Plugin.Base, {
initializer: function(config) {
var host = this.get('host');
//setup listeners on recordset events
this.onHostEvent('add', Y.bind("_defAddHash", this), host);
this.onHostEvent('remove', Y.bind('_defRemoveHash', this), host);
this.onHostEvent('update', Y.bind('_defUpdateHash', this), host);
},
destructor: function(config) {
},
/**
* @description Setup the hash table for a given key with all existing records in the recordset
*
* @method _setHashTable
* @param key {string} A key to hash by.
* @return obj {object} The created hash table
* @private
*/
_setHashTable: function(key) {
var host = this.get('host'),
obj = {},
i = 0,
len = host.getLength();
for (; i < len; i++) {
obj[host._items[i].getValue(key)] = host._items[i];
}
return obj;
},
//---------------------------------------------
// Syncing Methods
//---------------------------------------------
/**
* @description Updates all hash tables when a record is added to the recordset
*
* @method _defAddHash
* @private
*/
_defAddHash: function(e) {
var tbl = this.get('hashTables');
//Go through every hashtable that is stored.
//in each hashtable, look to see if the key is represented in the object being added.
Y.each(tbl,
function(v, key) {
Y.each(e.added || e.updated,
function(o) {
//if the object being added has a key which is being stored by hashtable v, add it into the table.
if (o.getValue(key)) {
v[o.getValue(key)] = o;
}
});
});
},
/**
* @description Updates all hash tables when a record is removed from the recordset
*
* @method _defRemoveHash
* @private
*/
_defRemoveHash: function(e) {
var tbl = this.get('hashTables'),
reckey;
//Go through every hashtable that is stored.
//in each hashtable, look to see if the key is represented in the object being deleted.
Y.each(tbl,
function(v, key) {
Y.each(e.removed || e.overwritten,
function(o) {
reckey = o.getValue(key);
//if the hashtable has a key storing a record, and the key and the record both match the record being deleted, delete that row from the hashtable
if (reckey && v[reckey] === o) {
delete v[reckey];
}
});
});
},
/**
* @description Updates all hash tables when the recordset is updated (a combination of add and remove)
*
* @method _defUpdateHash
* @private
*/
_defUpdateHash: function(e) {
//TODO: It will be more performant to create a new method rather than using _defAddHash, _defRemoveHash, due to the number of loops. See commented code.
e.added = e.updated;
e.removed = e.overwritten;
this._defAddHash(e);
this._defRemoveHash(e);
/*
var tbl = this.get('hashTables'), reckey;
Y.each(tbl, function(v, key) {
Y.each(e.updated, function(o, i) {
//delete record from hashtable if it has been overwritten
reckey = o.getValue(key);
if (reckey) {
v[reckey] = o;
}
//the undefined case is if more records are updated than currently exist in the recordset.
if (e.overwritten[i] && (v[e.overwritten[i].getValue(key)] === e.overwritten[i])) {
delete v[e.overwritten[i].getValue(key)];
}
// if (v[reckey] === o) {
// delete v[reckey];
// }
//
// //add the new updated record if it has a key that corresponds to a hash table
// if (o.getValue(key)) {
// v[o.getValue(key)] = o;
// }
});
});
*/
},
//---------------------------------------------
// Public Methods
//---------------------------------------------
/**
* @description Creates a new hash table.
*
* @method createTable
* @param key {string} A key to hash by.
* @return tbls[key] {object} The created hash table
* @public
*/
createTable: function(key) {
var tbls = this.get('hashTables');
tbls[key] = this._setHashTable(key);
this.set('hashTables', tbls);
return tbls[key];
},
/**
* @description Get a hash table that hashes records by a given key.
*
* @method getTable
* @param key {string} A key to hash by.
* @return table {object} The created hash table
* @public
*/
getTable: function(key) {
return this.get('hashTables')[key];
}
});
Y.namespace("Plugin").RecordsetIndexer = RecordsetIndexer;
}, '@VERSION@' ,{requires:['recordset-base','plugin']});
YUI.add('recordset', function(Y){}, '@VERSION@' ,{use:['recordset-base','recordset-sort','recordset-filter','recordset-indexer']});