1 // vim: set et sw=4 ts=4 sts=4 fdm=marker ff=unix fenc=utf8
  2 /**
  3  * Javascript 的 Array 扩展包
  4  *
  5  * 获取自 Trba 库(http://tbra.googlecode.com)
  6  *
  7  * @see http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array
  8  */
  9
 10 /**
 11  * 获取元素在数组中的位置
 12  *
 13  * @param  Object    元素
 14  * @param  Intger    fromIndex - 起始位置
 15  * @return Intger
 16  */
 17 if (!Array.prototype.indexOf) {
 18     Array.prototype.indexOf = function (obj, fromIndex) {
 19         if (fromIndex == null) {
 20             fromIndex = 0;
 21         } else if (fromIndex < 0) {
 22             fromIndex = Math.max(0, this.length + fromIndex);
 23         }
 24         for (var i = fromIndex; i < this.length; i++) {
 25             if (this[i] === obj)
 26                 return i;
 27         }
 28         return -1;
 29     };
 30 }
 31
 32
 33 /**
 34  * 反向查找元素在数组中的位置
 35  *
 36  * @param  object    元素
 37  * @param  fromIndex 起始位置
 38  * @return int
 39  */
 40 if (!Array.prototype.lastIndexOf) {
 41     Array.prototype.lastIndexOf = function (obj, fromIndex) {
 42         if (fromIndex == null) {
 43             fromIndex = this.length - 1;
 44         } else if (fromIndex < 0) {
 45             fromIndex = Math.max(0, this.length + fromIndex);
 46         }
 47         for (var i = fromIndex; i >= 0; i--) {
 48             if (this[i] === obj)
 49                 return i;
 50         }
 51         return -1;
 52     };
 53 }
 54
 55
 56 /**
 57  * Executes a provided function once per array element.
 58  *
 59  * @param  fucntion Function to test each element of the array.
 60  * @link   http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:forEach
 61  */
 62 if (!Array.prototype.forEach) {
 63     Array.prototype.forEach = function(fun /*, thisp*/) {
 64         var len = this.length;
 65         if (typeof fun != "function") {
 66             throw new TypeError();
 67         }
 68         var thisp = arguments[1];
 69         for (var i = 0; i < len; i++) {
 70             if (i in this) {
 71                 fun.call(thisp, this[i], i, this);
 72             }
 73         }
 74     };
 75 }
 76
 77
 78 /**
 79  * Creates a new array with all elements that pass the test
 80  * implemented by the provided function.
 81  *
 82  * @param  fucntion Function to test each element of the array.
 83  * @link   http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:filter
 84  * @return array
 85  */
 86 if (!Array.prototype.filter) {
 87     Array.prototype.filter = function(fun) {
 88         var len = this.length;
 89         if (typeof fun != "function") {
 90           throw new TypeError();
 91         }
 92         var res   = [];
 93         var thisp = arguments[1];
 94         for (var i = 0; i < len; i++) {
 95             if (i in this) {
 96                 var val = this[i]; // in case fun mutates this
 97                 if (fun.call(thisp, val, i, this)) {
 98                     res.push(val);
 99                 }
100             }
101         }
102         return res;
103     };
104 }
105
106
107 /**
108  * Creates a new array with the results of calling
109  * a provided function on every element in this array.
110  *
111  * @param  fucntion Function to test each element of the array.
112  * @link http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:map
113  * @return array
114  */
115 if (!Array.prototype.map) {
116     Array.prototype.map = function(fun /*, thisp*/) {
117         var len = this.length;
118         if (typeof fun != "function") {
119             throw new TypeError();
120         }
121         var res   = new Array(len);
122         var thisp = arguments[1];
123         for (var i = 0; i < len; i++) {
124             if (i in this) {
125                 res[i] = fun.call(thisp, this[i], i, this);
126             }
127         }
128         return res;
129     };
130 }
131
132
133 /**
134  * Tests whether some element in the array passes the
135  * test implemented by the provided function.
136  *
137  * @param  fucntion Function to test each element of the array.
138  * @link   http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:some
139  * @return bool
140  */
141 if (!Array.prototype.some) {
142     Array.prototype.some = function(fun /*, thisp*/) {
143         var len = this.length;
144         if (typeof fun != "function") {
145             throw new TypeError();
146         }
147
148         var thisp = arguments[1];
149         for (var i = 0; i < len; i++) {
150             if (i in this && fun.call(thisp, this[i], i, this)) {
151                 return true;
152             }
153         }
154
155         return false;
156     };
157 }
158
159
160 /**
161  * Tests whether all elements in the array
162  * pass the test implemented by the provided function.
163  *
164  * @param function Function to test for each element.
165  * @link  http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:every
166  */
167 if (!Array.prototype.every) {
168     Array.prototype.every = function(fun) {
169         var len = this.length;
170         if (typeof fun != "function") {
171             throw new TypeError();
172         }
173         var thisp = arguments[1];
174         for (var i = 0; i < len; i++) {
175             if (i in this && !fun.call(thisp, this[i], i, this)) {
176                 return false;
177             }
178         }
179         return true;
180     };
181 }
182
183
184 /**
185  * Apply a function simultaneously against two values
186  * of the array (from left-to-right) as to reduce it to a single value.
187  *
188  * @param function Function to test for each element.
189  * @link  http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:reduce
190  */
191 if (!Array.prototype.reduce) {
192     Array.prototype.reduce = function(fun /*, initial*/) {
193         var len = this.length;
194         if (typeof fun != "function") {
195             throw new TypeError();
196         }
197         // no value to return if no initial value and an empty array
198         if (len == 0 && arguments.length == 1) {
199           throw new TypeError();
200         }
201
202         var i = 0;
203         if (arguments.length >= 2) {
204             var rv = arguments[1];
205         } else {
206           do {
207                 if (i in this) {
208                     rv = this[i++];
209                     break;
210                 }
211                 // if array contains no values, no initial value to return
212                 if (++i >= len) {
213                   throw new TypeError();
214                 }
215           } while (true);
216         }
217
218         for (; i < len; i++) {
219             if (i in this) {
220                 rv = fun.call(null, rv, this[i], i, this);
221             }
222         }
223
224         return rv;
225     };
226 }
227
228
229 /**
230  * Apply a function simultaneously against two values
231  * of the array (from right-to-left) as to reduce it to a single value.
232  *
233  * @param function Function to test for each element.
234  * @link  http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:reduceRight
235  */
236 if (!Array.prototype.reduceRight) {
237     Array.prototype.reduceRight = function(fun /*, initial*/) {
238         var len = this.length;
239         if (typeof fun != "function") {
240             throw new TypeError();
241         }
242
243         // no value to return if no initial value, empty array
244         if (len == 0 && arguments.length == 1) {
245             throw new TypeError();
246         }
247
248         var i = len - 1;
249         if (arguments.length >= 2) {
250             var rv = arguments[1];
251         } else {
252             do {
253                 if (i in this) {
254                     rv = this[i--];
255                     break;
256                 }
257
258                 // if array contains no values, no initial value to return
259                 if (--i < 0) {
260                   throw new TypeError();
261                 }
262             } while (true);
263         }
264
265         for (; i >= 0; i--) {
266             if (i in this) {
267                 rv = fun.call(null, rv, this[i], i, this);
268             }
269         }
270
271         return rv;
272     };
273 }
274
275
276 /**
277  * 删除数组中重复的元素
278  *
279  * @return Array
280  */
281 if (!Array.prototype.unique) {
282     Array.prototype.unique = function() {
283         var resultArr = [],
284             returnArr = [],
285             origLen = this.length,
286             resultLen;
287
288         function include(arr, value) {
289             for (var i = 0, n = arr.length; i < n; ++i){
290                 if (arr[i] === value) {
291                     return true;
292                 }
293             }
294
295             return false;
296         }
297
298         resultArr.push(this[0]);
299         for (var i = 1; i < origLen; ++i) {
300             if (include(resultArr, this[i])) {
301                 returnArr.push(this[i]);
302             } else {
303                 resultArr.push(this[i]);
304             }
305         }
306
307         resultLen = resultArr.length;
308         this.length = resultLen;
309         for (var i = 0; i < resultLen; ++i){
310             this[i] = resultArr[i];
311         }
312
313         return returnArr;
314     }
315 }
316
317
318 /**
319  * 检查数组是否包含某元素
320  *
321  * @param  object 元素
322  * @return bool
323  */
324 Array.prototype.contains = function (obj) {
325     return this.indexOf(obj) != -1;
326 };
327
328
329 /**
330  * 复制数组中的全部元素
331  *
332  * @return Array
333  */
334 Array.prototype.copy = function (obj) {
335     return this.concat();
336 };
337
338
339 /**
340  * 在 i 之后插入元素
341  *
342  * @param Object 元素
343  * @param Intger 位置
344  */
345 Array.prototype.insertAt = function (obj, i) {
346     this.splice(i, 0, obj);
347 };
348
349
350 /**
351  * 在指定元素之前插入元素
352  *
353  * @param Object obj  插入元素
354  * @param Object obj2 目标元素
355  */
356 Array.prototype.insertBefore = function (obj, obj2) {
357     var i = this.indexOf(obj2);
358     if (i == -1)
359         this.push(obj);
360     else
361         this.splice(i, 0, obj);
362 };
363
364
365 /**
366  * 删除指定位置的元素
367  *
368  * @param Intger 位置
369  */
370 Array.prototype.removeAt = function (i) {
371     this.splice(i, 1);
372 };
373
374
375 /**
376  * 删除一个数组中指定的元素
377  *
378  * @param Object obj 目标元素
379  */
380 Array.prototype.remove = function (obj) {
381     var i = this.indexOf(obj);
382     if (i != -1) {
383         this.splice(i, 1);
384     }
385 };
386
387
388 /**
389  * 删除全部数组中指定的元素
390  *
391  * @param Object obj 目标元素
392  */
393 Array.prototype.removeAll = function (obj) {
394     for (var i = 0, length = this.length; i < length; i++) {
395         var j = this.indexOf(obj);
396         if (j != -1) {
397             this.splice(j, 1);
398         } else {
399             break;
400         }
401     }
402 };