1 (function () { 2 var testArray = function (o) { 3 return Object.prototype.toString.apply(o) === '[object Array]'; 4 }; 5 6 // Functions that are used in assembling nativelib. 7 8 /** 9 * @namespace Functions for dealing with Objects. 10 */ 11 Obj = { 12 /** 13 * Adds all of the passed properties if they are not already 14 * defined. 15 * 16 * @example 17 * var o = { a: 1 }; 18 * Object.merge(o, { 19 * a: 2, 20 * b: 2 21 * }); 22 * o; // { a: 1, b: 2 } 23 * 24 * @name merge 25 * @methodOf Obj 26 * @param {Object} o The object to modify (in-place). 27 * @param {Object} props The properties to write. 28 * @param {Boolean} clobber true to overwrite defined properties 29 * @return {Object} The modified object. 30 */ 31 merge: function (o, props, clobber) { 32 for (var p in props) { 33 if (props.hasOwnProperty(p) && (!o[p] || clobber)) { 34 o[p] = props[p]; 35 } 36 } 37 return o; 38 } 39 }; 40 41 Obj.merge(Obj, { 42 /** 43 * Maps each property to its value. 44 * 45 * @example 46 * var o = { a: 1 }; 47 * Object.alias(o, { 48 * a: 'b' 49 * }; // { a: 1, b: 1 } 50 * Object.alias(o, { 51 * b: ['c', 'd'] 52 * }; // { a: 1, b: 1, c: 1, d: 1 } 53 * 54 * @name alias 55 * @methodOf Obj 56 * @param {Object} o The object to modify (in-place). 57 * @param {Object} map The properties to map. 58 * @return {Object} The modified object. 59 */ 60 alias: function (o, map) { 61 for (var p in map) { 62 if (map.hasOwnProperty(p) && !o[map[p]]) { 63 if (testArray(map[p])) { 64 for (var i = 0, len = map[p].length; i < len; i++) { 65 o[map[p][i]] = o[p]; 66 } 67 } else { 68 o[map[p]] = o[p]; 69 } 70 } 71 } 72 return o; 73 }, 74 75 /** 76 * Copies each property of the provided object into a new object, 77 * creating a shallow copy. 78 * 79 * @name copy 80 * @methodOf Obj 81 * @param {Object} o The object to copy. 82 * @return {Object} The copied object. 83 */ 84 copy: function (o) { 85 return Obj.merge({}, o); 86 }, 87 88 /** 89 * Creates a new object from an array of property-value pairs. 90 * 91 * @example 92 * Object.create([['a', 1], ['b', 2]]); // { a: 1, b: 2 } 93 * 94 * @name create 95 * @methodOf Obj 96 * @param {Array} pairs The pair array. 97 * @return {Object} The created object. 98 */ 99 create: function (pairs) { 100 var o = {}; 101 for (var i = 0; i < pairs.length; i++) { 102 o[pairs[i][0]] = pairs[i][1]; 103 } 104 return o; 105 }, 106 107 /** 108 * Compares the value of each property in each object. 109 * 110 * @name equals 111 * @methodOf Obj 112 * @param {Object} o One object to compare. 113 * @param {Object} o2 The object to compare it with. 114 * @return {Boolean} true if the properties are equal, false if not. 115 */ 116 equals: function (o, o2) { 117 var p; 118 for (p in o2) { 119 if (o2.hasOwnProperty(p) && !Obj.valuesEqual(o[p], o2[p])) { 120 return false; 121 } 122 } 123 for (p in o) { 124 if (o.hasOwnProperty(p) && !Obj.valuesEqual(o[p], o2[p])) { 125 return false; 126 } 127 } 128 return true; 129 }, 130 131 /** 132 * Creates a new object containing only the properties that return 133 * true when passed to the provided function. 134 * 135 * @name filter 136 * @methodOf Obj 137 * @param {Object} o The object to filter. 138 * @param {Function} fn The filtering function. 139 * @param {Object} ctx The function's execution context. 140 * @return {Object} The filtered object. 141 */ 142 filter: function (o, fn, ctx) { 143 var filtered = {}; 144 for (var p in o) { 145 if (o.hasOwnProperty(p) && fn.call(ctx || o, o[p], p, o)) { 146 filtered[p] = o[p]; 147 } 148 } 149 return filtered; 150 }, 151 152 /** 153 * The same as filter, but only returning the first match. 154 * 155 * @name filterOne 156 * @methodOf Obj 157 * @param {Object} o The object to filter. 158 * @param {Function} fn The filtering function. 159 * @param {Object} ctx The function's execution context. 160 * @return The first filtered item, if found. 161 */ 162 filterOne: function (o, fn, ctx) { 163 var filtered = {}; 164 for (var p in o) { 165 if (o.hasOwnProperty(p) && fn.call(ctx || o, o[p], p, o)) { 166 filtered[p] = o[p]; 167 return filtered; 168 } 169 } 170 }, 171 172 /** 173 * Returns an object matching the specified path, if it exists. 174 * 175 * @name fromPath 176 * @methodOf Obj 177 * @param {String} path The period '.' separated object path. 178 * @param {Object} root The root path (default is window). 179 * @return {Object} The object, if found. 180 */ 181 fromPath: function (path, root) { 182 return path.split('.').reduce(function (a, b) { 183 return a ? a[b] : undefined; 184 }, root || window); 185 }, 186 187 /** 188 * Calls the property, if present, on each property in the passed 189 * object, returning an object with the results at each property. If 190 * the named property is not present on a given item, the result 191 * property will be undefined. 192 * 193 * @name invoke 194 * @methodOf Obj 195 * @param {Object} o The object 196 * @param {String} name The property name to call. 197 * @param {Array} args An argument list that will be provided with 198 * each call. 199 * @param {Object} ctx The function's execution context. 200 * @return {Object} The return value of each call. 201 */ 202 invoke: function (o, name, args, ctx) { 203 var invoked = {}; 204 for (var p in o) { 205 if (o.hasOwnProperty(p)) { 206 invoked[p] = (name in o[p] ? 207 o[p][name].apply(ctx || o[p], args || []) : undefined); 208 } 209 } 210 return invoked; 211 }, 212 213 /** 214 * Creates an array containing the property names of an object. 215 * 216 * @name keys 217 * @methodOf Obj 218 * @param {Object} o The object 219 * @return {Array} The object's keys 220 */ 221 keys: function (o) { 222 var keys = []; 223 for (var p in o) { 224 if (o.hasOwnProperty(p)) { 225 keys.push(p); 226 } 227 } 228 return keys; 229 }, 230 231 /** 232 * Runs the passed function for each property in the object. 233 * 234 * @name forEach 235 * @methodOf Obj 236 * @param {Object} o The object to iterate. 237 * @param {Function} fn The function to call. 238 * @param {Object} ctx The function's execution context. 239 */ 240 forEach: function (o, fn, ctx) { 241 for (var p in o) { 242 if (o.hasOwnProperty(p)) { 243 fn.call(ctx || o, o[p], p, o); 244 } 245 } 246 }, 247 248 /** 249 * Creates an array containing the values of a function applied to 250 * each property in the object. 251 * 252 * @example 253 * Object.map({ Rice: 'Jerry', Clark: 'Gary' }, function (v, p, o) { 254 * return p + ', ' + v; 255 * }; // { Rice: 'Rice, Jerry', Clark: 'Clark, Gary' } 256 * 257 * @name map 258 * @methodOf Obj 259 * @param {Object} o The object to iterate. 260 * @param {Function} fn The map function. 261 * @param {Object} ctx The function's execution context, called with (value, 262 * property, object). 263 * @return {Object} The mapped object. 264 */ 265 map: function (o, fn, ctx) { 266 var mapped = {}; 267 for (var p in o) { 268 if (o.hasOwnProperty(p)) { 269 mapped[p] = fn.call(ctx || o, o[p], o, o); 270 } 271 } 272 return mapped; 273 }, 274 275 /** 276 * Creates a nested object to the depth specified, not overwriting 277 * existing properties. 278 * 279 * @name namespace 280 * @methodOf Obj 281 * @param {String} path The period '.' separated object path. 282 * @return {Object} The resulting object. 283 */ 284 namespace: function (path) { 285 return path.split('.').reduce(function (o, part) { 286 return o[part] || (o[part] = {}); 287 }, window); 288 }, 289 290 /** 291 * Checks if the passed item is an object. 292 * 293 * @name test 294 * @methodOf Obj 295 * @param {Object} o The item to check. 296 * @return {Boolean} true if it is an object, false if not. 297 */ 298 test: function (o) { 299 return Object.prototype.toString.call(o) === '[object Object]'; 300 }, 301 302 /** 303 * Returns each property's value. 304 * 305 * @name values 306 * @methodOf Obj 307 * @param {Object} o The object to use. 308 * @return {Array} An array of values. 309 */ 310 values: function (o) { 311 var vals = []; 312 for (var p in o) { 313 if (o.hasOwnProperty(p)) { 314 vals.push(o[p]); 315 } 316 } 317 return vals; 318 }, 319 320 /** 321 * Checks if the two passed objects are equal, performing deep 322 * checks for Objects and Arrays. 323 * 324 * @name valuesEqual 325 * @methodOf Obj 326 * @param {Object} a The item to check. 327 * @param {Object} b The item to check it against. 328 * @return {Boolean} true if the values are equal, false if not. 329 */ 330 valuesEqual: function (a, b) { 331 if (testArray(a)) { 332 return testArray(b) && a.equals(b); 333 } else if (Obj.test(a)) { 334 return Obj.test(b) && Obj.equals(a, b); 335 } else { 336 return a === b; 337 } 338 } 339 }); 340 341 Obj.alias(Obj, { 342 /** 343 * Alias of filter. 344 * 345 * @name select 346 * @methodOf Obj 347 */ 348 filter: 'select', 349 350 /** 351 * Alias of filterOne. 352 * 353 * @name detect 354 * @methodOf Obj 355 */ 356 filterOne: 'detect', 357 358 /** 359 * Alias of forEach. 360 * 361 * @name each 362 * @methodOf Obj 363 */ 364 forEach: 'each', 365 366 /** 367 * Alias of namespace 368 * 369 * @name ns 370 * @methodOf Obj 371 */ 372 namespace: 'ns' 373 }); 374 375 Obj.merge(Array.prototype, { 376 /** 377 * Checks if all items in this array pass the provided test. 378 * 379 * @name all 380 * @methodOf Array# 381 * @param {Function} fn The filtering function. 382 * @param {Object} ctx The function's execution context. 383 * @return {Boolean} If each item passes. 384 */ 385 all: function (fn, ctx) { 386 for (var i = 0, len = this.length; i < len; i++) { 387 if (!fn.call(ctx || this, this[i], i, this)) { 388 return false; 389 } 390 } 391 return true; 392 }, 393 394 /** 395 * Creates a copy of this array. 396 * 397 * @name copy 398 * @methodOf Array# 399 * @return {Array} The copy. 400 */ 401 copy: function () { 402 return this.slice(0); 403 }, 404 405 /** 406 * Checks that the passed array equals this one at each index. 407 * 408 * @name equals 409 * @methodOf Array# 410 * @param {Array} a The array to check 411 * @return {Boolean} true if equal, false if not 412 */ 413 equals: function (a) { 414 if (a.length !== this.length) { 415 return false; 416 } 417 for (var i = 0, len = a.length; i < len; i++) { 418 if (!Obj.valuesEqual(this[i], a[i])) { 419 return false; 420 } 421 } 422 return true; 423 }, 424 425 /** 426 * Creates an array containing only the elements in this one for 427 * which the filtering function returns true. 428 * 429 * @name filter 430 * @methodOf Array# 431 * @param {Function} fn The filtering function. 432 * @param {Object} ctx The function's execution context. 433 * @return {Array} The filtered array. 434 */ 435 filter: function (fn, ctx) { 436 var filtered = []; 437 for (var i = 0, len = this.length; i < len; i++) { 438 if (fn.call(ctx || this, this[i], i, this)) { 439 filtered.push(this[i]); 440 } 441 } 442 return filtered; 443 }, 444 445 /** 446 * Creates a flattened array, merging down all sub-arrays. 447 * 448 * @name flatten 449 * @methodOf Array# 450 * @return {Array} The flattened array. 451 */ 452 flatten: function () { 453 var flattened = []; 454 for (var i = 0, len = this.length; i < len; i++) { 455 if (testArray(this[i])) { 456 flattened = flattened.concat(this[i].flatten()); 457 } else { 458 flattened.push(this[i]); 459 } 460 } 461 return flattened; 462 }, 463 464 /** 465 * The same as filter, but only returning the first match. 466 * 467 * @name filterOne 468 * @methodOf Array# 469 * @param {Function} fn The filtering function. 470 * @param {Object} ctx The function's execution context. 471 * @return {Object} The first filtered item, if found. 472 */ 473 filterOne: function (fn, ctx) { 474 for (var i = 0, len = this.length; i < len; i++) { 475 if (fn.call(ctx || this, this[i], i, this)) { 476 return this[i]; 477 } 478 } 479 }, 480 481 /** 482 * Gets the array's first item. 483 * 484 * @name first 485 * @methodOf Array# 486 * @return {Object} The first item. 487 */ 488 first: function () { 489 return this[0]; 490 }, 491 492 /** 493 * Gets a propertie's value for each object in the array. 494 * 495 * @name eachProperty 496 * @methodOf Array# 497 * @param {String} p The property value to retrieve. 498 * @param {Function} fn true if the property is a function and should be 499 * called. 500 * @param {Object} ctx The function's execution context. 501 * @return {Array} Each property's value. 502 */ 503 eachProperty: function (p, fn, ctx) { 504 var vals = []; 505 for (var i = 0, len = this.length; i < len; i++) { 506 vals.push(fn ? this[i][p].apply(ctx || this[i][p]) : this[i][p]); 507 } 508 return vals; 509 }, 510 511 /** 512 * Runs the provided function for each item in this array. 513 * 514 * @name forEach 515 * @methodOf Array# 516 * @param {Function} fn The function to run. 517 * @param {Object} ctx The function's execution context. 518 */ 519 forEach: function (fn, ctx) { 520 for (var i = 0, len = this.length; i < len; i++) { 521 fn.call(ctx || this, this[i], i, this); 522 } 523 }, 524 525 /** 526 * Checks if the array includes an item identical to the one passed. 527 * 528 * @name includes 529 * @methodOf Array# 530 * @param {Object} item The item to check. 531 * @return {Boolean} true if the item was found, false if not. 532 */ 533 includes: function (item) { 534 for (var i = 0, len = this.length; i < len; i++) { 535 if (this[i] === item) { 536 return true; 537 } 538 } 539 return false; 540 }, 541 542 /** 543 * Gets all but the last item of the array. 544 * 545 * @name init 546 * @methodOf Array# 547 * @return {Array} The array's init. 548 */ 549 init: function () { 550 return this.slice(0, this.length - 1); 551 }, 552 553 /** 554 * Calls the property, if present, on each item in this array, 555 * returning a list of results. If the named property is not present 556 * on a given item, undefined will pushed on the result list. 557 * 558 * @name invoke 559 * @methodOf Array# 560 * @param {String} name The property name to call. 561 * @param {Array} args An argument list that will be provided with 562 * each call. 563 * @param {Object} ctx The function's execution context. 564 * @return {Array} The return value of each call. 565 */ 566 invoke: function (name, args, ctx) { 567 var results = []; 568 for (var i = 0, len = this.length; i < len; i++) { 569 results.push(name in this[i] ? 570 this[i][name].apply(ctx || this[i], args || []) : undefined); 571 } 572 return results; 573 }, 574 575 /** 576 * Gets the last item of the array. 577 * 578 * @name last 579 * @methodOf Array# 580 * @return {Object} The array's last item. 581 */ 582 last: function () { 583 return this[this.length - 1]; 584 }, 585 586 /** 587 * Creates an array containing the values of a function applied to 588 * each item in the array. 589 * 590 * @example 591 * [1, 2, 3].map(function (n, i, a) { 592 * return n * i; 593 * }; // [0, 2, 6] 594 * 595 * @name map 596 * @methodOf Array# 597 * @param {Function} fn The map function 598 * @param {Object} ctx The function's execution context, called with (value, 599 * index, array) 600 * @return {Array} The mapped array 601 */ 602 map: function (fn, ctx) { 603 var mapped = []; 604 for (var i = 0, len = this.length; i < len; i++) { 605 mapped.push(fn.call(ctx || this, this[i], i, this)); 606 } 607 return mapped; 608 }, 609 610 /** 611 * Splits this array in two based on the return value of the 612 * partitioning function. The first array contains all positives and 613 * the second all negatives. 614 * 615 * @name partition 616 * @methodOf Array# 617 * @param {Function} fn The partition function. 618 * @param {Object} ctx The function's execution context. 619 * @return {Array} The array of paritions. 620 */ 621 partition: function (fn, ctx) { 622 var parts = [[], []]; 623 for (var i = 0, len = this.length; i < len; i++) { 624 parts[fn.call(ctx || this, this[i], i, this) ? 0 : 1].push(this[i]); 625 } 626 return parts; 627 }, 628 629 /** 630 * Processes the items of an array from left to right, applying the 631 * reducing function to each item. 632 * 633 * @name reduce 634 * @methodOf Array# 635 * @param {Function} fn The function that is called with each iteration. This 636 * function is provided with the following arguments: 637 * - The result of the previous iteration 638 * - The current item 639 * - The current item's index 640 * - The array 641 * @param {Object} init A value to be used as the previous value in the first 642 * iteration. 643 * @param {Object} ctx The context in which to apply the function. 644 * @return {Object} The reduced value. 645 */ 646 reduce: function (fn, init, ctx) { 647 var i = 0, len = this.length, prev = init || this[i++]; 648 while (i < len) { 649 prev = fn.apply(ctx || this, [prev, this[i], i++, this]); 650 } 651 return prev; 652 }, 653 654 /** 655 * Removes all items matching the passed object. 656 * 657 * @name remove 658 * @methodOf Array# 659 * @param {Object} o The object to remove. 660 * @return {Boolean} true if the object was removed, false if not. 661 */ 662 remove: function (o) { 663 var removed = false; 664 for (var i = 0, len = this.length; i < len; i++) { 665 if (this[i] === o) { 666 removed = true; 667 this.splice(i--, 1); 668 } 669 } 670 return removed; 671 }, 672 673 /** 674 * Checks if any item in this array passes the provided test. 675 * 676 * @name some 677 * @methodOf Array# 678 * @param {Function} fn The filtering function. 679 * @param {Object} ctx The function's execution context. 680 * @return {Boolean} If any item passes. 681 */ 682 some: function (fn, ctx) { 683 for (var i = 0, len = this.length; i < len; i++) { 684 if (fn.call(ctx || this, this[i], i, this)) { 685 return true; 686 } 687 } 688 return false; 689 }, 690 691 /** 692 * Returns an array containing all items not identical to those in the 693 * passed array. 694 * 695 * @name subtract 696 * @methodOf Array# 697 * @param {Array} a The array of of objects to filter. 698 * @param {Boolean} v true to check value rather than identity. 699 * @return {Array} The array with none of the passed objects. 700 */ 701 subtract: function (a, v) { 702 var subtracted = []; 703 704 if (v) { 705 for (var i = 0, len = this.length; i < len; i++) { 706 for (var j = 0, len2 = a.length; j < len2; j++) { 707 if (this[i] != a[j]) { 708 subtracted.push(this[i]); 709 } 710 } 711 } 712 } else { 713 for (var i = 0, len = this.length; i < len; i++) { 714 for (var j = 0, len2 = a.length; j < len2; j++) { 715 if (this[i] !== a[j]) { 716 subtracted.push(this[i]); 717 } 718 } 719 } 720 } 721 722 return subtracted; 723 }, 724 725 /** 726 * Returns an array containing only items with unique identity. 727 * 728 * @name unique 729 * @methodOf Array# 730 * @return {Array} The array with no duplicate objects. 731 */ 732 unique: function () { 733 var uniq = []; 734 for (var i = 0, len = this.length; i < len; i++) { 735 if (!uniq.includes(this[i])) { 736 uniq.push(this[i]); 737 } 738 } 739 return uniq; 740 }, 741 742 /** 743 * Creates an array containing arrays of each item at each index. 744 * 745 * @example 746 * [1, 2, 3].zip([4, 5, 6]); // [[1, 4], [2, 5], [3, 6]] 747 * 748 * @name zip 749 * @methodOf Array# 750 * @param {Array} arr The array to zip. 751 * @return {Array} The zipped array. 752 */ 753 zip: function (arr) { 754 var zipped = []; 755 var len = this.length > arr.length ? this.length : arr.length; 756 for (var i = 0; i < len; i++) { 757 zipped.push([this[i], arr[i]]); 758 } 759 return zipped; 760 } 761 }); 762 763 Obj.merge(Array, { 764 /** 765 * Creates an array from an array-like object. 766 * 767 * @name create 768 * @methodOf Array 769 * @param {Object} o The source object 770 * @return {Array} The new array 771 */ 772 create: function (o) { 773 var arr; 774 if (testArray(o)) { 775 arr = o; 776 } else if (o.toArray) { 777 arr = o.toArray(); 778 } else { 779 arr = []; 780 for (var i = 0, len = o.length || 0; i < len; i++) { 781 arr.push(o[i]); 782 } 783 } 784 return arr; 785 }, 786 787 /** 788 * Checks if the object is an array. 789 * 790 * @name test 791 * @methodOf Array 792 * @param {Object} o The object to check. 793 * @return {Boolean} true if it is an array, false if not. 794 */ 795 test: testArray 796 }); 797 798 Obj.alias(Array.prototype, { 799 /** 800 * Alias of eachProperty. 801 * 802 * @name pluck 803 * @methodOf Array# 804 */ 805 eachProperty: 'pluck', 806 807 /** 808 * Alias of filter. 809 * 810 * @name select 811 * @methodOf Array# 812 */ 813 filter: 'select', 814 815 /** 816 * Alias of filterOne. 817 * 818 * @name detect 819 * @methodOf Array# 820 */ 821 filterOne: 'detect', 822 823 /** 824 * Alias of forEach. 825 * 826 * @name each 827 * @methodOf Array# 828 */ 829 forEach: 'each' 830 }); 831 832 Obj.merge(Function.prototype, { 833 /** 834 * Binds an execution context and an optional argument list to this 835 * function. 836 * 837 * @name bind 838 * @methodOf Function# 839 * @param {Object} ctx The bound execution context. 840 * @param {Array} args The bound arguments array. 841 * @return {Function} The bound function. 842 */ 843 bind: function (ctx, args) { 844 var fn = this; 845 return function () { 846 return fn.apply(ctx, args || []); 847 }; 848 }, 849 850 /** 851 * Bind a number of arguments to this function, not specifying 852 * execution context. 853 * 854 * @name curry 855 * @methodOf Function# 856 * @param {Arguments} list An argument list to bind. 857 * @return {Function} The bound function. 858 */ 859 curry: function () { 860 var fn = this, args = Array.create(arguments); 861 return function () { 862 return fn.apply(this, args.concat(Array.create(arguments))); 863 }; 864 }, 865 866 /** 867 * Calls the function after the specified timeout. 868 * 869 * @name delay 870 * @methodOf Function# 871 * @param {Number} ms The number of milliseconds to delay. 872 * @param {Object} ctx The function's execution context. 873 * @param {Array} args An arguments array to pass to the function. 874 * @return {Number} The timeout's ID. 875 */ 876 delay: function (ms, ctx, args) { 877 var fn = this; 878 return setTimeout(function () { 879 fn.apply(ctx || this, args || []); 880 }, ms); 881 }, 882 883 repeat: function (n, ctx) { 884 var fn = this; 885 return function (x) { 886 return n.range(1).reduce(function (p) { 887 return fn.call(ctx || this, p); 888 }, x); 889 } 890 } 891 }); 892 893 Obj.merge(Function, { 894 /** 895 * Returns an empty (typically placeholder) function. 896 * 897 * @name empty 898 * @methodOf Function 899 * @return {Function} The empty function. 900 */ 901 empty: function () { 902 return function () {}; 903 }, 904 905 /** 906 * Checks if the provided object is a Function. 907 * 908 * @name test 909 * @methodOf Function 910 * @param {Object} o The object to check. 911 * @return {Boolean} true if it is a Function, false if not. 912 */ 913 test: function (o) { 914 return Object.prototype.toString.apply(o) === '[object Function]'; 915 } 916 }); 917 918 Obj.alias(Function.prototype, { 919 /** 920 * Alias of curry. 921 * 922 * @name partial 923 * @methodOf Function# 924 */ 925 curry: 'partial' 926 }); 927 928 Obj.merge(Math, { 929 /** 930 * Calculates the average (mean) of a list of numbers. 931 * 932 * @name average 933 * @methodOf Math 934 * @param {Array/Arguments} list An array or argument list of numbers. 935 * @return {Number} Their average. 936 */ 937 average: function (list) { 938 var nums; 939 if (testArray(list)) { 940 nums = list; 941 } else { 942 nums = Array.create(arguments); 943 } 944 return Math.sum(nums) / nums.length; 945 }, 946 947 /** 948 * Creates a range between two numbers. 949 * 950 * @name range 951 * @methodOf Math 952 * @param {Number} a The start of the range. 953 * @param {Number} b The end of the range. 954 * @return {Array} The range. 955 */ 956 range: function (a, b) { 957 // Switch a and b so that the second is greater 958 if (a > b) { var _b = b; b = a; a = _b; } 959 var range = []; 960 while (a <= b && range.push(a++)) {} 961 return range; 962 }, 963 964 /** 965 * Calculates the sum of a list of numbers. 966 * 967 * @name sum 968 * @methodOf Math 969 * @param {Array/Arguments} list An array or argument list of numbers. 970 * @return {Number} Their sum. 971 */ 972 sum: function (list) { 973 var nums; 974 if (testArray(list)) { 975 nums = list; 976 } else { 977 nums = Array.create(arguments); 978 } 979 return nums.reduce(function (a, b) { 980 return a + b; 981 }); 982 } 983 }); 984 985 Obj.alias(Math, { 986 /** 987 * Alias of average. 988 * 989 * @name mean 990 * @methodOf Math 991 */ 992 average: 'mean' 993 }); 994 995 Obj.merge(Number.prototype, { 996 /** 997 * Calls the passed function 'n' times, where 'n' is this Number. 998 * 999 * @name times 1000 * @methodOf Number# 1001 * @param {Function} fn The function to call. 1002 * @param {Object} ctx The execution context for the function. 1003 */ 1004 times: function (fn, ctx) { 1005 for (var i = 1; i <= this; i++) { 1006 fn.call(ctx || this, i); 1007 } 1008 } 1009 }); 1010 1011 // Make all Math methods callable from a Number 1012 Obj.merge(Number.prototype, Obj.create([ 1013 'abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 1014 'log', 'max', 'min', 'pow', 'random', 'round', 'sin', 'sqrt', 'tan' 1015 ].concat(Obj.keys(Math)).map(function (fn) { 1016 return [fn, function () { 1017 return Math[fn].apply(this, [this].concat(Array.prototype.slice.apply(arguments))); 1018 }]; 1019 }))); 1020 1021 Obj.merge(Number, { 1022 /** 1023 * Checks if the passed object is a Number. 1024 * 1025 * @name test 1026 * @methodOf Number 1027 * @param {Object} o The object to check. 1028 * @return {Boolean} true if it is a number, false if not. 1029 */ 1030 test: function (o) { 1031 return typeof o === 'number'; 1032 } 1033 }); 1034 1035 Obj.alias(Number.prototype, { 1036 /** 1037 * Alias of range. 1038 * 1039 * @name to 1040 * @methodOf Number# 1041 */ 1042 range: 'to' 1043 }); 1044 1045 Obj.merge(String.prototype, { 1046 /** 1047 * Capitalizes the string's first letter. 1048 * 1049 * @name capitalize 1050 * @methodOf String# 1051 * @return {String} The capitalized string. 1052 */ 1053 capitalize: function () { 1054 return this.substr(0, 1).toUpperCase() + this.slice(1); 1055 }, 1056 1057 /** 1058 * Checks if this string ends with the passed string. 1059 * 1060 * @name endsWith 1061 * @methodOf String# 1062 * @param {String} str The string to check. 1063 * @return {Boolean} true if it matches, false if not. 1064 */ 1065 endsWith: function (str) { 1066 return this.slice(this.length - str.length) === str; 1067 }, 1068 1069 /** 1070 * Checks if this string has non-whitespace characters. 1071 * 1072 * @name empty 1073 * @methodOf String# 1074 * @return {Boolean} true if empty, false, if not. 1075 */ 1076 empty: function () { 1077 return this.strip().length < 1; 1078 }, 1079 1080 /** 1081 * Creates a formatted string. 1082 * 1083 * @example 1084 * '{0}-{1}-{2}'.format('a', 'b', 'c'); // 'a-b-c' 1085 * '{1}-{1}-{3}'.format('a', 'b', 'c', 'd'); // 'b-b-d' 1086 * 1087 * @name format 1088 * @methodOf String# 1089 * @param {Arguments} list An argument list of ordered replacements. 1090 * @return {String} The formatted string. 1091 */ 1092 format: function () { 1093 var replacements = arguments; 1094 return this.replace(/\{(\d+)\}/g, function (match, group) { 1095 return replacements[Number(group)]; 1096 }); 1097 }, 1098 1099 /** 1100 * Concatenates this string n times. 1101 * 1102 * @name mult 1103 * @methodOf String# 1104 * @param {Number} n The number of times to concatenate the string. 1105 * @return {String} The multipled string. 1106 */ 1107 mult: function (n) { 1108 var str = ''; 1109 for (var i = 0; i < n; i++) { 1110 str += this; 1111 } 1112 return str; 1113 }, 1114 1115 /** 1116 * sprintf implementation: works for basic replacement but 1117 * precision, width, and flags are not implemented fully. 1118 * 1119 * @name sprintf 1120 * @methodOf String# 1121 * @param {Arguments} An argument list of ordered replacements. 1122 * @return {String} The formatted string. 1123 */ 1124 sprintf: function () { 1125 var args = Array.prototype.slice.call(arguments); 1126 1127 return this.replace(String.sprintfRegex, function (match, 1128 mainGroup, flag, width, precisionMatch, precision, 1129 specifier, chars) { 1130 // Special cases that use no argument 1131 if (specifier === '%') { 1132 return '%'; 1133 } 1134 if (specifier === 'n') { 1135 return chars; 1136 } 1137 if (width === '*') { 1138 width = args.shift(); 1139 } 1140 if (precisionMatch && precision === '') { 1141 precision = 0; 1142 } else if (precision === '*') { 1143 precision = args.shift(); 1144 } 1145 1146 var replace = args.shift(); 1147 1148 switch (specifier) { 1149 case 'c': 1150 case 's': 1151 replace = String(replace); 1152 break; 1153 1154 case 'd': 1155 case 'i': 1156 case 'u': 1157 replace = Number(replace).floor(); 1158 break; 1159 1160 case 'e': 1161 replace = Number(replace).toExponential(); 1162 break; 1163 1164 case 'E': 1165 replace = Number(replace).toExponential().toUpperCase(); 1166 break; 1167 1168 case 'f': 1169 replace = Number(replace); 1170 break; 1171 1172 case 'g': 1173 if (Number(replace).toExponential().length <= 1174 String(Number(replace)).length) { 1175 replace = Number(replace).toExponential(); 1176 } else { 1177 replace = Number(replace); 1178 } 1179 break; 1180 1181 case 'G': 1182 if (Number(replace).toExponential().length <= 1183 String(Number(replace)).length) { 1184 replace = Number(replace).toExponential().toUpperCase(); 1185 } else { 1186 replace = Number(replace); 1187 } 1188 break; 1189 1190 case 'o': 1191 replace = Number(replace).toString(8); 1192 break; 1193 1194 case 'x': 1195 replace = Number(replace).toString(16); 1196 break; 1197 1198 case 'X': 1199 replace = Number(replace).toString(16).toUpperCase(); 1200 break; 1201 1202 default: 1203 break; 1204 } 1205 1206 return replace; 1207 } 1208 ); 1209 }, 1210 1211 /** 1212 * Checks if this string starts with the passed string. 1213 * 1214 * @name startsWith 1215 * @methodOf String# 1216 * @param {String} str The string to check. 1217 * @return {Boolean} true if it matches, false if not. 1218 */ 1219 startsWith: function (str) { 1220 return this.slice(0, str.length) === str; 1221 }, 1222 1223 /** 1224 * Strips all whitespace characters from each side of the string. 1225 * 1226 * @name strip 1227 * @methodOf String# 1228 * @return {String} The stripped string. 1229 */ 1230 strip: function () { 1231 return this.replace(/^\s+/g, '').replace(/\s+$/, ''); 1232 }, 1233 1234 /** 1235 * Splits the string into words. 1236 * 1237 * @name words 1238 * @methodOf String# 1239 * @return {Array} An array of words. 1240 */ 1241 words: function () { 1242 return this.split(/\s+/); 1243 } 1244 }); 1245 1246 Obj.merge(String, { 1247 sprintfRegex: new RegExp( 1248 '%' + // % Prefix 1249 '(' + 1250 '([-+ #0])?' + // Flags 1251 '(\\d+|\\*)?' + // Width 1252 '(\\.(\\d+|\\*))?' + // Precision 1253 '([cdieEfgGosuxXpn%])' + // Specifier 1254 ')', 1255 'g'), 1256 1257 /** 1258 * Checks if the provided object is a string. 1259 * 1260 * @name test 1261 * @methodOf String 1262 * @param {Object} o The object to check. 1263 * @return {Boolean} true if it is a string, false if not. 1264 */ 1265 test: function (o) { 1266 return typeof o === 'string'; 1267 } 1268 }); 1269 1270 Obj.alias(String.prototype, { 1271 /** 1272 * Alias of mult. 1273 * 1274 * @name x 1275 * @methodOf String# 1276 */ 1277 mult: 'x' 1278 }); 1279 1280 })(); 1281