All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.smartclient.debug.public.sc.client.language.Array.js Maven / Gradle / Ivy

The newest version!
/*
 * Isomorphic SmartClient
 * Version SC_SNAPSHOT-2011-08-08 (2011-08-08)
 * Copyright(c) 1998 and beyond Isomorphic Software, Inc. All rights reserved.
 * "SmartClient" is a trademark of Isomorphic Software, Inc.
 *
 * [email protected]
 *
 * http://smartclient.com/license
 */




//>	@object	Array
// Generic extensions to JavaScript Arrays.  You can call these on any Array.
// 

// JavaScript's native Array is retrofitted to support the List API. // // @implements List // @see List // @treeLocation Client Reference/System // @visibility external //< // Internal notes: Array vs the List interface // - List is an interface which the native JavaScript Array object is retrofitted to implement // - When a given method can be implemented solely in terms of other parts of the List interface, // there is the possibility that Array and the List interface can share the actual JavaScript // function object. When this is done, the method is first defined on Array (for load order // reasons). // - on Array, several methods can be faster if they use various native functions (like splice()), // and so a unique implementation appears here // - on List, in order to allow a valid List implementation with a minimum of effort, all methods // boil down to very simple primitives like addAt // - public documentation for the List interface is in List.js //> @groupDef dataChanged // Operations that change the Array // @title Data Changes //< //> @groupDef iteration // Operations on entire Arrays at once // @title Iteration //< //> @groupDef arrayMath // Math operations on entire Arrays at once // @title Array Math //< // add a "Class" property to the array prototype // so we can recognize Array instances Array.prototype.Class = "Array"; //> @classMethod Array.newInstance() // Create a new array, adding any arguments passed in as properties. // Here so we can make standard newInstance() calls on arrays. // // @param [all arguments] (object) objects with properties to override from default // @return (array) new array. //< Array.newInstance = function () { var instance = []; isc.addPropertyList(instance, arguments); return instance; } Array.create = Array.newInstance; //> @classAttr Array.LOADING (String : "loading" : IRA) // Marker value returned by Lists that manage remote datasets, indicating the requested data is // being loaded. Note that the recommended test for loading data is to call +link{Array.isLoading()} // rather than compare to this value directly. // @visibility external //< Array.LOADING = "loading"; //> @classMethod Array.isLoading() (A) // Is the object passed in a loading marker value? For use with Lists that manage remote // datasets, to indicate that a record has not yet been retrieved from the server. A typical // use case might be to check if a row has been loaded in a ListGrid - for example: //

// // if (Array.isLoading(myList.getRecord(0))) isc.warn("Please wait for the data to load."); // // @param value (any) data to test. // @visibility external //< Array.isLoading = function (row) { return row != null && !isc.isAn.XMLNode(row) && (row === Array.LOADING); } //> @classAttr Array.CASE_INSENSITIVE (Function : See below : ) // This is a built-in comparator for the +link{array.find,find} and +link{array.findIndex,findIndex} // methods of Array. Passing this comparator to those methods will find case-insensitively, // so, eg, find("foo", "bar") would find objects with a "foo" property set to // "Bar", "BAR" or "bar" // @visibility external //< Array.CASE_INSENSITIVE = function(arrayMemberProperty, comparisonProperty, propertyName) { if (isc.isA.String(arrayMemberProperty) && isc.isA.String(comparisonProperty) && arrayMemberProperty.toLowerCase() == comparisonProperty.toLowerCase()) { return true; } else { return arrayMemberProperty == comparisonProperty; } } //> @classAttr Array.DATE_VALUES (Function : See below : ) // This is a built-in comparator for the +link{array.find,find} and +link{array.findIndex,findIndex} // methods of Array. Passing this comparator to those methods will find instances where Dates // in the search criteria match Dates in the array members (ordinarily, Javascript only regards // Dates as equal if they refer to the exact same object). This comparator compares logical // dates; the time elements of the values being compared are ignored, so two Dates representing // different times on the same day will be considered equal. // @see Array.DATETIME_VALUES // @visibility external //< Array.DATE_VALUES = function(arrayMemberProperty, comparisonProperty, propertyName) { if (isc.isA.Date(arrayMemberProperty) && isc.isA.Date(comparisonProperty) && Date.compareLogicalDates(arrayMemberProperty, comparisonProperty) == 0) { return true; } else { return arrayMemberProperty == comparisonProperty; } } //> @classAttr Array.DATETIME_VALUES (Function : See below : ) // This is a built-in comparator for the +link{array.find,find} and +link{array.findIndex,findIndex} // methods of Array. Passing this comparator to those methods will find instances where Dates // in the search criteria match Dates in the array members (ordinarily, Javascript only regards // Dates as equal if they refer to the exact same object). This comparator compares entire // date values, including the time elements of the values being compared, so two Dates // representing different times on the same day (even if they are only a millisecond apart) will // be considered equal. // @see Array.DATE_VALUES // @visibility external //< Array.DATETIME_VALUES = function(arrayMemberProperty, comparisonProperty, propertyName) { if (isc.isA.Date(arrayMemberProperty) && isc.isA.Date(comparisonProperty) && Date.compareDates(arrayMemberProperty, comparisonProperty) == 0) { return true; } else { return arrayMemberProperty == comparisonProperty; } } if (!Array.prototype.localeStringFormatter) Array.prototype.localeStringFormatter = "toString"; // add a bunch of methods to the Array prototype so all arrays can use them isc.addMethods(Array.prototype, { iscToLocaleString : function () { return this[this.localeStringFormatter](); }, //> @method array.getPrototype() // Return the Array.prototype -- for conformity with the Class.getPrototype() method // Used in exporting arrays. //< getPrototype : function () { return Array.prototype; }, //> @method array.newInstance() // Create a new array, adding any arguments passed in as properties. // Here so we can make standard newInstance() calls on arrays. // // @param [all arguments] (object) objects with properties to override from default // @return (array) new array. //< newInstance : Array.newInstance, create : Array.newInstance, // List Interface // -------------------------------------------------------------------------------------------- //> @method array.get() // @include list.get() //< get : function (pos) { return this[pos] }, //> @method array.getLength() // @include list.getLength() //< getLength : function () { return this.length }, //> @method array.isEmpty() // @include list.isEmpty() //< // NOTE: implementation stolen by List interface. Must use only List API for internal access. isEmpty : function () { return this.getLength() == 0; }, //> @method array.first() // @include list.first() //< first : function () { return this[0] }, //> @method array.last() // @include list.last() //< last : function () { return this[this.length-1] }, //> @method array.indexOf() // @include list.indexOf() //< indexOf : function (obj, pos, endPos) { // normalize position to the start of the list if (pos == null) pos = 0; if (endPos == null) endPos = this.length - 1; for (var i = pos; i <= endPos; i++) if (this[i] == obj) return i; // not found -- return the not found flag return -1; }, //> @method array.lastIndexOf() // @include list.lastIndexOf() //< lastIndexOf : function (obj, pos, endPos) { // normalize position to the end of the list if (pos == null) pos = this.length-1; if (endPos == null) endPos = 0; for (var i = pos; i >= endPos; i--) if (this[i] == obj) return i; // not found -- return the not found flag return -1; }, //> @method array.contains() // @include list.contains() //< // NOTE: implementation stolen by List interface. Must use only List API for internal access. contains : function (obj, pos) { return (this.indexOf(obj, pos) != -1); }, //> @method array.containsAll() // @include list.containsAll() //< // NOTE: implementation stolen by List interface. Must use only List API for internal access. containsAll : function (list) { if (list == null) return true; var length = list.getLength(); for (var i = 0; i < length; i++) { if (!this.contains(list.get(i))) return false; } return true; }, //> @method array.intersect() // @include list.intersect() //< // NOTE: implementation stolen by List interface. Must use only List API for internal access. intersect : function () { var results = []; // for each element in this array for (var i = 0; i < this.length; i++) { // if the element is in each argument, add it to the results var item = this.get(i), isPresent = true; // skip null elements if (item == null) continue; // for each array passed in for (var a = 0; a < arguments.length; a++) { // if the item is not in that array if (!arguments[a].contains(item)) { // it hasn't been found isPresent = false; break; } } if (isPresent) results.add(item); } // return true return results; }, //> @method array.equals() // @include list.equals() //< // NOTE: implementation stolen by List interface. Must use only List API for internal access. equals : function (list) { if (list == null || !isc.isA.List(list)) return false; var length = list.getLength(); // arrays of differing lengths cannot be equals if (length != this.getLength()) return false; for (var i = 0; i < length; i++) { if (list.get(i) != this.get(i)) return false; } return true; }, //> @method array.getItems() // @include list.getItems() //< // NOTE: implementation stolen by List interface. Must use only List API for internal access. getItems : function (itemList) { var outputs = [], length = itemList.getLength(); for (var i = 0; i < length; i++) { outputs[i] = this.get(itemList.get(i)); } return outputs; }, //> @method array.getRange() // @include list.getRange() //< getRange : function (start, end) { if (end == null) end = this.length - 1; return this.slice(start, end); }, //> @method array.duplicate() (A) // @include list.duplicate() //< duplicate : function () { return isc._emptyArray.concat(this); // NOTE: concat creates a copy }, // getData() from list - no analogous method //> @method array.set() // @include list.set() //< set : function (pos, item) { this[pos] = item; this.dataChanged(); }, //> @method array.addAt() // @include list.addAt() //< addAt : function (obj, pos) { if (pos == null) pos = 0; // copy items in the original array to their new position; copy backwards from last item to // item at pos, so that none of the items are overwritten for (var i = this.length - 1; i >= pos; i--) { this[i+1] = this[i]; } // add the new object to the list this[pos] = obj; // call dataChanged in case anyone is observing it this.dataChanged(); // return the object that was added return obj; }, //> @method array.removeAt() // @include list.removeAt() //< removeAt : function (pos) { // make sure the pos passed in is valid var length = this.length; if (pos >= length || pos < 0) return null; // get the item at that position var it = this[pos]; // remove the item at that position by sliding other things over it for(;pos < length-1;pos++) this[pos] = this[pos+1]; // now update the length this.length--; // call dataChanged in case anyone is observing it this.dataChanged(); return it; }, //> @method array.add() // @include list.add() //< add : function (object, secondArg) { var undefined; if (secondArg !== undefined) { // support calling as add(index, object) return this.addAt(object, secondArg); } var pos; // if the list.sortUnique is true, we're only supposed to have each item once if (this.sortUnique) { // find the current position of the item in the list pos = this.indexOf(object); // if it wasn't found, put it at the end if (pos == -1) pos = this.length; } else { // otherwise we always put the item at the end pos = this.length; } // actually stick the object in the list this[pos] = object; // if we are currently sorted, maintain current sort if (this.sortProps && this.sortProps.length > 0) { this.sortByProperties(this.sortProps, this.sortDirections, this.sortNormalizers); } // call dataChanged in case anyone is observing it this.dataChanged(); // return the object that was added return object; }, //> @method array.addList() // @include list.addList() //< // NOTE: implementation stolen by List interface. Must use only List API for internal access. addList : function (list, listStartRow, listEndRow) { if (list == null) return null; this._startChangingData(); if (listStartRow == null) listStartRow = 0; if (listEndRow == null) listEndRow = list.getLength(); for (var pos = listStartRow; pos < listEndRow; pos++) { this.add(list.get(pos)); } this._doneChangingData(); // return the objects that were added return list; }, //> @method array.setLength() // @include list.setLength() //< setLength : function (length) { this.length = length; }, //> @method array.addListAt() // @include list.addListAt() //< addListAt : function (list, pos) { if (list == null) return null; // copy items in the original array to their new position; copy backwards from last item to // item at pos, so that none of the items are overwritten for (var i = this.length - 1, l = list.length; i >= pos; i--) { this[i+l] = this[i]; } // add the new items in list for (i = 0; i < l; i++) { this[i+pos] = list[i]; } // call dataChanged in case anyone is observing it this.dataChanged(); // return the list that was added return list; }, //> @method array.remove() // @include list.remove() //< remove : function (obj) { var index = this.indexOf(obj); if (index == -1) return false; for (var i = index; i < this.length; i++) this[i] = this[i+1]; this.length = this.length-1; this.dataChanged(); return true; // indicating object was removed, per java.util.Collection }, //> @method array.removeList() // @include list.removeList() //< removeList : function (list) { if (list == null) return null; // run through all the items, putting things we want to retain into new list output for (var output = [], i = 0, l = this.length;i < l;i++) { if (!list.contains(this[i])) output.add(this[i]); } // now set the items in this list to the items in output this.setArray(output); // return the list that was removed return list; }, // useful in chaining expressions eg someList.removeEvery(null).getProperty(...) // .. removeList/removeAll don't work in this circumstance removeEvery : function (value) { this.removeList([value]); return this; }, // methods to ensure dataChanged() fired only once when a series of changes are made: see List.js _startChangingData : function () { var undef; if (this._dataChangeFlag === undef) this._dataChangeFlag = 0; this._dataChangeFlag++; }, _doneChangingData : function () { if (--this._dataChangeFlag == 0) this.dataChanged(); }, //> @method array.dataChanged() (A) // @include list.dataChanged() //< dataChanged : function () { if (this.onDataChanged) this.onDataChanged() }, // In some cases we want to perform a one-liner - call dataChanged unless we're inside a data // changing loop _isChangingData : function () { return (this._dataChangeFlag != null && this._dataChangeFlag > 0); }, // End of List API // -------------------------------------------------------------------------------------------- //> @method array.setArray() // Completely change the contents of one array to the contents of another array. //

// This is useful if you have an external pointer to an array, but you want to change its // contents, such as when you remove some items from the array. // // @group dataChanged // // @param (array) array to set this array to //< setArray : function (list) { // match length this.setLength(list.length); // fill slots for (var i = 0; i < list.length; i++) this[i] = list[i]; // call dataChanged in case someone is observing data in the list this.dataChanged(); }, //> @method array.addAsList() // Add either a single object or a list of items to this array. // // @group dataChanged // // @param list (array or object) a single object or a list of items to add // // @return (list) list of items that were added //< addAsList : function (list) { if (!isc.isAn.Array(list)) list = [list]; // return the objects that were added return this.addList(list); }, //> @method array.removeRange() // Remove and return a range of elements from an array - same return value as array.slice(), // but removes the slice from the array // // @group dataChanged // // @param startPos (number) start position of range to remove // @param endPos (number) end position of range to remove // // @return (array) array of items that were removed //< removeRange : function (startPos, endPos) { // fall through to splice var undefined; if (startPos === undefined) return this; // no arguments if (!isc.isA.Number(startPos)) startPos = 0; if (!isc.isA.Number(endPos)) endPos = this.length; return this.splice(startPos, endPos - startPos); }, //> @method array.removeWhere() // Remove all instances of object from this array // @group dataChanged // // @param property (string) property to look for // @param value (string) value to look for // // @return (array) list of items that were removed //< removeWhere : function (property, value) { for (var i = 0, newList = []; i < this.length; i++) { if (!this[i] || this[i][property] != value) { newList.add(this[i]); } } this.setArray(newList); }, // Corollary to removeWhere - remove every item where some property is not set to some // specified value. removeUnless : function (property, value) { for (var i = 0, newList = []; i < this.length; i++) { if (this[i] && this[i][property] == value) { newList.add(this[i]); } } this.setArray(newList); }, //> @method array.removeEmpty() // Remove all empty slots in this array (where array[n] == null) // @group dataChanged //< removeEmpty : function (property, value) { for (var i = 0, newList = []; i < this.length; i++) { if (this[i] != null) { newList.add(this[i]); } } this.setArray(newList); }, //> @method array.getProperty() // @include list.getProperty // @visibility external //< getProperty : function (property) { for(var output = [], i = 0, l = this.length;i < l;i++) output[output.length] = (this[i] ? this[i][property] : null); return output; }, //>@method array.getValueMap() // @include list.getValueMap() // @visibility external //< getValueMap : function (idField, displayField) { var valueMap = {}; for (var i = 0, l = this.getLength(); i < l; i++) { var item = this.get(i); // Don't attempt to pull properties from empty values / basic data types in the list. if (!isc.isAn.Object(item)) continue; if (item && item[idField] != null) { valueMap[item[idField]] = item[displayField]; } } return valueMap; }, //> @method array.map() // Return an array where the value of item i is the result of calling the provided // function on item i in this array. //

// The function to call can either be provided directly as a function object, in which case it // is invoked with the item as the first argument, or can be provided as the String name of a // method present on each item, which will be invoked. In the latter case, if any item is null // or lacks the named method, null will be returned for that item. //

// Examples:

//    // line up widgets at 20 pixels from page edge
//    [widget1, widget2].map("setPageLeft", 20);
//
//    // find furthest right widget
//    [widget1, widget2].map("getPageRight").max();
// 
// // @group iteration // // @param method (string or function) function object, or name of method // @param [(arguments 1-N)] (any) arguments to pass to the function or method // invoked on each item // @return (array) array of returned values // @visibility external //< map : function (method, arg1, arg2, arg3, arg4, arg5) { var isFunc = isc.isA.Function(method), output = [], length = this.getLength(); var undef, mimicNativeImp = isFunc && (arg1 === undef || isc.isAn.Object(arg1)) && arg2 === undef && arg3=== undef && arg4 === undef && arg5 === undef; for (var i = 0; i < length; i++) { var item = this.get(i); if (mimicNativeImp) { if (arg1 == null) output[i] = method(item, i, this); else { arg1._tempSlot = method; output[i] = arg1._tempSlot(item, i, this); delete arg1._tempSlot; } } else if (isFunc) { output[i] = method(item, arg1, arg2, arg3, arg4, arg5); } else { output[i] = (item && item[method] != null ? item[method](arg1, arg2, arg3, arg4, arg5) : null); } } return output; }, //> @method array.setProperty() // Set item[property] = value for each item in this array. // @group iteration // // @param property (string) name of the property to set // @param value (any) value to set to // @visibility external //< setProperty : function (property, value) { for(var i = 0, l = this.length;i < l;i++) if (this[i]) this[i][property] = value; }, //> @method array.clearProperty() // Delete property in each item in this array. // @group iteration // // @param property (string) name of the property to clear // @return (boolean) returns true if any of the properties in the array had a value for the // specified property. // @visibility external //< clearProperty : function (property) { var hadValue = false, undef; for(var i = 0, l = this.length;i < l;i++) { hadValue = hadValue || this[i] !== undef; if (this[i]) delete this[i][property]; } return hadValue; }, //> @method array.getProperties() // Return a copy of the array where each object has only the list of properties provided. // @group iteration // // @param properties (string[]) names of the properties you want to export // (object) object with the properties you want to export // // @return (Array) new Array with each item limited to the specified properties //< getProperties : function (properties) { return isc.applyMask(this, properties); }, //> @method array.getUniqueItems() // Return a list of each unique item in this list exactly once. //

// Returns in the same order they were found in the list. //

// Usage example:
//     uniqueList = myArray.getProperty("foo").getUniqueItems(); // // @group subset // // @return (array) list of each unique item in the list // @visibility external //< getUniqueItems : function () { for (var output = [], i = 0, l = this.length; i < l; i++) { if (!output.contains(this[i])) output[output.length] = this[i]; } return output; }, //> @method array.slice() // Return a contiguous range of rows of the array. // DOES NOT include element at position end (similar to .substring()) //

// NOTE: uses browser's native implementation if one is provided // // @param start (number) start index // @param [end] (number) end index, if not specified will be list.length // // @return (array) new array with items from start -> end-1 in it // @group subset //< slice : (Array.prototype.slice ? Array.prototype.slice : function (start, end) { if (end == null) end = this.length; for(var output = [], l = this.length; start < end && start < l;start++) output[output.length] = this[start]; return output; } ), //> @method array.findIndex() // @include list.findIndex //< findIndex : function (property, value, comparator) { return this.findNextIndex(0, property, value, null, comparator); }, //> @method array.findNextIndex() // @include list.findNextIndex //< findNextIndex : function (start, property, value, endPos, comparator) { if (start == null) start = 0; else if (start >= this.length) return -1; if (endPos == null) endPos = this.length - 1; if (property == null) return -1; if (isc.isA.String(property)) { // single property to match if (comparator) { for (var i = start; i <= endPos; i++) { if (this[i] && comparator(this[i][property], value, property)) return i; } } else { for (var i = start; i <= endPos; i++) { if (this[i] && this[i][property] == value) return i; } } return -1; } else if (isc.isA.Function(property)) { for (var i = start; i <= endPos; i++) { if (property(this[i])) return i; } return -1; } else { // "property" is an object specifying a set of properties to match return this.findNextMatch(property, start, endPos, comparator); } }, // internal: assumes multiple properties findNextMatch : function (properties, start, end, comparator) { var propertyNames = isc.getKeys(properties); // This processing is largely duplicated, to avoid a check on comparator in the inner loop if (comparator) { for (var i = start; i <= end; i++) { var item = this.get(i); if (!item) continue; var found = true; for (var j = 0; j < propertyNames.length; j++) { var propertyName = propertyNames[j]; if (!comparator(item[propertyName], properties[propertyName], propertyName)) { found = false; break; } } if (found) return i; } } else { for (var i = start; i <= end; i++) { var item = this.get(i); if (!item) continue; var found = true; for (var j = 0; j < propertyNames.length; j++) { var propertyName = propertyNames[j]; if (item[propertyName] != properties[propertyName]) { found = false; break; } } if (found) return i; } } return -1; }, //> @method array.find() // @include list.find //< find : function (property, value, comparator) { var index = this.findIndex(property, value, comparator); return (index != -1) ? this.get(index) : null; }, // given values for the primary key fields ("record"), find the _index of_ the unique // matching record. // Will automatically trim extra, non-key fields from "record" findByKeys : function (record, dataSource, pos, endPos) { if (record == null) { //>DEBUG isc.Log.logWarn("findByKeys: passed null record"); //DEBUG isc.Log.logWarn("findByKeys: passed record does not have a value for key field '" + keyField + "'"); //DEBUG isc.Log.logWarn("findByKeys: dataSource '" + dataSource.ID + "' does not have primary " + "keys declared, can't find record"); // @method array.containsProperty() // Determine whether this array contains any members where the property passed in matches the value // passed in. // // @group find // @param property (string) property to look for // (object) key:value pairs to look for // @param [value] (any) value to compare against (if property is a string) // // @return (boolean) true if this array contains an object with the appropriate property value // @visibility external //< containsProperty : function (property, value) { var index = this.findIndex(property, value); return (index != -1); }, //> @method array.findAll() // @include list.findAll //< findAll : function (property, value) { if (property == null) return null; if (isc.isA.String(property)) { var matches = null, l = this.length; // single property to match var multiVal = isc.isAn.Array(value); for (var i = 0; i < l; i++) { var item = this[i]; if (item && (multiVal ? value.contains(item[property]) : item[property] == value)) { if (matches == null) matches = []; matches.add(item); } } return matches; } else if (isc.isA.Function(property)) { var matches = null, l = this.length, iterator = property, context = value; for (var i = 0; i < l; i++) { var item = this[i]; if (iterator(item, context)) { if (matches == null) matches = []; matches.add(item); } } return matches; } else { // "property" is an object specifying a set of properties to match return this.findAllMatches(property); } }, // internal: assumes multiple properties findAllMatches : function (properties) { var l = this.getLength(), propertyNames = isc.getKeys(properties), matches = null ; for (var i = 0; i < l; i++) { var item = this.get(i); if (!item) continue; var found = true; for (var j = 0; j < propertyNames.length; j++) { var propertyName = propertyNames[j]; if (item[propertyName] != properties[propertyName]) { found = false; break; } } if (found) { if (matches == null) matches = []; matches.add(item); } } return matches; }, //> @method array.slide() (A) // Slide element at position start to position destination, moving all the other elements to cover // the gap. // // @param start (number) start position // @param destination (number) destination position for this[start] // @visibility external //< slide : function (start, destination) { this.slideRange(start, start+1, destination); }, //> @method array.slideRange() (A) // Slide a range of elements from start to end to position destination, moving all the other // elements to cover the gap. // // @param start (number) start position // @param end (number) end position (exclusive, like substring() and slice()) // @param destination (number) destination position for the range // @visibility external //< slideRange : function (rangeStart, rangeEnd, destination) { // remove the range to be moved var removed = this.splice(rangeStart, rangeEnd - rangeStart); // and add it at the destination this.addListAt(removed, destination); }, //> @method array.slideList() (A) // Slide the array of rows list to position destination. // // @param start (number) start position // @param destination (number) destination position for this[start] //< slideList : function (list, destination) { var output = [], i ; //XXX if destination is negative, set to 0 (same effect, cleaner code below) if (destination < 0) destination = 0; // take all the things from this table before destination that aren't in the list to be moved for(i = 0;i < destination;i++) if (!list.contains(this[i])) output.add(this[i]); // now put in all the things to be moved for(i = 0;i < list.length;i++) output.add(list[i]); // now put in all the things after destination that aren't in the list to be moved for(i = destination;i < this.length;i++) if (!list.contains(this[i])) output.add(this[i]); // now copy the reordered list back into this array this.setArray(output); }, //> @method array.makeIndex() (A) // Make an index for the items in this Array by a particular property of each item. //

// Returns an Object with keys for each distinct listItem[property] value. Each key will point // to an array of items that share that property value. The sub-array will be in the same order // that they are in this list. // // @param property (strings) names of the property to index by // @param alwaysMakeArray (boolean : false) // if true, we always make an array for every index. if false, we make an Array only // when more than one item has the same value for the index property // @return (object) index object // @visibility external //< // NOTE: we don't document the awkard -1 param to allow collisions makeIndex : function (property, alwaysMakeArray, useIndexAsKey) { var index = {}; var allowCollisions = (alwaysMakeArray == -1); alwaysMakeArray = (alwaysMakeArray != null && alwaysMakeArray != 0); for (var i = 0; i < this.length; i++) { var item = this[i], key = item[property] ; // if the item has no value for the key property if (key == null) { // either skip it.. if (!useIndexAsKey) continue; // or place it in the index under its position in the array key = i; } if (allowCollisions) { index[key] = item; continue; } var existingValue = index[key]; if (existingValue == null) { if (alwaysMakeArray) { // every entry should be an array index[key] = [item]; } else { index[key] = item; } } else { if (alwaysMakeArray) { // any non-null value is an array we created the first time we found an item // with this key value index[key].add(item); } else { // if the existing value is an array, add to it, otherwise put the new and old // value together in a new array if (isc.isAn.Array(existingValue)) { index[key].add(item); } else { index[key] = [existingValue, item]; } } } } return index; }, //> @method array.arraysToObjects() (A) // Map an array of arrays to an array of objects. //

// Each array becomes one object, which will have as many properties as the number of property // names passed as the "propertyList". The values of the properties will be the values found // in the Array, in order. //

// For example: //

//    var arrays = [
//       ["Mickey", "Mouse"],
//       ["Donald", "Duck"],
//       ["Yosemite", "Sam"]
//    ];
//    var objects = arrays.arraysToObjects(["firstName", "lastName"]);
// 
// objects is now: //
//    [
//       { firstName:"Mickey", lastName:"Mouse" },
//       { firstName:"Donald", lastName:"Duck" },
//       { firstName:"Yosemite", lastName:"Sam" }
//    ]
// 
// // @param propertyList (Array of String) names of the properties to assign to // // @return (Array of Object) corresponding array of objects //< arraysToObjects : function (propertyList) { // get the number of properties we're dealing with var propLength = propertyList.length; // for each item in this array for (var output = [], i = 0, l = this.length; i < l; i++) { // make a new object to hold the output var it = output[i] = {}; // for each property in the propertyList list for (var p = 0; p < propLength; p++) { var property = propertyList[p]; // assign that item in the array to the proper name of the new object it[property] = this[i][p]; } } // return the list that was generated return output; }, //> @method array.objectsToArrays() (A) // Map an array of objects into an array of arrays. //

// Each object becomes one array, which contains the values of a list of properties from // the source object. //

// For example: //

//    var objects = [
//       { firstName:"Mickey", lastName:"Mouse" },
//       { firstName:"Donald", lastName:"Duck" },
//       { firstName:"Yosemite", lastName:"Sam" }
//    ]
//    var arrays = objects.objectsToArrays(["firstName", "lastName"]);
// 
// arrays is now: //
// [
//    ["Mickey", "Mouse"],
//    ["Donald", "Duck"],
//    ["Yosemite", "Sam"]
// ]
// 
// // @param propertyList (Array of String) names of the properties to output // // @return (Array of Object) corresponding array of arrays //< objectsToArrays : function (propertyList) { // get the number of properties we're dealing with var propLength = propertyList.length; // for each item in this array for (var output = [], i = 0, l = this.length; i < l; i++) { // make a new object to hold the output var it = output[i] = []; // for each property in the propertyList list for (var p = 0; p < propLength; p++) { var property = propertyList[p]; // assign that item in the array to the proper name of the new object it[p] = this[i][property]; } } // return the list that was generated return output; }, //> @method array.spliceArray() // Like array.splice() but takes an array (to concat) as a third parameter, // rather than a number of additional parameters. // // @param startPos (number) starting position for the splice // @param deleteCount (number) Number of elements to delete from affected array // @param newArray (any[]) Array of elements to splice into existing array // // @return (any[]) array of removed elements //< spliceArray : function (startPos, deleteCount, newArray) { var undefined; if (startPos === undefined) return this.splice(); if (deleteCount === undefined) return this.splice(startPos); if (newArray === undefined) return this.splice(startPos, deleteCount); if (!isc.isAn.Array(newArray)) { isc.Log.logWarn("spliceArray() method passed a non-array third parameter. Ignoring...", "Array"); return this.splice(startPos, deleteCount); } // use 'apply' - allows you to pass in the arguments as an array! // xxx - // Note 1: Another syntax for this would be of this form // if(newArray.length <= 10) return this.splice(startPos, deleteCount, newArray[0], ...) // else return this.splice.apply(...) // but seems no better performance-wise, and since (at least in our overridden implementation of // splice for IE 5.0) we use arguments.length, is unreliable unless we have a plethora of // if ... then/s to pass in exactly the right number of arguments. // // Note 2: you have to use concat, rather than splice to put startPos / deleteCount at the // beginning of newArray, as newArray points to an array object that may be being reused // elsewhere, so we can't modify it. // return this.splice.apply(this, [startPos, deleteCount].concat(newArray)) }, // stack peek method - returns the top item on the stack without removing it. peek : function () { var item = this.pop(); this.push(item); return item; }, // // ---------------------------------------------------------------------------------- // add the observation methods to the Array.prototype as well so we can use 'em there // observe: isc.Class.getPrototype().observe, ignore : isc.Class.getPrototype().ignore, _makeNotifyFunction : isc.Class.getPrototype()._makeNotifyFunction, // Synonyms and backcompat // -------------------------------------------------------------------------------------------- //>!BackCompat 2004.6.15 for old ISC names removeItem : function (pos) { return this.removeAt(pos) }, getItem : function (pos) { return this.get(pos) }, setItem : function (pos) { return this.set(pos) }, // NOTE: instead of calling clearAll(), setLength(0) should be called (which is much more // efficient), however clearAll() still exists to support the old behavior of returning the // removed items. clearAll : function (list) { return this.removeList(this) }, //




© 2015 - 2024 Weber Informatics LLC | Privacy Policy