1 /* See license.txt for terms of usage */
  2 
  3 define([
  4     "firebug/lib/trace",
  5     "firebug/lib/deprecated",
  6 ],
  7 function(FBTrace, Deprecated) {
  8 "use strict";
  9 // ********************************************************************************************* //
 10 // Constants
 11 
 12 const Ci = Components.interfaces;
 13 
 14 /**
 15  * @name Arr
 16  * @lib Utility for Arrays
 17  */
 18 var Arr = {};
 19 
 20 
 21 // Array Generic methods
 22 // use them to call Array methods with Array-Like objects (arguments, String, NodeList...)
 23 // example: var firstArg = Array.forEach(nodeList, func);
 24 //
 25 // 1. xxxFlorent: should be deprecated as soon Array generics are standardized in ES5 or ES6
 26 // 2. xxxFlorent: BTW, can we consider Array generic methods as safe to be used?? What would happen if it is eventually abandoned?
 27 var ArrayGen = {};
 28 (function()
 29 {
 30     var methods = [
 31         'join', 'reverse', 'sort', 'push', 'pop', 'shift', 'unshift',
 32         'splice', 'concat', 'slice', 'indexOf', 'lastIndexOf',
 33         'forEach', 'map', 'reduce', 'reduceRight', 'filter',
 34         'some', 'every'
 35     ];
 36 
 37     methods.forEach(function(methodName)
 38     {
 39         // xxxFlorent: TODO: [REST]
 40         ArrayGen[methodName] = function(thisObj/*, ...args*/)
 41         {
 42             var args = Array.prototype.slice.call(arguments, 1);
 43             return Array.prototype[methodName].apply(thisObj, args);
 44         };
 45     });
 46 })();
 47 
 48 Object.seal(ArrayGen);
 49 Object.freeze(ArrayGen);
 50 
 51 Arr.ArrayGen = ArrayGen;
 52 
 53 // ********************************************************************************************* //
 54 // Arrays
 55 
 56 /**
 57  * @deprecated use Array.isArray instead
 58  */ 
 59 Arr.isArray = Deprecated.deprecated("Use Array.isArray instead", Array.isArray);
 60 /**
 61  * Returns true if the given object is an Array or an Array-Like object
 62  *
 63  * @param {*} obj The object
 64  * @return true if it is an array-like object or false otherwise
 65  */
 66 Arr.isArrayLike = function(obj)
 67 {
 68     try
 69     {
 70         if (typeof obj !== "object")
 71             return false;
 72         if (!isFinite(obj.length))
 73             return false;
 74         if (Array.isArray(obj))
 75             return true;
 76         if (typeof obj.callee === "function") // arguments
 77             return true;
 78         if (typeof obj.splice === "function") // jQuery etc.
 79             return true;
 80         if (obj instanceof Ci.nsIDOMHTMLCollection)
 81             return true;
 82         if (obj instanceof Ci.nsIDOMNodeList)
 83             return true;
 84         if (obj instanceof Ci.nsIDOMDOMTokenList)
 85             return true;
 86     }
 87     catch (exc) {}
 88     return false;
 89 };
 90 
 91 /**
 92  * @deprecated Use Object.keys instead
 93  * see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys
 94  */
 95 Arr.keys = Deprecated.deprecated("Use Object.keys instead", function(map)
 96 {
 97     var keys = [];
 98     try
 99     {
100         for (var name in map)  // enumeration is safe
101             keys.push(name);   // name is string, safe
102     }
103     catch (exc)
104     {
105         // Sometimes we get exceptions trying to iterate properties
106     }
107 
108     return keys;  // return is safe
109 });
110 
111 /**
112  * Returns the values of an object
113  *
114  * @param {*} map The object
115  *
116  * @return {Array} the values
117  */
118 Arr.values = function(map)
119 {
120     var values = [];
121     try
122     {
123         for (var name in map)
124         {
125             try
126             {
127                 values.push(map[name]);
128             }
129             catch (exc)
130             {
131                 // Sometimes we get exceptions trying to access properties
132                 if (FBTrace.DBG_ERRORS)
133                     FBTrace.dumpPropreties("lib.values FAILED ", exc);
134             }
135         }
136     }
137     catch (exc)
138     {
139         // Sometimes we get exceptions trying to iterate properties
140         if (FBTrace.DBG_ERRORS)
141             FBTrace.dumpPropreties("lib.values FAILED ", exc);
142     }
143 
144     return values;
145 };
146 
147 /**
148  * Removes an item from an array or an array-like object
149  *
150  * @param {Array or Array-Like object} list The array
151  * @param {*} item The item to remove from the object
152  *
153  * @return true if an item as been removed, false otherwise
154  */
155 Arr.remove = function(list, item)
156 {
157     var index = ArrayGen.indexOf(list, item);
158     if (index >= 0)
159     {
160         ArrayGen.splice(list, index, 1);
161         return true;
162     }
163     return false;
164 };
165 /**
166  * Same as Arr.remove but removes all the occurences of item
167  *
168  * @param {Array or Array-Like object} list The array
169  * @param {*} item The item to remove from the object
170  *
171  * @return true if an item as been removed, false otherwise
172  */
173 Arr.removeAll = function(list, item)
174 {
175     var iter = 0;
176 
177     while (Arr.remove(list, item))
178         iter++;
179 
180     return (iter > 0);
181 }
182 
183 /**
184  * Returns a shallow copy of a portion of an array.
185  * @deprecated use Array.prototype.slice instead
186  */
187 Arr.sliceArray = Deprecated.deprecated("use Array.prototype.slice instead",
188 function(array, index)
189 {
190     var slice = [];
191     for (var i = index; i < array.length; ++i)
192         slice.push(array[i]);
193 
194     return slice;
195 });
196 
197 /**
198  * Clone an array. If a function is given as second parameter, the function is called for each
199  * elements of the passed array and the results are put in the new one.
200  *
201  * @param {Array or Array-Like object} [array] The array
202  * @param {function} [fn] The function
203  *
204  * @deprecated Use either Array.slice(array) or Array.map(array, fn) instead. 
205  * see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array
206  *
207  */
208 Arr.cloneArray = Deprecated.deprecated("use either Array.slice or Array.map instead",
209 function(array, fn)
210 {
211     if (fn)
212         return ArrayGen.map(array, fn);
213     else
214         return ArrayGen.slice(array);
215 });
216 
217 /**
218  * @deprecated Use Array.concat(array, array2) or array.concat(array2) instead
219  */
220 Arr.extendArray = Deprecated.deprecated("use Array.prototype.concat or Array.concat instead",
221 function(array, array2)
222 {
223    return array.concat(array2);
224 });
225 
226 /**
227  * insert elements at a specific index
228  * NOTE: that method modifies the array passed as the first parameter
229  *
230  * @param {Array or Array-Like object} array The array in which we insert elements
231  * @param {Integer} index The index
232  * @param {Array or Array-Like object} other The elements to insert
233  *
234  * @return the updated array
235  */
236 Arr.arrayInsert = function(array, index, other)
237 {
238     var splice = ArrayGen.splice.bind(Array, array, index, 0);
239     splice.apply(null, other);
240     return array;
241 }
242 
243 /**
244  * Filters out unique values of an array, saving only the first occurrence of
245  * every value. In case the array is sorted, a faster path is taken.
246  *
247  * @param {Array or Array-Like object} arr The array
248  * @param {Boolean} sorted If set to true, use the faster path
249  *
250  * @return {Array} the array deprived of duplication
251  */
252 Arr.unique = function(arr, sorted)
253 {
254     var ret = [], len = arr.length;
255     if (sorted)
256     {
257         for (var i = 0; i < len; ++i)
258         {
259             // Skip duplicated entries
260             if (i && arr[i-1] === arr[i])
261                 continue;
262             ret.push(arr[i]);
263         }
264     }
265     else
266     {
267         // Keep a map whose ","-prefixed keys represent the values that have
268         // occurred so far in the array (this avoids overwriting e.g. __proto__).
269         var map = {};
270         for (var i = 0; i < len; ++i)
271         {
272             if (!map.hasOwnProperty("," + arr[i]))
273             {
274                 ret.push(arr[i]);
275                 map["," + arr[i]] = 1;
276             }
277         }
278     }
279     return ret;
280 };
281 
282 /**
283  * Sorts an array and eliminate duplicates from it.
284  *
285  * @param {Array or Array-Like object} arr The array
286  * @param {function} sortFunc The function used to sort the array (optional)
287  *
288  * @return {Array} the sorted array
289  */
290 Arr.sortUnique = function(arr, sortFunc)
291 {
292     // make a clone of the array so the original one is preserved
293     var arrCopy = ArrayGen.slice(arr);
294     return Arr.unique(arrCopy.sort(sortFunc), true);
295 };
296 
297 /**
298  * Merge together two arrays, sort the result, and eliminate any duplicates.
299  *
300  * @deprecated use Arr.sortUnique and/or Array.prototype.concat instead
301  */
302 Arr.merge = Deprecated.deprecated("use Arr.sortUnique and/or Array.prototype.concat instead",
303 function(arr1, arr2, sortFunc)
304 {
305     return Arr.sortUnique(arr1.concat(arr2), sortFunc);
306 });
307 
308 // ********************************************************************************************* //
309 
310 return Arr;
311 
312 // ********************************************************************************************* //
313 });
314