1 /*
  2  * vim:set spell spl=en fo=wan1croqlt tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix:
  3  *
  4  * Copyright (C) 2008, 2009 Mihai Şucan
  5  *
  6  * This file is part of libmacrame.
  7  *
  8  * Libmacrame is free software: you can redistribute it and/or modify
  9  * it under the terms of the GNU General Public License as published by
 10  * the Free Software Foundation, either version 3 of the License, or
 11  * (at your option) any later version.
 12  *
 13  * Libmacrame is distributed in the hope that it will be useful,
 14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16  * GNU General Public License for more details.
 17  *
 18  * You should have received a copy of the GNU General Public License
 19  * along with Libmacrame.  If not, see <http://www.gnu.org/licenses/>.
 20  *
 21  * $URL: http://code.google.com/p/libmacrame $
 22  * $Date: 2009-04-17 18:32:39 +0300 $
 23  *
 24  */
 25 
 26 /**
 27  * @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a>
 28  * @version pre-alpha
 29  * @requires core.js
 30  *
 31  * @fileOverview This is a plugin for libmacrame which adds important methods 
 32  * for manipulating strings, arrays and objects.
 33  */
 34 
 35 (function () {
 36 // We will use $ to refer to libmacrame in this plugin.
 37 var $ = libmacrame;
 38 
 39 /*
 40  * If extend_objects is set to true:
 41  * - adds several static methods to the global Object.
 42  * - adds new methods to the Array.prototype.
 43  * - adds new methods to the String.prototype.
 44  */
 45 var extend_objects = true;
 46 
 47 // We store all the methods in $.js for easy reuse in other cases.
 48 
 49 /**
 50  * Holds methods for manipulating several types of JavaScript objects.
 51  *
 52  * <p>This plugin extends the global Object, Array and String prototypes, adding 
 53  * new methods.
 54  *
 55  * <p>The new methods implement common functionality.
 56  *
 57  * @namespace Holds methods for manipulating several types of JavaScript 
 58  * objects.
 59  */
 60 $.js = {};
 61 
 62 /**
 63  * Holds the methods used for manipulating objects. These methods are very 
 64  * similar to their Array counter-parts.
 65  *
 66  * <p>By default, the global <var>Object</var> (not the 
 67  * <var>Object.prototype</var>!) is extended to contain all the methods in this 
 68  * namespace. Besides these methods, <var>Object</var> will have one more 
 69  * method: <code>Object.extend</code>, which is an alias of {@link $.extend}.
 70  *
 71  * <p>All of these methods use their <var>this</var> object. As such, you need 
 72  * to pass the correct <var>this</var> object.
 73  *
 74  * <p>Note that the methods provided iterate over the object properties using 
 75  * <code>for (<var>property</var> in <var>this</var>)</code>. As such, the order 
 76  * depends on the browser implementation. Also, those properties which are not 
 77  * enumerable 
 78  * (<code><var>this</var>.propertyIsEnumerable(<var>property</var>)</code>) are 
 79  * skipped.
 80  *
 81  * <p>In the examples provided for each method we will assume that the objects 
 82  * defined are already extended. Thus code like 
 83  * <code><var>obj</var>.filter(<var>callback</var>)</code> can be written 
 84  * directly.
 85  *
 86  * @example
 87  * // To use these methods proceed as follows:
 88  *
 89  * <code>var <var>obj</var> = {'a' : 'test1', 'b' : 'test2', 'c' : 'test3'};
 90  *
 91  * Object.forEach.call(<var>obj</var>, function (<var>value</var>) {
 92  *   // ...
 93  * });</code>
 94  *
 95  * // Instead of forEach, you can use any of the methods defined in this
 96  * // namespace.
 97  *
 98  * @example
 99  * Additionally, you can extend your object to have all these methods:
100  *
101  * <code>Object.extend.call(<var>obj</var>, Object);</code>
102  *
103  * // After doing so, you can directly use any desired method.
104  * // For example:
105  * <code><var>obj</var>.forEach(<var>callback</var>)</code>.
106  *
107  * @namespace Holds the methods used for manipulating Objects.
108  *
109  * @see $.js.Array The Array functions which provide similar functionality.
110  */
111 $.js.Object = {
112   /**
113    * Filter the current object using the <var>callback</var> function.
114    *
115    * <p>The <var>callback</var> function is invoked for each property in the 
116    * current object, and it is given three arguments: the value, the property 
117    * name, and the entire object being iterated over.
118    *
119    * <p>This method does not alter the current object.
120    *
121    * @example
122    * <code>var <var>obj1</var> = {'a' : 5, 'b' : 11, 'c' : 7};</code>
123    *
124    * // Filter only the properties with a value lower than 10:
125    *
126    * <code>var <var>obj2</var> = <var>obj1</var>.filter(
127    *   function (<var>value</var>, <var>property</var>, <var>object</var>) {
128    *     return <var>value</var> < 10;
129    *   }
130    * );</code>
131    *
132    * // Now <code><var>obj2</var> = {'a' : 5, 'c' : 7}</code>.
133    *
134    * @param {Function} callback The <var>callback</var> function used for 
135    * filtering the current object.
136    *
137    * @param {Object} [thisObject] The object which will be the <var>this</var> 
138    * within the scope of the <var>callback</var> function, for each invocation.
139    *
140    * @throws {TypeError} If <var>callback</var> is not a function.
141    *
142    * @returns {Object} The new object contains only the properties for which the 
143    * <var>callback</var> function returned true.
144    *
145    * @see $.js.Array.filter
146    */
147   filter : function (callback, thisObject) {
148     if (typeof callback != 'function') {
149       throw new TypeError('The first argument must be a function.');
150     }
151 
152     var resObj = {}, name;
153     for (name in this) {
154       if (this.propertyIsEnumerable(name) && callback.call(thisObject, this[name], 
155           name, this)) {
156         resObj[name] = this[name];
157       }
158     }
159 
160     return resObj;
161   },
162 
163   /**
164    * Execute the <var>callback</var> function for each property in the current 
165    * object.
166    *
167    * <p>The <var>callback</var> function is invoked with three arguments: the 
168    * value, the property name, and the entire object being iterated over.
169    *
170    * <p>This method does not alter the current object.
171    *
172    * @example
173    * <code>var <var>obj</var> = {'a' : 10, 'b' : 11, 'c' : 12};</code>
174    *
175    * // alert() each property and value.
176    * <code><var>obj</var>.forEach(
177    *   function (<var>value</var>, <var>property</var>) {
178    *     alert(<var>property</var> + ' : ' + <var>value</var>);
179    *   }
180    * );</code>
181    *
182    * @param {Function} callback The <var>callback</var> function used for 
183    * traversing the current object.
184    *
185    * @param {Object} [thisObject] The object which will be the <var>this</var> 
186    * within the scope of the <var>callback</var> function, for each invocation.
187    *
188    * @throws {TypeError} If <var>callback</var> is not a function.
189    *
190    * @see $.js.Array.forEach
191    */
192   forEach : function (callback, thisObject) {
193     if (typeof callback != 'function') {
194       throw new TypeError('The first argument must be a function.');
195     }
196 
197     for (var name in this) {
198       if (this.propertyIsEnumerable(name)) {
199         callback.call(thisObject, this[name], name, this);
200       }
201     }
202   },
203 
204   /**
205    * Check if the <var>callback</var> function returns true for every property 
206    * in the current object.
207    *
208    * <p>The <var>callback</var> function is invoked for each property in the 
209    * current object, and it is given three arguments: the value, the property 
210    * name, and the entire object being iterated over.
211    *
212    * <p>Iteration over the object stops once reaching the end, or once the 
213    * <var>callback</var> function returns false.
214    *
215    * <p>This method does not alter the current object.
216    *
217    * @example
218    * <code>var <var>obj</var> = {'a' : 10, 'b' : 11, 'c' : 12};</code>
219    *
220    * // The following returns true.
221    * <code><var>obj</var>.every(function (<var>value</var>) {
222    *   return <var>value</var> > 9;
223    * });</code>
224    *
225    * @param {Function} callback The <var>callback</var> function used for 
226    * traversing the current object.
227    *
228    * @param {Object} [thisObject] The object which will be the <var>this</var> 
229    * within the scope of the <var>callback</var> function, for each invocation.
230    *
231    * @returns {Boolean} False is returned if the <var>callback</var> returns 
232    * false once. Otherwise, this method returns true.
233    *
234    * @throws {TypeError} If <var>callback</var> is not a function.
235    *
236    * @see $.js.Array.every
237    */
238   every : function (callback, thisObject) {
239     if (typeof callback != 'function') {
240       throw new TypeError('The first argument must be a function.');
241     }
242 
243     for (var name in this) {
244       if (this.propertyIsEnumerable(name) && !callback.call(thisObject, 
245             this[name], name, this)) {
246         return false;
247       }
248     }
249 
250     return true;
251   },
252 
253   /**
254    * Check if the <var>callback</var> function returns true for at least one 
255    * property in the current object.
256    *
257    * <p>The <var>callback</var> function is invoked for each property in the 
258    * current object, and it is given three arguments: the value, the property 
259    * name, and the entire object being iterated over.
260    *
261    * <p>Iteration over the object stops once reaching the end, or once the 
262    * <var>callback</var> function returns true.
263    *
264    * <p>This method does not alter the current object.
265    *
266    * @example
267    * <code>var <var>obj</var> = {'a' : 10, 'b' : 11, 'c' : 12};</code>
268    *
269    * // The following returns true.
270    * <code><var>obj</var>.some(function (<var>value</var>) {
271    *   return <var>value</var> > 11;
272    * });</code>
273    *
274    * @param {Function} callback The <var>callback</var> function used for 
275    * traversing the current object.
276    *
277    * @param {Object} [thisObject] The object which will be the <var>this</var> 
278    * within the scope of the <var>callback</var> function, for each invocation.
279    *
280    * @throws {TypeError} If <var>callback</var> is not a function.
281    *
282    * @returns {Boolean} True is returned if the <var>callback</var> returns true 
283    * once. Otherwise, this method returns false.
284    *
285    * @see $.js.Array.some
286    */
287   some : function (callback, thisObject) {
288     if (typeof callback != 'function') {
289       throw new TypeError('The first argument must be a function.');
290     }
291 
292     for (var name in this) {
293       if (this.propertyIsEnumerable(name) && callback.call(thisObject, this[name], 
294             name, this)) {
295         return true;
296       }
297     }
298 
299     return false;
300   },
301 
302   /**
303    * Create a new object with the same properties as the current object using 
304    * the values returned by the <var>callback</var> function.
305    *
306    * <p>The <var>callback</var> function is invoked for each property in the 
307    * current object, and it is given three arguments: the value, the property 
308    * name, and the entire object being iterated over.
309    *
310    * <p>This method does not alter the current object.
311    *
312    * @example
313    * <code>var <var>obj1</var> = {'a' : 5, 'b' : 11, 'c' : 7};</code>
314    *
315    * // Let's double the numbers:
316    * <code>var <var>obj2</var> = <var>obj1</var>.map(function (<var>value</var>) {
317    *   return <var>value</var> * 2;
318    * });</code>
319    *
320    * // Now <code><var>obj2</var> = {'a' : 10, 'b' : 22, 'c' : 14}</code>.
321    *
322    * @param {Function} callback The <var>callback</var> function used for 
323    * traversing the current object.
324    *
325    * @param {Object} [thisObject] The object which will be the <var>this</var> 
326    * within the scope of the <var>callback</var> function, for each invocation.
327    *
328    * @throws {TypeError} If <var>callback</var> is not a function.
329    *
330    * @returns {Object} The new object has the same properties as the current 
331    * object, but the values are those returned by the <var>callback</var> 
332    * function.
333    *
334    * @see $.js.Array.map
335    */
336   map : function (callback, thisObject) {
337     if (typeof callback != 'function') {
338       throw new TypeError('The first argument must be a function.');
339     }
340 
341     var resObj = {};
342 
343     for (var name in this) {
344       if (this.propertyIsEnumerable(name)) {
345         resObj[name] = callback.call(thisObject, this[name], name, this);
346       }
347     }
348 
349     return resObj;
350   },
351 
352   /**
353    * Apply the <var>callback</var> function to two values from the current 
354    * object, simultaneously, for the purpose of reducing the object to a single 
355    * value.
356    *
357    * <p>The <var>callback</var> function is called for each property in the 
358    * current object, and it is given four arguments: the previous value, the 
359    * current value, the current property name, and the entire object being 
360    * iterated over.
361    *
362    * <p>This method does not alter the current object.
363    *
364    * <p>Note that the execution order is the same as doing <code>for (prop in 
365    * obj)</code>.
366    *
367    * @example
368    * <code>var <var>obj</var> = {'a' : 5, 'b' : 10, 'c' : 6};</code>
369    *
370    * // Let's calculate the sum:
371    * <code>var <var>result</var> = <var>obj</var>.reduce(
372    *   function (<var>sum</var>, <var>value</var>) {
373    *     return <var>sum</var> + <var>value</var>;
374    *   },
375    * 0);</code>
376    *
377    * // Now <var>result</var> = 21.
378    *
379    * @param {Function} callback The <var>callback</var> function used for 
380    * traversing the current object.
381    *
382    * @param [initialValue] The initial value used when the <var>callback</var> 
383    * function is first invoked. If the initial value is not provided, then the 
384    * method uses the value of the first property found in the object being 
385    * iterated.
386    *
387    * @throws {TypeError} If <var>callback</var> is not a function.
388    *
389    * @returns The result of the last <var>callback</var> function invocation.
390    *
391    * @see $.js.Array.reduce
392    * @see $.js.Array.reduceRight
393    */
394   reduce : function (callback, initialValue) {
395     if (typeof callback != 'function') {
396       throw new TypeError('The first argument must be a function.');
397     }
398 
399     var res, found_initial = false;
400 
401     if (typeof initialValue != 'undefined') {
402       found_initial = true;
403       res = initialValue;
404     }
405 
406     for (var name in this) {
407       if (!this.propertyIsEnumerable(name)) {
408         continue;
409       }
410 
411       if (!found_initial) {
412         found_initial = true;
413         res = this[name];
414         continue;
415       }
416 
417       res = callback.call(null, res, this[name], name, this);
418     }
419 
420     return res;
421   }
422 };
423 
424 /**
425  * Holds the methods used for manipulating arrays and other array-like objects.
426  * 
427  * <p>These methods are very similar to their Object counter-parts. Also, most 
428  * of the methods (if not all) are already implemented natively in the global 
429  * <var>Array.prototype</var> object (new in JavaScript 1.6).
430  *
431  * <p>By default, the global <var>Array.prototype</var> object is extended to 
432  * contain all the methods in this namespace, if they do not exists already.  
433  * Thus, native Array methods are not overwritten.
434  *
435  * <p>All of these methods use their <var>this</var> object. As such, you need 
436  * to pass the correct <var>this</var> object.
437  *
438  * <p>Note that the methods provided iterate over the object properties using 
439  * <code>for (var <var>i</var> = 0; <var>i</var> < <var>this</var>.length; 
440  * <var>i</var>++) { ... <var>this[i]</var> ... }</code>. As such, any object 
441  * can be used if it provides index-based access to its properties, together 
442  * with the <var>length</var> property. 
443  *
444  * <p>The implementations do minimal type checks, such that the methods can be 
445  * used together with NodeLists and other Array-like objects.
446  *
447  * <p>In the examples provided for each method we will assume that the arrays 
448  * defined already have the methods. Thus code like 
449  * <code><var>array</var>.filter(<var>callback</var>)</code> can be written 
450  * directly.
451  *
452  * @example
453  * // To use these methods with a NodeList proceed as follows:
454  *
455  * <code>var <var>nodes</var> = document.body.childNodes;
456  *
457  * $.js.Array.forEach.call(<var>nodes</var>, function (<var>node</var>) {
458  *   // ...
459  * });</code>
460  *
461  * // Instead of forEach, you can use any of the methods defined in this
462  * // namespace.
463  *
464  * @example
465  * Additionally, you can extend your object to have all these methods:
466  *
467  * <code>$.extend.call(NodeList, $.js.Array);</code>
468  *
469  * // After doing so, you can directly use any desired method.
470  * // For example:
471  * <code><var>nodes</var>.forEach(<var>callback</var>)</code>.
472  *
473  * @namespace Holds the methods used for manipulating arrays and other 
474  * array-like objects.
475  *
476  * @see $.js.Object The Object methods which provide similar functionality for 
477  * objects.
478  */
479 $.js.Array = {
480   /**
481    * Extract a section of the current array and return it.
482    *
483    * @example
484    * <code>var <var>arr1</var> = ['a', 'b', 'c', 'd'];
485    * var <var>arr2</var> = <var>arr1</var>.slice(1, 3);</code>
486    * // Now <code><var>arr2</var> = ['b', 'c']</code>.
487    *
488    * <code>var <var>arr3</var> = <var>arr1</var>.slice(1);</code>
489    * // Now <code><var>arr3</var> = ['b', 'c', 'd']</code>.
490    *
491    * <code>var <var>arr4</var> = <var>arr1</var>.slice(0, -2);</code>
492    * // Now <code><var>arr4</var> = ['a', 'b']</code>.
493    *
494    * @param {Number} begin Zero-based index from where to start the extraction 
495    * of array elements.
496    *
497    * @param {Number} [end=this.length] Zero-based index where to end the 
498    * extraction of array elements.
499    * 
500    * <p>If the argument is not given, then the end is considered the array 
501    * length. Thus, the slice being extracted contains all the array elements 
502    * from the given <var>begin</var> index.
503    * 
504    * <p>If the number is negative, then the <var>end</var> index is calculated 
505    * by a simple formula: <code><var>end</var> = <var>array.length</var> 
506    * - <var>end</var></code>. Thus, given a negative index like -3, it would 
507    *   mean that the slice being extracted will not contain the last three 
508    *   elements.
509    *
510    * @throws {TypeError} If <var>begin</var> is not a number, or if it is 
511    * a negative number.
512    *
513    * @throws {TypeError} If <var>end</var> is provided, but it is not a number.
514    *
515    * @returns {Array} The new array returned contains all the elements starting 
516    * from the <var>begin</var> index (including it) up to the <var>end</var> 
517    * index (excluding it).
518    */
519   slice : function (begin, end) {
520     if (isNaN(begin) || begin < 0) {
521       throw new TypeError("The first argument is not a valid number.");
522     } else if (end && isNaN(end)) {
523       throw new TypeError("The second argument is not a valid number.");
524     }
525 
526     var resArr = [], i = begin, n = this.length;
527 
528     if (end && end < 0) {
529       end = n - end;
530     }
531 
532     if (!end || end > n) {
533       end = n;
534     }
535 
536     for ( ; i < end; i++) {
537       resArr.push(this[i]);
538     }
539 
540     return resArr;
541   },
542 
543   /**
544    * Join all the elements from the current array into a single string using 
545    * a separator.
546    *
547    * <p>Each element of the array is converted to a string using the automatic 
548    * <code>toString()</code> method invocation. A string separator is added into 
549    * the result string between each array element.
550    *
551    * <p>This method does not alter the current array.
552    *
553    * @example
554    * <code>var <var>arr</var> = ['a', 'b', 'c'];
555    * var <var>str</var> = <var>arr</var>.join('-');</code>
556    *
557    * // Now <code><var>str</var> = 'a-b-c'</code>.
558    *
559    * @param {String} [separator] The separator string which gets added between 
560    * each array element into the result string. If the argument is not provided, 
561    * then a comma will be used.
562    *
563    * @returns {String} The new string containing all the elements in the current 
564    * array, converted into strings, separated by the given <var>separator</var>.
565    */
566   join : function (separator) {
567     if (typeof separator == 'undefined') {
568       separator = ',';
569     }
570 
571     var res = '', i = 0, n = this.length;
572     for ( ; i < n; i++) {
573       if (i > 0) {
574         res += separator;
575       }
576       res += this[i];
577     }
578 
579     return res;
580   },
581 
582   /**
583    * Concatenate multiple arrays.
584    *
585    * <p>This method takes any number of arguments.
586    *
587    * @example
588    * <code>var <var>arr1</var> = [5, 11, 7];
589    * var <var>arr2</var> = <var>arr1</var>.concat('test', [1, 2, 3], 4);</code>
590    *
591    * // Now <code><var>arr2</var> = [5, 11, 7, 'test', 1, 2, 3, 4]</code>.
592    *
593    * @param value1 Array/value to concatenate.
594    * @param [valueN] Array/value to concatenate.
595    *
596    * @returns {Array} The result is a new array consisting of:
597    * <ol>
598    *   <li>the <var>this</var> array object on which the <code>concat</code> 
599    *   method was called;
600    *   <li>each element of the arrays passed as arguments;
601    *   <li>if an argument is not an array, then the argument value is added 
602    *   as-is.
603    * </ol>
604    *
605    * <p>The current array is not altered, nor the arrays passed as arguments.
606    */
607   concat : function () {
608     var resArr = [], arr, y, i = -1, m, n = arguments.length;
609     for ( ; i < n; i++) {
610       if (i == -1) {
611         arr = this;
612       } else {
613         arr = arguments[i];
614       }
615 
616       if (!arr.length  || isNaN(arr.length)) {
617         resArr.push(arrr);
618         continue;
619       }
620 
621       m = arr.length;
622       for (y = 0; y < m; y++) {
623         if (y in arr) {
624           resArr.push(arr[y]);
625         }
626       }
627     }
628 
629     return resArr;
630   },
631 
632   /**
633    * Filter the current array using the <var>callback</var> function.
634    *
635    * <p>The <var>callback</var> function is invoked for each element in the 
636    * current array, and it is given three arguments: the value, the index, and 
637    * the entire array being iterated over.
638    *
639    * <p>This method does not alter the current array.
640    *
641    * @example
642    * <code>var <var>arr1</var> = [5, 11, 7];</code>
643    *
644    * // Filter only the elements with a value lower than 10:
645    *
646    * <code>var <var>arr2</var> = <var>arr1</var>.filter(
647    *   function (<var>value</var>, <var>index</var>, <var>array</var>) {
648    *     return <var>value</var> < 10;
649    *   }
650    * );</code>
651    *
652    * // Now <code><var>arr2</var> = [5, 7]</code>.
653    *
654    * @param {Function} callback The <var>callback</var> function used for 
655    * filtering the current array.
656    *
657    * @param {Object} [thisObject] The object which will be the <var>this</var> 
658    * within the scope of the <var>callback</var> function, for each invocation.
659    *
660    * @throws {TypeError} If <var>callback</var> is not a function.
661    *
662    * @returns {Array} The new array contains only the elements for which the 
663    * <var>callback</var> function returned true.
664    *
665    * @see $.js.Object.filter
666    */
667   filter : function (callback, thisObject) {
668     if (typeof callback != 'function') {
669       throw new TypeError('The first argument must be a function.');
670     }
671 
672     var resArr = [], i = 0, n = this.length;
673     for ( ; i < n; i++) {
674       if ((i in this) && callback.call(thisObject, this[i], i, this)) {
675         resArr.push(this[i]);
676       }
677     }
678 
679     return resArr;
680   },
681 
682   /**
683    * Execute the <var>callback</var> function for each element in the current 
684    * array.
685    *
686    * <p>The <var>callback</var> function is invoked with three arguments: the 
687    * value, the index, and the entire array being iterated over.
688    *
689    * <p>This method does not alter the current array.
690    *
691    * @example
692    * <code>var <var>arr</var> = [10, 11, 12];</code>
693    *
694    * // alert() each index and value.
695    * <code><var>arr</var>.forEach(
696    *   function (<var>value</var>, <var>index</var>) {
697    *     alert(<var>index</var> + ' : ' + <var>value</var>);
698    *   }
699    * );</code>
700    *
701    * @param {Function} callback The <var>callback</var> function used for 
702    * traversing the current array.
703    *
704    * @param {Object} [thisObject] The object which will be the <var>this</var> 
705    * within the scope of the <var>callback</var> function, for each invocation.
706    *
707    * @throws {TypeError} If <var>callback</var> is not a function.
708    *
709    * @see $.js.Object.forEach
710    */
711   forEach : function (callback, thisObject) {
712     if (typeof callback != 'function') {
713       throw new TypeError('The first argument must be a function.');
714     }
715 
716     for (var i = 0, n = this.length, val ; i < n; i++) {
717       if (i in this) {
718         callback.call(thisObject, this[i], i, this);
719       }
720     }
721   },
722 
723   /**
724    * Check if the <var>callback</var> function returns true for every element in 
725    * the current array.
726    *
727    * <p>The <var>callback</var> function is invoked for each element in the 
728    * current array, and it is given three arguments: the value, the index, and 
729    * the entire array being iterated over.
730    *
731    * <p>Iteration over the array stops once reaching the end, or once the 
732    * <var>callback</var> function returns false.
733    *
734    * <p>This method does not alter the current array.
735    *
736    * @example
737    * <code>var <var>arr</var> = [10, 11, 12];</code>
738    *
739    * // The following returns true.
740    * <code><var>arr</var>.every(function (<var>value</var>) {
741    *   return <var>value</var> > 9;
742    * });</code>
743    *
744    * @param {Function} callback The <var>callback</var> function used for 
745    * traversing the current array.
746    *
747    * @param {Object} [thisObject] The object which will be the <var>this</var> 
748    * within the scope of the <var>callback</var> function, for each invocation.
749    *
750    * @throws {TypeError} If <var>callback</var> is not a function.
751    *
752    * @returns {Boolean} False is returned if the <var>callback</var> returns 
753    * false once. Otherwise, this method returns true.
754    *
755    * @see $.js.Object.every
756    */
757   every : function (callback, thisObject) {
758     if (typeof callback != 'function') {
759       throw new TypeError('The first argument must be a function.');
760     }
761 
762     for (var i = 0, n = this.length; i < n; i++) {
763       if ((i in this) && !callback.call(thisObject, this[i], i, this)) {
764         return false;
765       }
766     }
767 
768     return true;
769   },
770 
771   /**
772    * Check if the <var>callback</var> function returns true for at least one 
773    * element in the current array.
774    *
775    * <p>The <var>callback</var> function is invoked for each element in the 
776    * current array, and it is given three arguments: the value, the index, and 
777    * the entire array being iterated over.
778    *
779    * <p>Iteration over the array stops once reaching the end, or once the 
780    * <var>callback</var> function returns true.
781    *
782    * <p>This method does not alter the current array.
783    *
784    * @example
785    * <code>var <var>arr</var> = [10, 11, 12];</code>
786    *
787    * // The following returns true.
788    * <code><var>arr</var>.some(function (<var>value</var>) {
789    *   return <var>value</var> > 11;
790    * });</code>
791    *
792    * @param {Function} callback The <var>callback</var> function used for 
793    * traversing the current array.
794    *
795    * @param {Object} [thisObject] The object which will be the <var>this</var> 
796    * within the scope of the <var>callback</var> function, for each invocation.
797    *
798    * @throws {TypeError} If <var>callback</var> is not a function.
799    *
800    * @returns {Boolean} True is returned if the <var>callback</var> returns true 
801    * once. Otherwise, this method returns false.
802    *
803    * @see $.js.Object.some
804    */
805   some : function (callback, thisObject) {
806     if (typeof callback != 'function') {
807       throw new TypeError('The first argument must be a function.');
808     }
809 
810     for (var i = 0, n = this.length; i < n; i++) {
811       if ((i in this) && callback.call(thisObject, this[i], i, this)) {
812         return true;
813       }
814     }
815 
816     return false;
817   },
818 
819   /**
820    * Create a new array with the same length as the current array using the 
821    * values returned by the <var>callback</var> function.
822    *
823    * <p>The <var>callback</var> function is invoked for each element in the 
824    * current array, and it is given three arguments: the value, the index, and 
825    * the entire array being iterated over.
826    *
827    * <p>This method does not alter the current array.
828    *
829    * @example
830    * <code>var <var>arr1</var> = [5, 11, 7];</code>
831    *
832    * // Let's double the numbers:
833    * <code>var <var>arr2</var> = <var>arr1</var>.map(function (<var>value</var>) {
834    *   return <var>value</var> * 2;
835    * });</code>
836    *
837    * // Now <code><var>arr2</var> = [10, 22, 14]</code>.
838    *
839    * @param {Function} callback The <var>callback</var> function used for 
840    * traversing the current array.
841    *
842    * @param {Object} [thisObject] The object which will be the <var>this</var> 
843    * within the scope of the <var>callback</var> function, for each invocation.
844    *
845    * @throws {TypeError} If <var>callback</var> is not a function.
846    *
847    * @returns {Array} The new array has the same length as the current array, 
848    * but the values are those returned by the <var>callback</var> function.
849    *
850    * @see $.js.Object.map
851    */
852   map : function (callback, thisObject) {
853     if (typeof callback != 'function') {
854       throw new TypeError('The first argument must be a function.');
855     }
856 
857     var n = this.length;
858     var resArr = new Array(n);
859 
860     for (var i = 0; i < n; i++) {
861       if (i in this) {
862         resArr[i] = callback.call(thisObject, this[i], i, this);
863       }
864     }
865 
866     return resArr;
867   },
868 
869   /**
870    * Apply the <var>callback</var> function to two values from the current 
871    * array, from left to right, simultaneously, for the purpose of reducing the 
872    * array to a single value.
873    *
874    * <p>The <var>callback</var> function is called for each element in the 
875    * current array, and it is given four arguments: the previous value, the 
876    * current value, the current index, and the entire array being iterated over.
877    *
878    * <p>This method does not alter the current array.
879    *
880    * <p>Iteration starts from the first element in the current array (index 0) 
881    * going up to the last element (<code>array.length-1</code>), one by one.
882    *
883    * @example
884    * <code>var <var>arr</var> = [5, 10, 6];</code>
885    *
886    * // Let's calculate the sum:
887    * <code>var <var>result</var> = <var>arr</var>.reduce(
888    *   function (<var>sum</var>, <var>value</var>) {
889    *     return <var>sum</var> + <var>value</var>;
890    *   },
891    * 0);</code>
892    *
893    * // Now <var>result</var> = 21.
894    *
895    * @param {Function} callback The <var>callback</var> function used for 
896    * traversing the current array.
897    *
898    * @param [initialValue] The initial value used when the <var>callback</var> 
899    * function is first invoked. If the initial value is not provided, then the 
900    * method uses the value of the first element found in the array being 
901    * iterated.
902    *
903    * @throws {TypeError} If <var>callback</var> is not a function.
904    *
905    * @returns The result of the last <var>callback</var> function invocation.
906    *
907    * @see $.js.Array.reduceRight
908    * @see $.js.Object.reduce
909    */
910   reduce : function (callback, initialValue) {
911     if (typeof callback != 'function') {
912       throw new TypeError('The first argument must be a function.');
913     }
914 
915     var res, found_initial = false, n = this.length;
916 
917     if (typeof initialValue != 'undefined') {
918       found_initial = true;
919       res = initialValue;
920     } else if (n == 0) {
921       throw new TypeError();
922     }
923 
924     for (var i = 0; i < n; i++) {
925       if (!(i in this)) {
926         continue;
927       }
928 
929       if (!found_initial) {
930         found_initial = true;
931         res = this[i];
932         continue;
933       }
934 
935       res = callback.call(null, res, this[i], i, this);
936     }
937 
938     if (!found_initial) {
939       throw new TypeError();
940     }
941 
942     return res;
943   },
944 
945   /**
946    * Apply the <var>callback</var> function to two values from the current 
947    * array, from right to left, simultaneously, for the purpose of reducing the 
948    * array to a single value.
949    *
950    * <p>The <var>callback</var> function is called for each element in the 
951    * current array, and it is given four arguments: the previous value, the 
952    * current value, the current index, and the entire array being iterated over.
953    *
954    * <p>This method does not alter the current array.
955    *
956    * <p>Iteration starts from the last element in the current array 
957    * (<code>array.length-1</code>) going down to 0, the first element, one by 
958    * one.
959    *
960    * @example
961    * <code>var arr = [5, 10, 6];</code>
962    *
963    * // Let's calculate the sum:
964    * <code>var result = arr.reduce(function (<var>sum</var>, <var>value</var>) {
965    *   return <var>sum</var> + <var>value</var>;
966    * }, 0);</code>
967    *
968    * // Now result = 21.
969    *
970    * @param {Function} callback The callback function used for traversing the 
971    * current array.
972    *
973    * @param [initialValue] The initial value used when the <var>callback</var> 
974    * function is first invoked. If the initial value is not provided, then the 
975    * method uses the value of the last element found in the array being 
976    * iterated.
977    *
978    * @throws {TypeError} If <var>callback</var> is not a function.
979    *
980    * @returns The result of the last <var>callback</var> function invocation.
981    *
982    * @see $.js.Array.reduce
983    * @see $.js.Object.reduce
984    */
985   reduceRight : function (callback, initialValue) {
986     if (typeof callback != 'function') {
987       throw new TypeError('The first argument must be a function.');
988     }
989 
990     var res, found_initial = false, i = this.length - 1;
991 
992     if (typeof initialValue != 'undefined') {
993       found_initial = true;
994       res = initialValue;
995     } else if (i == -1) {
996       return null;
997     }
998 
999     for ( ; i >= 0; i--) {
1000       if (!(i in this)) {
1001         continue;
1002       }
1003 
1004       if (!found_initial) {
1005         found_initial = true;
1006         res = this[i];
1007         continue;
1008       }
1009 
1010       res = callback.call(null, res, this[i], i, this);
1011     }
1012 
1013     if (!found_initial) {
1014       throw new TypeError();
1015     }
1016 
1017     return res;
1018   },
1019 
1020   /**
1021    * Return the first index where the given value is found in the current array.
1022    *
1023    * <p>Note that the method uses strict comparison for finding the value 
1024    * (<code>===</code>).
1025    *
1026    * @example
1027    * <code>var arr = ['a', 'b', 'c', 'd', 'b'];
1028    * arr.indexOf('b');</code>
1029    * // Returns 1.
1030    *
1031    * <code>arr.indexOf('b', 2);</code>
1032    * // Returns 4.
1033    *
1034    * <code>arr.indexOf('b', -2);</code>
1035    * // Returns 4.
1036    *
1037    * @param value The value you want to find in the current array.
1038    *
1039    * @param {Number} [offset=0] Zero-based index from where to start searching 
1040    * the array.
1041    * 
1042    * <p>If the argument is not given, the entire array is searched.
1043    * 
1044    * <p>If the number is negative, then the <var>offset</var> is considered to 
1045    * be an offset from the end of the array. Thus, given a negative index like 
1046    * -3, it would mean that only the last three elements are checked.
1047    *
1048    * <p>The method always starts searching from the calculated/given 
1049    * <var>offset</var> increasing one by one up to the array length (forward 
1050    * search).
1051    *
1052    * @throws {TypeError} If <var>value</var> is undefined.
1053    *
1054    * @returns {Number} The element index where the first occurrence of 
1055    * <var>value</var> was found in the current array. If the value was not 
1056    * found, -1 is returned.
1057    *
1058    * @see $.js.Array.lastIndexOf
1059    */
1060   indexOf : function (value, offset) {
1061     var n = this.length;
1062 
1063     if (n == 0) {
1064       return -1;
1065     } else if (typeof value == 'undefined') {
1066       throw new TypeError('The first argument is undefined.');
1067     }
1068 
1069     if (isNaN(offset)) {
1070       offset = 0;
1071     } else {
1072       offset = offset < 0 ? Math.ceil(offset) : Math.floor(offset);
1073       if (offset < 0) {
1074         offset += n;
1075       }
1076     }
1077 
1078     for ( ; offset < n; offset++) {
1079       if ((offset in this) && this[offset] === value) {
1080         return offset;
1081       }
1082     }
1083 
1084     return -1;
1085   },
1086 
1087   /**
1088    * Return the last index where the given value is found in the current array.
1089    *
1090    * <p>Note that the method uses strict comparison for finding the value 
1091    * (<code>===</code>).
1092    *
1093    * @example
1094    * <code>var <var>arr</var> = ['a', 'b', 'c', 'd', 'b'];
1095    * <var>arr</var>.lastIndexOf('b');</code>
1096    * // Returns 4.
1097    *
1098    * <code><var>arr</var>.lastIndexOf('b', 2);</code>
1099    * // Returns 1.
1100    *
1101    * <code><var>arr</var>.lastIndexOf('b', -2);</code>
1102    * // Returns 1.
1103    *
1104    * @param value The value you want to find in the current array.
1105    *
1106    * @param {Number} [offset=this.length] Zero-based index from where to start 
1107    * searching backwards.
1108    * 
1109    * <p>If the argument is not given, the entire array is searched.
1110    * 
1111    * <p>If the number is negative, then the <var>offset</var> is considered to 
1112    * be an offset from the end of the array. Thus, given a negative index like 
1113    * -3, it would mean that the last <strong>two</strong> elements are 
1114    *  <strong>not</strong> checked.
1115    *
1116    * <p>The method always starts searching from the calculated/given 
1117    * <var>offset</var> decreasing one by one, down to index 0 (backwards 
1118    * search).
1119    *
1120    * @throws {TypeError} If <var>value</var> is undefined.
1121    *
1122    * @returns {Number} The element index where the last occurrence of 
1123    * <var>value</var> was found in the current array. If the value was not 
1124    * found, -1 is returned.
1125    *
1126    * @see $.js.Array.indexOf
1127    */
1128   lastIndexOf : function (value, offset) {
1129     var n = this.length;
1130 
1131     if (n == 0) {
1132       return -1;
1133     } else if (typeof value == 'undefined') {
1134       throw new TypeError('The first argument is undefined.');
1135     }
1136 
1137     if (isNaN(offset)) {
1138       offset = n - 1;
1139     } else {
1140       offset = offset < 0 ? Math.ceil(offset) : Math.floor(offset);
1141       if (offset < 0) {
1142         offset += n;
1143       } else if (offset >= n) {
1144         offset = n - 1;
1145       }
1146     }
1147 
1148     for ( ; offset >= 0; offset--) {
1149       if ((offset in this) && this[offset] === value) {
1150         return offset;
1151       }
1152     }
1153 
1154     return -1;
1155   }
1156 };
1157 
1158 
1159 /**
1160  * Holds the methods used for manipulating strings. Some of them are already 
1161  * available in JavaScript 1.6, natively.
1162  *
1163  * <p>By default, the global <var>String.prototype</var> object is extended to 
1164  * contain all the methods in this namespace, if they do not exists already.  
1165  * Thus, native String methods are not overwritten.
1166  *
1167  * <p>All of these methods use their <var>this</var> object. As such, you need 
1168  * to pass the correct <var>this</var> object.
1169  *
1170  * <p>In the examples provided for each method we will assume that the strings 
1171  * defined already have the methods. Thus code like 
1172  * <code><var>string</var>.trim()</code> can be written directly.
1173  *
1174  * @namespace Holds the methods used for manipulating strings. Some of them are 
1175  * already available in JavaScript 1.6, natively.
1176  */
1177 $.js.String = {
1178   /**
1179    * Trim whitespace from the start and the end of the string.
1180    *
1181    * @example
1182    * <code>var str = " \n test \n  ";
1183    * var str2 = str.trim();</code>
1184    * // Now <code>str2 = 'test'</code>.
1185    *
1186    * @returns {String} The string without any whitespace at the beginning, nor 
1187    * at the end.
1188    *
1189    * @see $.js.String.trimLeft
1190    * @see $.js.String.trimRight
1191    */
1192   trim : function () {
1193     return this.replace(/^\s+|\s+$/g, '');
1194   },
1195 
1196   /**
1197    * Trim whitespace from the start of the string.
1198    *
1199    * @example
1200    * <code>var str = " \n test \n  ";
1201    * var str2 = str.trimLeft();</code>
1202    * // Now <code>str2 = "test \n  "</code>.
1203    *
1204    * @returns {String} The string without any whitespace at the beginning.
1205    *
1206    * @see $.js.String.trim
1207    * @see $.js.String.trimRight
1208    */
1209   trimLeft : function () {
1210     return this.replace(/^\s+/, '');
1211   },
1212 
1213   /**
1214    * Trim whitespace from the end of the string.
1215    *
1216    * @example
1217    * <code>var str = " \n test \n  ";
1218    * var str2 = str.trimRight();</code>
1219    * // Now <code>str2 = " \n test"</code>.
1220    *
1221    * @returns {String} The string without any whitespace at the end.
1222    *
1223    * @see $.js.String.trim
1224    * @see $.js.String.trimLeft
1225    */
1226   trimRight : function () {
1227     return this.replace(/\s+$/, '');
1228   },
1229 
1230   /**
1231    * Convert significant characters into entities. Any XML/HTML code has 
1232    * significant characters, like angle brackets and quotes. This method 
1233    * converts them to HTML entities, such that the resulting string can be used 
1234    * in Web pages without any problems.
1235    *
1236    * <p>The characters being encoded are:
1237    *
1238    * <ul>
1239    *   <li>' & ' to ' &amp; '
1240    *   <li>' < ' to ' &lt; '
1241    *   <li>' > ' to ' &gt; '
1242    *   <li>' " ' to ' &#34; '
1243    *   <li>" ' " to ' &#39; '
1244    * </ul>
1245    *
1246    * @returns {String} The string with the special characters encoded.
1247    *
1248    * @see $.js.String.unescapeXML
1249    */
1250   escapeXML : function () {
1251     return this.replace(/&/g,  '&')
1252       .replace(/</g, '<')
1253       .replace(/>/g,  '>')
1254       .replace(/"/g, '"')
1255       .replace(/'/g, ''');
1256   },
1257 
1258   /**
1259    * Decode significant characters from entities. This is the reverse method of 
1260    * {@link $.js.String.escapeXML}.
1261    *
1262    * @returns {String} The string with the special characters decoded.
1263    *
1264    * @see $.js.String.escapeXML
1265    */
1266   unescapeXML : function () {
1267     return this.replace(/</g, '<')
1268       .replace(/>/g,  '>')
1269       .replace(/"/g, '"')
1270       .replace(/'/g, "'")
1271       .replace(/&/g, '&');
1272   },
1273 
1274   /**
1275    * Strip tags from a string.
1276    *
1277    * <p>Optionally, you may provide the list of tags to be stripped from the 
1278    * string. Each argument is considered a tag name.
1279    *
1280    * <p>If no argument is provided, then all the tags in the string are 
1281    * stripped.
1282    *
1283    * @params {String} [tag1] The name of the tag you want to strip.
1284    * @params {String} [tagN] The name of the tag you want to strip.
1285    *
1286    * @returns {String} The string with tags stripped.
1287    */
1288   stripTags : function () {
1289     var tags = '[a-zA-Z][a-zA-Z\d_]*';
1290     if (arguments.length > 0) {
1291       tags = '';
1292       $.js.Array.forEach.call(arguments, function (val) {
1293         tags += '|' + $.js.String.escapeRegexp.call(val);
1294       });
1295       tags = '(' + tags.substr(1) + ')';
1296     }
1297 
1298     var regex = new RegExp('</?' + tags + '[^>]*>', 'g');
1299 
1300     return this.replace(regex, '');
1301   },
1302 
1303   /**
1304    * Escape special characters in regular expressions.
1305    *
1306    * @returns {String} The string with the special characters escaped.
1307    */
1308   escapeRegexp : function () {
1309     return this.replace(/([$^\/()\\|?+*\[\]{}.-])/g, "\\$1");
1310   }
1311 };
1312 
1313 if (extend_objects) {
1314   Object.extend = $.extend;
1315   $.extend(Object, $.js.Object);
1316   $.extend(Array.prototype, $.js.Array);
1317   $.extend(String.prototype, $.js.String);
1318 }
1319 
1320 })();
1321