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 ' & ' 1240 * <li>' < ' to ' < ' 1241 * <li>' > ' to ' > ' 1242 * <li>' " ' to ' " ' 1243 * <li>" ' " to ' ' ' 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