1 // ========================================================================== 2 // Project: SproutCore Costello - Property Observing Library 3 // Copyright: ©2006-2011 Strobe Inc. and contributors. 4 // Portions ©2008-2011 Apple Inc. All rights reserved. 5 // License: Licensed under MIT license (see license.js) 6 // ========================================================================== 7 /*global require, console */ 8 9 // These commands are used by the build tools to control load order. On the 10 // client side these are a no-op. 11 if (!window.require) { window.require = function require() {}; } 12 if (!window.sc_require) { window.sc_require = require; } 13 if (!window.sc_resource) { window.sc_resource = function sc_resource() {}; } 14 sc_require('license'); 15 16 // ........................................ 17 // GLOBAL CONSTANTS 18 // 19 // Most global constants should be defined inside of the SC namespace. 20 // However the following two are useful enough and generally benign enough 21 // to put into the global object. 22 window.YES = true; 23 window.NO = false; 24 25 // prevent a console.log from blowing things up if we are on a browser that 26 // does not support it 27 if (typeof console === 'undefined') { 28 window.console = {}; 29 console.log = console.info = console.warn = console.error = function () {}; 30 } 31 32 // prevent console.trace from blowing things up - some browsers like IE10 has 33 // a console object but no console.trace 34 if (!console.trace) { 35 console.trace = function() {}; 36 } 37 38 window.SC = window.SC || {}; 39 window.SproutCore = window.SproutCore || SC; 40 41 // ........................................ 42 // BOOTSTRAP 43 // 44 // The root namespace and some common utility methods are defined here. The 45 // rest of the methods go into the mixin defined below. 46 47 /** 48 @version 1.11.0 49 @namespace 50 51 All SproutCore methods and functions are defined 52 inside of this namespace. You generally should not add new properties to 53 this namespace as it may be overwritten by future versions of SproutCore. 54 55 You can also use the shorthand "SC" instead of "SproutCore". 56 57 SproutCore-runtime is a framework that provides core functions for SproutCore 58 including cross-platform functions, support for property observing and 59 objects. It's focus is on small size and performance. You can use this 60 in place of or along-side other cross-platform libraries such as jQuery or 61 Prototype. 62 */ 63 SC = window.SC; // This is dumb but necessary for jsdoc to get it right 64 65 SC.VERSION = '1.11.0'; 66 67 /** 68 @private 69 70 Adds properties to a target object. You must specify whether 71 to overwrite a value for a property or not. 72 73 Used as a base function for the wrapper functions SC.mixin and SC.supplement. 74 75 @param {Boolean} overwrite if a target has a value for a property, this specifies 76 whether or not to overwrite that value with the copyied object's 77 property value. 78 @param {Object} target the target object to extend 79 @param {Object} properties one or more objects with properties to copy. 80 @returns {Object} the target object. 81 @static 82 */ 83 SC._baseMixin = function (override, args) { 84 // Copy reference to target object 85 var target = args[0] || {}, 86 idx = 1, 87 length = args.length, 88 options, copy, key; 89 90 // Handle case where we have only one item...extend SC 91 if (length === 1) { 92 target = this || {}; 93 idx = 0; 94 } 95 96 for (; idx < length; idx++) { 97 if (!(options = args[idx])) continue; 98 for (key in options) { 99 if (!options.hasOwnProperty(key)) continue; 100 copy = options[key]; 101 if (target === copy) continue; // prevent never-ending loop 102 if (copy !== undefined && (override || (target[key] === undefined))) target[key] = copy; 103 } 104 // Manually copy toString() because some JS engines do not enumerate it 105 // (such as IE8) 106 if (options.hasOwnProperty('toString')) target.toString = options.toString; 107 } 108 109 return target; 110 }; 111 112 /** 113 Adds properties to a target object. 114 115 Takes the root object and adds the attributes for any additional 116 arguments passed. 117 118 @param {Object} target the target object to extend 119 @param {Object} properties one or more objects with properties to copy. 120 @returns {Object} the target object. 121 @static 122 */ 123 SC.mixin = function () { 124 // Accessing `arguments.length` is just a Number and doesn't materialize the `arguments` object, which is costly. 125 // TODO: Add macro to build tools for this. 126 var args = new Array(arguments.length); // Array.prototype.slice.call(arguments) 127 for (var i = 0, len = args.length; i < len; i++) { args[i] = arguments[i]; } 128 129 return SC._baseMixin(true, args); 130 }; 131 132 /** 133 Adds properties to a target object. Unlike SC.mixin, however, if the target 134 already has a value for a property, it will not be overwritten. 135 136 Takes the root object and adds the attributes for any additional 137 arguments passed. 138 139 @param {Object} target the target object to extend 140 @param {Object} properties one or more objects with properties to copy. 141 @returns {Object} the target object. 142 @static 143 */ 144 SC.supplement = function () { 145 // Accessing `arguments.length` is just a Number and doesn't materialize the `arguments` object which is costly. 146 // TODO: Add macro to build tools for this. 147 var args = new Array(arguments.length); // Array.prototype.slice.call(arguments) 148 for (var i = 0, len = args.length; i < len; i++) { args[i] = arguments[i]; } 149 150 return SC._baseMixin(false, args); 151 }; 152 153 /** 154 Alternative to mixin. Provided for compatibility with jQuery. 155 @function 156 */ 157 SC.extend = SC.mixin; 158 159 // .......................................................... 160 // CORE FUNCTIONS 161 // 162 // Enough with the bootstrap code. Let's define some core functions 163 164 SC.mixin(/** @scope window.SC.prototype */ { 165 166 // ........................................ 167 // GLOBAL CONSTANTS 168 // 169 T_ERROR: 'error', 170 T_OBJECT: 'object', 171 T_NULL: 'null', 172 T_CLASS: 'class', 173 T_HASH: 'hash', 174 T_FUNCTION: 'function', 175 T_UNDEFINED: 'undefined', 176 T_NUMBER: 'number', 177 T_BOOL: 'boolean', 178 T_ARRAY: 'array', 179 T_STRING: 'string', 180 T_DATE: 'date', 181 T_REGEXP: 'regexp', 182 183 // ........................................ 184 // TYPING & ARRAY MESSAGING 185 // 186 187 /** 188 Returns a consistent type for the passed item. 189 190 Use this instead of the built-in typeOf() to get the type of an item. 191 It will return the same result across all browsers and includes a bit 192 more detail. 193 194 @param {Object} item the item to check 195 @returns {String} One of the following, depending on the type of the item<br> 196 SC.T_STRING: String primitive,<br> 197 SC.T_NUMBER: Number primitive,<br> 198 SC.T_BOOLEAN: Boolean primitive,<br> 199 SC.T_NULL: Null value,<br> 200 SC.T_UNDEFINED: Undefined value,<br> 201 SC.T_FUNCTION: A function,<br> 202 SC.T_DATE: Date primitive,<br> 203 SC.T_REGEXP: RegExp primitive,<br> 204 SC.T_ARRAY: An instance of Array,<br> 205 SC.T_CLASS: A SproutCore class (created using SC.Object.extend()),<br> 206 SC.T_OBJECT: A SproutCore object instance,<br> 207 SC.T_HASH: A JavaScript object not inheriting from SC.Object, <br> 208 SC.T_ERROR: A SproutCore SC.Error object <br> 209 */ 210 typeOf: function (item) { 211 var nativeType = SC._nativeTypeOf(item); 212 213 // Translate it into an SC type. 214 if (nativeType === "function") { 215 return item.isClass ? SC.T_CLASS : SC.T_FUNCTION; 216 } else if (nativeType === "object") { 217 218 // Note: typeOf() may be called before SC.Error has had a chance to load 219 // so this code checks for the presence of SC.Error first just to make 220 // sure. No error instance can exist before the class loads anyway so 221 // this is safe. 222 if (SC.Error && (item instanceof SC.Error)) { 223 return SC.T_ERROR; 224 } else if (item instanceof SC.Object) { 225 return SC.T_OBJECT; 226 } else { 227 return SC.T_HASH; 228 } 229 } 230 231 return nativeType; 232 }, 233 // Inlined from jQuery.type to avoid dependency. 234 _nativeTypeOf: function(item) { 235 if (item === undefined) return SC.T_UNDEFINED; 236 if (item === null) return SC.T_NULL; 237 238 var nativeType = typeof item, 239 toString; 240 if (nativeType === "object" || nativeType === "function") { 241 toString = SC._nativeToString.call(item); 242 return SC._nativeTypeHash[toString] || "object"; 243 } else { 244 return nativeType; 245 } 246 } , 247 // Inlined from jQuery's class2type to avoid dependency. 248 _nativeTypeHash: { 249 "[object Boolean]": "boolean", 250 "[object Number]": "number", 251 "[object String]": "string", 252 "[object Function]": "function", 253 "[object Array]": "array", 254 "[object Date]": "date", 255 "[object RegExp]": "regexp", 256 "[object Object]": "object" 257 }, 258 // Inlined from jQuery to avoid dependency. 259 _nativeToString: Object.prototype.toString, 260 261 /** 262 Returns YES if the passed value is null or undefined. This avoids errors 263 from JSLint complaining about use of ==, which can be technically 264 confusing. 265 266 @param {Object} obj value to test 267 @returns {Boolean} 268 */ 269 none: function (obj) { 270 /*jshint eqnull:true */ 271 return obj == null; 272 }, 273 274 /** 275 Verifies that a value is either null or an empty string. Return false if 276 the object is not a string. 277 278 @param {Object} obj value to test 279 @returns {Boolean} 280 */ 281 empty: function (obj) { 282 return obj === null || obj === undefined || obj === ''; 283 }, 284 285 /** 286 Returns YES if the passed object is an array or Array-like. 287 288 SproutCore Array Protocol: 289 * the object has an objectAt property; or 290 * the object is a native Array; or 291 * the object is an Object, and has a length property 292 293 Unlike SC.typeOf this method returns true even if the passed object is 294 not formally array but appears to be array-like (i.e. has a length 295 property, responds to .objectAt, etc.) 296 297 @param {Object} obj the object to test 298 @returns {Boolean} 299 */ 300 isArray: function (obj) { 301 if (!obj || obj.setInterval) { return false; } 302 if (Array.isArray && Array.isArray(obj)) { return true; } 303 if (obj.objectAt) { return true; } 304 if (obj.length !== undefined && SC._nativeTypeOf(obj) === "object") { return true; } 305 306 return false; 307 }, 308 309 /** 310 Makes an object into an Array if it is not array or array-like already. 311 Unlike SC.A(), this method will not clone the object if it is already 312 an array. 313 314 @param {Object} obj object to convert 315 @returns {Array} Actual array 316 */ 317 makeArray: function (obj) { 318 return SC.isArray(obj) ? obj : SC.A(obj); 319 }, 320 321 /** 322 Converts the passed object to an Array. If the object appears to be 323 array-like, a new array will be cloned from it. Otherwise, a new array 324 will be created with the item itself as the only item in the array. 325 326 @param {Object} object any enumerable or array-like object. 327 @returns {Array} Array of items 328 */ 329 A: function (obj) { 330 // null or undefined -- fast path 331 if (obj === null || obj === undefined) return []; 332 333 // primitive -- fast path 334 if (obj.slice instanceof Function) { 335 // do we have a string? 336 if (typeof(obj) === 'string') return [obj]; 337 else return obj.slice(); 338 } 339 340 // enumerable -- fast path 341 if (obj.toArray) return obj.toArray(); 342 343 // if not array-like, then just wrap in array. 344 if (!SC.isArray(obj)) return [obj]; 345 346 // when all else fails, do a manual convert... 347 var ret = [], len = obj.length; 348 while (--len >= 0) ret[len] = obj[len]; 349 return ret; 350 }, 351 352 // 353 // GUIDS & HASHES 354 // 355 356 // Like jQuery.expando but without any risk of conflict 357 guidKey: "SproutCore" + (SC.VERSION + Math.random()).replace(/\D/g, ""), 358 359 // Used for guid generation... 360 _keyCache: {}, 361 _uuid: 0, 362 363 /** 364 Returns a unique GUID for the object. If the object does not yet have 365 a guid, one will be assigned to it. You can call this on any object, 366 SC.Object-based or not, but be aware that it will add a _guid property. 367 368 You can also use this method on DOM Element objects. 369 370 @param {Object} obj any object, string, number, Element, or primitive 371 @returns {String} the unique guid for this instance. 372 */ 373 guidFor: function (obj) { 374 var cache, ret, 375 type = typeof obj; 376 377 // special cases where we don't want to add a key to object 378 if (obj === undefined) return "(undefined)"; 379 if (obj === null) return "(null)"; 380 381 // Don't allow prototype changes to String etc. to change the guidFor 382 if (type === SC.T_NUMBER || type === SC.T_STRING) { 383 return '(' + obj + ')'; // E.g. '(Abc)' or '(123)' 384 } else if (type === SC.T_BOOL) { 385 return (obj) ? "(true)" : "(false)"; 386 } 387 388 var guidKey = this.guidKey; 389 if (obj[guidKey]) return obj[guidKey]; 390 391 // More special cases; not as common, so we check for them after the cache 392 // lookup 393 if (obj === Object) return '(Object)'; 394 if (obj === Array) return '(Array)'; 395 396 return SC.generateGuid(obj, "sc"); 397 }, 398 399 /** 400 Returns a key name that combines the named key + prefix. This is more 401 efficient than simply combining strings because it uses a cache 402 internally for performance. 403 404 @param {String} prefix the prefix to attach to the key 405 @param {String} key The key 406 @returns {String} result 407 */ 408 keyFor: function (prefix, key) { 409 var ret, pcache = this._keyCache[prefix]; 410 if (!pcache) pcache = this._keyCache[prefix] = {}; // get cache for prefix 411 ret = pcache[key]; 412 if (!ret) ret = pcache[key] = prefix + '_' + key; 413 return ret; 414 }, 415 416 /** 417 Generates a new guid, optionally saving the guid to the object that you 418 pass in. You will rarely need to use this method. Instead you should 419 call SC.guidFor(obj), which return an existing guid if available. 420 421 @param {Object} obj the object to assign the guid to 422 @param {String} prefix prefixes the generated guid 423 @returns {String} the guid 424 */ 425 generateGuid: function (obj, prefix) { 426 var ret = (prefix + (SC._uuid++)); 427 if (obj) obj[this.guidKey] = ret; 428 return ret; 429 }, 430 431 /** 432 Returns a unique hash code for the object. If the object implements 433 a hash() method, the value of that method will be returned. Otherwise, 434 this will return the same value as guidFor(). 435 436 If you pass multiple arguments, hashFor returns a string obtained by 437 concatenating the hash code of each argument. 438 439 Unlike guidFor(), this method allows you to implement logic in your 440 code to cause two separate instances of the same object to be treated as 441 if they were equal for comparisons and other functions. 442 443 <b>IMPORTANT</b>: If you implement a hash() method, it MUST NOT return a 444 number or a string that contains only a number. Typically hash codes 445 are strings that begin with a "%". 446 447 @param {Object...} objects the object(s) 448 @returns {String} the hash code for this instance. 449 */ 450 hashFor: function () { 451 var l = arguments.length, 452 h = '', 453 obj, f, i; 454 455 for (i = 0; i < l; ++i) { 456 obj = arguments[i]; 457 h += (obj && (f = obj.hash) && (typeof f === SC.T_FUNCTION)) ? f.call(obj) : this.guidFor(obj); 458 } 459 460 return h === '' ? null : h; 461 }, 462 463 /** 464 This will compare the two object values using their hash codes. 465 466 @param {Object} a first value to compare 467 @param {Object} b the second value to compare 468 @returns {Boolean} YES if the two have equal hash code values. 469 470 */ 471 isEqual: function (a, b) { 472 // QUESTION: is there a compelling performance reason to special-case 473 // undefined here? 474 return this.hashFor(a) === this.hashFor(b); 475 }, 476 477 /** 478 This will compare two javascript values of possibly different types. 479 It will tell you which one is greater than the other by returning 480 -1 if the first is smaller than the second, 481 0 if both are equal, 482 1 if the first is greater than the second. 483 484 The order is calculated based on SC.ORDER_DEFINITION , if types are different. 485 In case they have the same type an appropriate comparison for this type is made. 486 487 @param {Object} v first value to compare 488 @param {Object} w the second value to compare 489 @returns {NUMBER} -1 if v < w, 0 if v = w and 1 if v > w. 490 491 */ 492 compare: function (v, w) { 493 // Doing a '===' check is very cheap, so in the case of equality, checking 494 // this up-front is a big win. 495 if (v === w) return 0; 496 497 var type1 = SC.typeOf(v); 498 var type2 = SC.typeOf(w); 499 500 // If we haven't yet generated a reverse-mapping of SC.ORDER_DEFINITION, 501 // do so now. 502 var mapping = SC.ORDER_DEFINITION_MAPPING; 503 if (!mapping) { 504 var order = SC.ORDER_DEFINITION; 505 mapping = SC.ORDER_DEFINITION_MAPPING = {}; 506 var idx, len; 507 for (idx = 0, len = order.length; idx < len; ++idx) { 508 mapping[order[idx]] = idx; 509 } 510 511 // We no longer need SC.ORDER_DEFINITION. 512 delete SC.ORDER_DEFINITION; 513 } 514 515 var type1Index = mapping[type1]; 516 var type2Index = mapping[type2]; 517 518 if (type1Index < type2Index) return -1; 519 if (type1Index > type2Index) return 1; 520 521 // ok - types are equal - so we have to check values now 522 switch (type1) { 523 case SC.T_BOOL: 524 case SC.T_NUMBER: 525 if (v < w) return -1; 526 if (v > w) return 1; 527 return 0; 528 529 case SC.T_STRING: 530 var comp = v.localeCompare(w); 531 if (comp < 0) return -1; 532 if (comp > 0) return 1; 533 return 0; 534 535 case SC.T_ARRAY: 536 var vLen = v.length; 537 var wLen = w.length; 538 var l = Math.min(vLen, wLen); 539 var r = 0; 540 var i = 0; 541 var thisFunc = arguments.callee; 542 while (r === 0 && i < l) { 543 r = thisFunc(v[i], w[i]); 544 i++; 545 } 546 if (r !== 0) return r; 547 548 // all elements are equal now 549 // shorter array should be ordered first 550 if (vLen < wLen) return -1; 551 if (vLen > wLen) return 1; 552 // arrays are equal now 553 return 0; 554 555 case SC.T_OBJECT: 556 if (v.constructor.isComparable === YES) return v.constructor.compare(v, w); 557 return 0; 558 559 default: 560 return 0; 561 } 562 }, 563 564 // .......................................................... 565 // OBJECT MANAGEMENT 566 // 567 568 /** 569 Empty function. Useful for some operations. 570 571 @returns {Object} 572 */ 573 K: function () { return this; }, 574 575 /** 576 Empty array. Useful for some optimizations. 577 578 @type Array 579 */ 580 EMPTY_ARRAY: [], 581 582 /** 583 Empty hash. Useful for some optimizations. 584 585 @type Hash 586 */ 587 EMPTY_HASH: {}, 588 589 /** 590 Empty range. Useful for some optimizations. 591 592 @type Range 593 */ 594 EMPTY_RANGE: {start: 0, length: 0}, 595 596 /** 597 Creates a new object with the passed object as its prototype. 598 599 This method uses JavaScript's native inheritance method to create a new 600 object. 601 602 You cannot use beget() to create new SC.Object-based objects, but you 603 can use it to beget Arrays, Hashes, Sets and objects you build yourself. 604 Note that when you beget() a new object, this method will also call the 605 didBeget() method on the object you passed in if it is defined. You can 606 use this method to perform any other setup needed. 607 608 In general, you will not use beget() often as SC.Object is much more 609 useful, but for certain rare algorithms, this method can be very useful. 610 611 For more information on using beget(), see the section on beget() in 612 Crockford's JavaScript: The Good Parts. 613 614 @param {Object} obj the object to beget 615 @returns {Object} the new object. 616 */ 617 beget: function (obj) { 618 if (obj === null || obj === undefined) return null; 619 var K = SC.K; 620 K.prototype = obj; 621 var ret = new K(); 622 K.prototype = null; // avoid leaks 623 if (typeof obj.didBeget === "function") ret = obj.didBeget(ret); 624 return ret; 625 }, 626 627 /** 628 Creates a clone of the passed object. This function can take just about 629 any type of object and create a clone of it, including primitive values 630 (which are not actually cloned because they are immutable). 631 632 If the passed object implements the clone() method, then this function 633 will simply call that method and return the result. 634 635 @param {Object} object the object to clone 636 @param {Boolean} deep if true, a deep copy of the object is made 637 @returns {Object} the cloned object 638 */ 639 copy: function (object, deep) { 640 var ret = object, idx; 641 642 // fast paths 643 if (object) { 644 if (object.isCopyable) return object.copy(deep); 645 if (object.clone) return object.clone(); 646 } 647 648 switch (SC._nativeTypeOf(object)) { 649 case "array": 650 ret = object.slice(); 651 652 if (deep) { 653 idx = ret.length; 654 while (idx--) { ret[idx] = SC.copy(ret[idx], true); } 655 } 656 break; 657 658 case "object": 659 ret = {}; 660 for (var key in object) { ret[key] = deep ? SC.copy(object[key], true) : object[key]; } 661 } 662 663 return ret; 664 }, 665 666 /** 667 Returns a new object combining the values of all passed hashes. 668 669 @param {Object...} object one or more objects 670 @returns {Object} new Object 671 */ 672 merge: function () { 673 var ret = {}, len = arguments.length, idx; 674 for (idx = 0; idx < len; idx++) SC.mixin(ret, arguments[idx]); 675 return ret; 676 }, 677 678 /** 679 Returns all of the keys defined on an object or hash. This is useful 680 when inspecting objects for debugging. 681 682 @param {Object} obj The Object 683 @returns {Array} array of keys 684 */ 685 keys: function (obj) { 686 var ret = []; 687 for (var key in obj) ret.push(key); 688 return ret; 689 }, 690 691 /** 692 Convenience method to inspect an object. This method will attempt to 693 convert the object into a useful string description. 694 695 @param {Object} obj The object you want to inspect. 696 697 @returns {String} A description of the object 698 */ 699 inspect: function (obj) { 700 var v, ret = []; 701 for (var key in obj) { 702 v = obj[key]; 703 if (v === 'toString') continue; // ignore useless items 704 if (SC.typeOf(v) === SC.T_FUNCTION) v = "function () { ... }"; 705 ret.push(key + ": " + v); 706 } 707 return "{ " + ret.join(", ") + " }"; 708 }, 709 710 /** 711 Returns a tuple containing the object and key for the specified property 712 path. If no object could be found to match the property path, then 713 returns null. 714 715 This is the standard method used throughout SproutCore to resolve property 716 paths. 717 718 @param {String} path the property path 719 @param {Object} root optional parameter specifying the place to start 720 @returns {Array} array with [object, property] if found or null 721 */ 722 tupleForPropertyPath: function (path, root) { 723 /* jshint eqnull:true */ 724 // if passed nothing, return nothing. 725 if (path == null) return null; 726 727 // if the passed path is itself a tuple, return it 728 if (typeof path === "object" && (path instanceof Array)) return path; 729 730 // find the key. It is the last . or first * 731 var key; 732 var stopAt = path.indexOf('*'); 733 if (stopAt < 0) stopAt = path.lastIndexOf('.'); 734 key = (stopAt >= 0) ? path.slice(stopAt + 1) : path; 735 736 // convert path to object. 737 var obj = this.objectForPropertyPath(path, root, stopAt); 738 return (obj && key) ? [obj, key] : null; 739 }, 740 741 /** 742 Finds the object for the passed path or array of path components. This is 743 the standard method used in SproutCore to traverse object paths. 744 745 @param {String} path the path 746 @param {Object} root optional root object. window is used otherwise 747 @param {Integer} stopAt optional point to stop searching the path. 748 @returns {Object} the found object or undefined. 749 */ 750 objectForPropertyPath: function (path, root, stopAt) { 751 752 var loc, nextDotAt, key, max; 753 754 if (!root) root = window; 755 756 // faster method for strings 757 if (typeof path === "string") { 758 if (stopAt === undefined) stopAt = path.length; 759 loc = 0; 760 while ((root) && (loc < stopAt)) { 761 nextDotAt = path.indexOf('.', loc); 762 if ((nextDotAt < 0) || (nextDotAt > stopAt)) nextDotAt = stopAt; 763 key = path.slice(loc, nextDotAt); 764 root = root.get ? root.get(key) : root[key]; 765 loc = nextDotAt + 1; 766 } 767 if (loc < stopAt) root = undefined; // hit a dead end. :( 768 769 // older method using an array 770 } else { 771 loc = 0; 772 max = path.length; 773 key = null; 774 775 while ((loc < max) && root) { 776 key = path[loc++]; 777 if (key) root = (root.get) ? root.get(key) : root[key]; 778 } 779 if (loc < max) root = undefined; 780 } 781 782 return root; 783 }, 784 785 /** 786 Acts very similar to SC.objectForPropertyPath(), the only difference is 787 that it will throw an error when object can't be found. 788 789 @param {String} path the path 790 @param {Object} root optional root object. window is used otherwise 791 @param {Integer} stopAt optional point to stop searching the path. 792 @returns {Object} the found object or throws an error. 793 */ 794 requiredObjectForPropertyPath: function (path, root, stopAt) { 795 var o = SC.objectForPropertyPath(path, root, stopAt); 796 if (!o) { 797 throw path + " could not be found"; 798 } 799 return o; 800 } 801 802 }); // end mixin 803 804 /** @private Alias for SC.clone() */ 805 SC.clone = SC.copy; 806 807 /** @private Alias for SC.A() */ 808 SC.$A = SC.A; 809 810 /** @private Provided for compatibility with old HTML templates. */ 811 SC.didLoad = SC.K; 812 813 /** @private Used by SC.compare */ 814 SC.ORDER_DEFINITION = [ SC.T_ERROR, 815 SC.T_UNDEFINED, 816 SC.T_NULL, 817 SC.T_BOOL, 818 SC.T_NUMBER, 819 SC.T_STRING, 820 SC.T_ARRAY, 821 SC.T_HASH, 822 SC.T_OBJECT, 823 SC.T_FUNCTION, 824 SC.T_CLASS ]; 825