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