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