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 
  8 sc_require('core');
  9 sc_require('mixins/observable');
 10 sc_require('private/observer_queue');
 11 sc_require('mixins/array');
 12 sc_require('system/set');
 13 
 14 /*global*/
 15 
 16 SC.BENCHMARK_OBJECTS = NO;
 17 
 18 // ..........................................................
 19 // PRIVATE HELPER METHODS
 20 //
 21 // Private helper methods.  These are not kept as part of the class
 22 // definition because SC.Object is copied frequently and we want to keep the
 23 // number of class methods to a minimum.
 24 
 25 /** @private */
 26 SC._detect_base = function _detect_base(func, parent, name) {
 27 
 28   return function invoke_superclass_method() {
 29     var base = parent[name],
 30       args,
 31       i, len;
 32 
 33     //@if(debug)
 34     if (!base) {
 35       throw new Error("Developer Error: No '" + name + "' method was found on the superclass");
 36     }
 37     //@endif
 38 
 39     // NOTE: It is possible to cache the base, so that the first
 40     // call to sc_super will avoid doing the lookup again. However,
 41     // since the cost of the extra method dispatch is low and is
 42     // only incurred on sc_super, but also creates another possible
 43     // weird edge-case (when a class is enhanced after first used),
 44     // we'll leave it off for now unless profiling demonstrates that
 45     // it's a hotspot.
 46     //if(base && func === base) { func.base = function () {}; }
 47     //else { func.base = base; }
 48 
 49     // Accessing `arguments.length` is just a Number and doesn't materialize the `arguments` object, which is costly.
 50     // TODO: Add macro to build tools for this.
 51     if (func.isEnhancement) {
 52       // Fast copy.
 53       args = new Array(arguments.length - 1); // Array.prototype.slice.call(arguments, 1)
 54       for (i = 0, len = args.length; i < len; i++) { args[i] = arguments[i + 1]; }
 55     } else {
 56       // Fast copy.
 57       args = new Array(arguments.length);
 58       for (i = 0, len = args.length; i < len; i++) { args[i] = arguments[i]; }
 59     }
 60 
 61     return base.apply(this, args);
 62   };
 63 };
 64 
 65 /** @private
 66   Augments a base object by copying the properties from the extended hash.
 67   In addition to simply copying properties, this method also performs a
 68   number of optimizations that can make init'ing a new object much faster
 69   including:
 70 
 71   - concatenating concatenatedProperties
 72   - prepping a list of bindings, observers, and dependent keys
 73   - caching local observers so they don't need to be manually constructed.
 74 
 75   @param {Hash} base hash
 76   @param {Hash} extension
 77   @returns {Hash} base hash
 78 */
 79 SC._object_extend = function _object_extend(base, ext, proto) {
 80   //@if(debug)
 81   if (!ext) { throw new Error("Developer Error: SC.Object.extend expects a non-null value.  Did you forget to 'sc_require' something?  Or were you passing a Protocol to extend() as if it were a mixin?"); }
 82   //@endif
 83   // set _kvo_cloned for later use
 84   base._kvo_cloned = null;
 85 
 86   // get some common vars
 87   var key, idx, cur,
 88     cprops = base.concatenatedProperties,
 89     K = SC.K,
 90     p1, p2;
 91 
 92   // first, save any concat props.  use old or new array or concat
 93   idx = (cprops) ? cprops.length : 0;
 94   var concats = (idx > 0) ? {} : null;
 95   while (--idx >= 0) {
 96     key = cprops[idx];
 97     p1 = base[key];
 98     p2 = ext[key];
 99 
100     if (p1) {
101       if (!(p1 instanceof Array)) p1 = SC.$A(p1);
102       concats[key] = (p2) ? p1.concat(p2) : p2;
103     } else {
104       if (!(p2 instanceof Array)) p2 = SC.$A(p2);
105       concats[key] = p2;
106     }
107   }
108 
109   // setup arrays for bindings, observers, and properties.  Normally, just
110   // save the arrays from the base.  If these need to be changed during
111   // processing, then they will be cloned first.
112   var bindings = base._bindings, clonedBindings = NO,
113       observers = base._observers, clonedObservers = NO,
114       properties = base._properties, clonedProperties = NO,
115       paths, pathLoc, local, value;
116 
117   // outlets are treated a little differently because you can manually
118   // name outlets in the passed in hash. If this is the case, then clone
119   // the array first.
120   var outlets = base.outlets, clonedOutlets = NO;
121   if (ext.outlets) {
122     outlets = (outlets || SC.EMPTY_ARRAY).concat(ext.outlets);
123     clonedOutlets = YES;
124   }
125 
126   // now copy properties, add superclass to func.
127   for (key in ext) {
128 
129     if (key === '_kvo_cloned') continue; // do not copy
130 
131     // avoid copying builtin methods
132     if (!ext.hasOwnProperty(key)) continue;
133 
134     // get the value.  use concats if defined
135     value = (concats.hasOwnProperty(key) ? concats[key] : null) || ext[key];
136 
137     // Support fooBinding syntax...
138     if (key.length > 7 && key.slice(-7) === "Binding") {
139       if (!clonedBindings) {
140         bindings = (bindings || SC.EMPTY_ARRAY).slice();
141         clonedBindings = YES;
142       }
143       // If the binding key is new (not found on base), add it to the list of binding keys. (If it's on
144       // base, we assume that it's already in there. We don't check for performance reasons. If that's
145       // failing, fix the failure not the check.)
146 
147       /* jshint eqnull:true */
148       if (base[key] == null) { // (SC.none is inlined here for performance.)
149         bindings[bindings.length] = key;
150       }
151     }
152 
153     // Add observers, outlets, properties and extensions for functions...
154     else if (value && (value instanceof Function)) {
155 
156       // add super to funcs.  Be sure not to set the base of a func to
157       // itself to avoid infinite loops.
158       if (!value.superclass && (value !== (cur = base[key]))) {
159         value.superclass = cur || K;
160         value.base = proto ? SC._detect_base(value, proto, key) : cur || K;
161       }
162 
163       // handle regular observers
164       if (value.propertyPaths) {
165         if (!clonedObservers) {
166           observers = (observers || SC.EMPTY_ARRAY).slice();
167           clonedObservers = YES;
168         }
169         observers[observers.length] = key;
170 
171       // handle local properties
172       }
173 
174       paths = value.localPropertyPaths;
175       if (paths) {
176         pathLoc = paths.length;
177         while (--pathLoc >= 0) {
178           local = base._kvo_for(SC.keyFor('_kvo_local', paths[pathLoc]), SC.CoreSet);
179           local.add(key);
180           base._kvo_for('_kvo_observed_keys', SC.CoreSet).add(paths[pathLoc]);
181         }
182 
183       // handle computed properties
184       }
185 
186       if (value.dependentKeys) {
187         if (!clonedProperties) {
188           properties = (properties || SC.EMPTY_ARRAY).slice();
189           clonedProperties = YES;
190         }
191         properties[properties.length] = key;
192 
193       // handle outlets
194       }
195 
196       if (value.autoconfiguredOutlet) {
197         if (!clonedOutlets) {
198           outlets = (outlets || SC.EMPTY_ARRAY).slice();
199           clonedOutlets = YES;
200         }
201         outlets[outlets.length] = key;
202       }
203 
204       if (value.isEnhancement) {
205         value = SC._enhance(base[key] || K, value);
206       }
207     }
208 
209     // copy property
210     base[key] = value;
211   }
212 
213   // Manually set base on toString() because some JS engines (such as IE8) do
214   // not enumerate it
215   if (ext.hasOwnProperty('toString')) {
216     key = 'toString';
217     // get the value.  use concats if defined
218     value = (concats.hasOwnProperty(key) ? concats[key] : null) || ext[key];
219     if (!value.superclass && (value !== (cur = base[key]))) {
220       value.superclass = value.base = cur || K;
221     }
222     // copy property
223     base[key] = value;
224   }
225 
226 
227   // copy bindings, observers, and properties
228   base._bindings = bindings || [];
229   base._observers = observers || [];
230   base._properties = properties || [];
231   base.outlets = outlets || [];
232 
233   return base;
234 };
235 
236 /** @private */
237 SC._enhance = function (originalFunction, enhancement) {
238 
239   return function () {
240     // Accessing `arguments.length` is just a Number and doesn't materialize the `arguments` object, which is costly.
241     // TODO: Add macro to build tools for this.
242     var enhancedArgs = new Array(arguments.length + 1); // Array.prototype.slice.call(arguments)
243     for (var i = 1, len = enhancedArgs.length; i < len; i++) {
244       enhancedArgs[i] = arguments[i - 1];
245     }
246 
247     // Add the original function as the first argument passed to the enhancement.
248     var self = this;
249     enhancedArgs[0] = function () {
250       // Fast copy.
251       var originalArgs = new Array(arguments.length);
252       for (var i = 0, len = originalArgs.length; i < len; i++) {
253         originalArgs[i] = arguments[i];
254       }
255 
256       return originalFunction.apply(self, originalArgs);
257     }; // args.unshift(function ...
258 
259     return enhancement.apply(this, enhancedArgs);
260   };
261 
262 };
263 
264 /** @class
265 
266   Root object for the SproutCore framework.  SC.Object is the root class for
267   most classes defined by SproutCore.  It builds on top of the native object
268   support provided by JavaScript to provide support for class-like
269   inheritance, automatic bindings, properties observers, and more.
270 
271   Most of the classes you define in your application should inherit from
272   SC.Object or one of its subclasses.  If you are writing objects of your
273   own, you should read this documentation to learn some of the details of
274   how SC.Object's behave and how they differ from other frameworks.
275 
276   About SproutCore Classes
277   ===
278 
279   JavaScript is not a class-based language.  Instead it uses a type of
280   inheritance inspired by self called "prototypical" inheritance.
281   ...
282 
283   Using SproutCore objects with other JavaScript object.
284   ===
285 
286   You can create a SproutCore object just like any other object...
287   obj = new SC.Object();
288 
289   @extends SC.Observable
290   @since SproutCore 1.0
291 */
292 SC.Object = function (props) {
293   this.__sc_super__ = SC.Object.prototype;
294   return this._object_init(props);
295 };
296 
297 SC.mixin(SC.Object, /** @scope SC.Object */ {
298 
299   /**
300     Adds the passed properties to the object's class definition.  You can
301     pass as many hashes as you want, including Mixins, and they will be
302     added in the order they are passed.
303 
304     This is a shorthand for calling SC.mixin(MyClass, props...);
305 
306     @param {Hash} props the properties you want to add.
307     @returns {Object} receiver
308   */
309   mixin: function () {
310     var len = arguments.length, loc;
311     for (loc = 0; loc < len; loc++) SC.mixin(this, arguments[loc]);
312     return this;
313   },
314 
315   // ..........................................
316   // CREATING CLASSES AND INSTANCES
317   //
318 
319   /**
320     Points to the superclass for this class.  You can use this to trace a
321     class hierarchy.
322 
323     @type SC.Object
324   */
325   superclass: null,
326 
327   /**
328     Creates a new subclass of the receiver, adding any passed properties to
329     the instance definition of the new class.  You should use this method
330     when you plan to create several objects based on a class with similar
331     properties.
332 
333     Init:
334 
335     If you define an init() method, it will be called when you create
336     instances of your new class.  Since SproutCore uses the init() method to
337     do important setup, you must be sure to always call sc_super() somewhere
338     in your init() to allow the normal setup to proceed.
339 
340     @param {Hash} props the methods of properties you want to add
341     @returns {Class} A new object class
342   */
343   extend: function () {
344     //@if(debug)
345     var bench = SC.BENCHMARK_OBJECTS;
346     if (bench) SC.Benchmark.start('SC.Object.extend');
347     //@endif
348 
349     // build a new constructor and copy class methods.  Do this before
350     // adding any other properties so they are not overwritten by the copy.
351     var prop, ret = function (props) {
352       this.__sc_super__ = ret.prototype;
353       return this._object_init(props);
354     };
355     for (prop in this) {
356       if (!this.hasOwnProperty(prop)) continue;
357       ret[prop] = this[prop];
358     }
359 
360     // manually copy toString() because some JS engines do not enumerate it
361     if (this.hasOwnProperty('toString')) ret.toString = this.toString;
362 
363     // now setup superclass, guid
364     ret.superclass = this;
365     ret.__sc_super__ = this.prototype;
366     SC.generateGuid(ret, "sc"); // setup guid
367 
368     ret.subclasses = SC.Set.create();
369     this.subclasses.add(ret); // now we can walk a class hierarchy
370 
371     // setup new prototype and add properties to it
372     var base = (ret.prototype = SC.beget(this.prototype)),
373         idx, len = arguments.length;
374 
375     for (idx = 0; idx < len; idx++) {
376       SC._object_extend(base, arguments[idx], ret.__sc_super__);
377     }
378     base.constructor = ret; // save constructor
379 
380     //@if(debug)
381     if (bench) SC.Benchmark.end('SC.Object.extend');
382     //@endif
383 
384     return ret;
385   },
386 
387   // Tested in ../tests/system/object/enhance.js
388   reopen: function (props) {
389     // Reopen subclasses.
390     if (this.subclasses) {
391       var subclass, key, value, theseProps,
392           len = this.subclasses.length, i;
393       for (i = 0; i < len; i++) {
394         theseProps = null;
395         subclass = this.subclasses[i];
396         for (key in props) {
397           // avoid copying builtin methods
398           if (!props.hasOwnProperty(key)) continue;
399 
400           // Remove properties that have already been overridden by the subclass.
401           if (subclass.prototype.hasOwnProperty(key)) {
402             if (!theseProps) {
403               theseProps = SC.clone(props);
404             }
405             delete theseProps[key];
406             continue;
407           }
408 
409           // Remove enhancements that are only intended for the superclass's
410           // function.
411           value = props[key];
412           if (value && (value instanceof Function) && (value.isEnhancement)) {
413             if (!theseProps) {
414               theseProps = SC.clone(props);
415             }
416             delete theseProps[key];
417             continue;
418           }
419         }
420 
421         subclass.reopen(theseProps || props);
422       }
423     }
424 
425     // Reopen this.
426     return SC._object_extend(this.prototype, props, this.__sc_super__);
427   },
428 
429   /**
430     Creates a new instance of the class.
431 
432     Unlike most frameworks, you do not pass parameters to the init function
433     for an object.  Instead, you pass a hash of additional properties you
434     want to have assigned to the object when it is first created.  This is
435     functionally like creating an anonymous subclass of the receiver and then
436     instantiating it, but more efficient.
437 
438     You can use create() like you would a normal constructor in a
439     class-based system, or you can use it to create highly customized
440     singleton objects such as controllers or app-level objects.  This is
441     often more efficient than creating subclasses and then instantiating
442     them.
443 
444     You can pass any hash of properties to this method, including mixins.
445 
446     @param {Hash} props
447       optional hash of method or properties to add to the instance.
448 
449     @returns {SC.Object} new instance of the receiver class.
450   */
451   create: function () {
452     var C = this, ret = new C(arguments);
453 
454     if (SC.ObjectDesigner) {
455       SC.ObjectDesigner.didCreateObject(ret, SC.$A(arguments));
456     }
457     return ret;
458   },
459   /**
460     Walk like a duck.  You can use this to quickly test classes.
461 
462     @type Boolean
463   */
464   isClass: YES,
465 
466   /**
467     Set of subclasses that extend from this class.  You can observe this
468     array if you want to be notified when the object is extended.
469 
470     @type SC.Set
471   */
472   subclasses: SC.Set.create(),
473 
474   /** @private */
475   toString: function () { return SC._object_className(this); },
476 
477   // ..........................................
478   // PROPERTY SUPPORT METHODS
479   //
480 
481   /**
482     Returns YES if the receiver is a subclass of the named class.  If the
483     receiver is the class passed, this will return NO since the class is not
484     a subclass of itself.  See also kindOf().
485 
486     Example:
487 
488           ClassA = SC.Object.extend();
489           ClassB = ClassA.extend();
490 
491           ClassB.subclassOf(ClassA) => YES
492           ClassA.subclassOf(ClassA) => NO
493 
494     @param {Class} scClass class to compare
495     @returns {Boolean}
496   */
497   subclassOf: function (scClass) {
498     if (this === scClass) return NO;
499     var t = this;
500     while ((t = t.superclass)) if (t === scClass) return YES;
501     return NO;
502   },
503 
504   /**
505     Returns YES if the passed object is a subclass of the receiver.  This is
506     the inverse of subclassOf() which you call on the class you want to test.
507 
508     @param {Class} scClass class to compare
509     @returns {Boolean}
510   */
511   hasSubclass: function (scClass) {
512     return (scClass && scClass.subclassOf) ? scClass.subclassOf(this) : NO;
513   },
514 
515   /**
516     Returns YES if the receiver is the passed class or is a subclass of the
517     passed class.  Unlike subclassOf(), this method will return YES if you
518     pass the receiver itself, since class is a kind of itself.  See also
519     subclassOf().
520 
521     Example:
522 
523           ClassA = SC.Object.extend();
524           ClassB = ClassA.extend();
525 
526           ClassB.kindOf(ClassA) => YES
527           ClassA.kindOf(ClassA) => YES
528 
529     @param {Class} scClass class to compare
530     @returns {Boolean}
531   */
532   kindOf: function (scClass) {
533     return (this === scClass) || this.subclassOf(scClass);
534   },
535 
536   // ..........................................................
537   // Designers
538   //
539   /**
540     This method works just like extend() except that it will also preserve
541     the passed attributes.
542 
543     @param {Hash} attrs Attributes to add to view
544     @returns {Class} SC.Object subclass to create
545     @function
546   */
547   design: function () {
548     if (this.isDesign) {
549       // @if (debug)
550       SC.Logger.warn("Developer Warning: SC.Object.prototype.design called twice for %@.".fmt(this));
551       // @endif
552       return this;
553     }
554 
555     var ret = this.extend.apply(this, arguments);
556     ret.isDesign = YES;
557     if (SC.ObjectDesigner) {
558       SC.ObjectDesigner.didLoadDesign(ret, this, SC.A(arguments));
559     }
560     return ret;
561   }
562 
563 });
564 
565 // ..........................................
566 // DEFAULT OBJECT INSTANCE
567 //
568 SC.Object.prototype = {
569 
570   _kvo_enabled: YES,
571 
572   /** @private
573     This is the first method invoked on a new instance.  It will first apply
574     any added properties to the new instance and then calls the real init()
575     method.
576 
577     @param {Array} extensions an array-like object with hashes to apply.
578     @returns {Object} receiver
579   */
580   _object_init: function (extensions) {
581     // apply any new properties
582     var idx,
583       len = (extensions) ? extensions.length : 0;
584     for (idx = 0; idx < len; idx++) { SC._object_extend(this, extensions[idx], this.__sc_super__); }
585     SC.generateGuid(this, "sc"); // add guid
586     this.init(); // call real init
587 
588     // Call 'initMixin' methods to automatically setup modules.
589     var inits = this.initMixin;
590     len = (inits) ? inits.length : 0;
591     for (idx = 0; idx < len; idx++) inits[idx].call(this);
592 
593     return this; // done!
594   },
595 
596   /**
597     You can call this method on an object to mixin one or more hashes of
598     properties on the receiver object.  In addition to simply copying
599     properties, this method will also prepare the properties for use in
600     bindings, computed properties, etc.
601 
602     If you plan to use this method, you should call it before you call
603     the inherited init method from SC.Object or else your instance may not
604     function properly.
605 
606     Example:
607 
608           // dynamically apply a mixin specified in an object property
609           var MyClass = SC.Object.extend({
610              extraMixin: null,
611 
612              init: function () {
613                this.mixin(this.extraMixin);
614                sc_super();
615              }
616           });
617 
618           var ExampleMixin = { foo: "bar" };
619 
620           var instance = MyClass.create({ extraMixin: ExampleMixin });
621 
622           instance.get('foo') => "bar"
623 
624     @param {Hash} ext a hash to copy.  Only one.
625     @returns {Object} receiver
626   */
627   mixin: function () {
628     var idx, len = arguments.length, init;
629     for (idx = 0; idx < len; idx++) SC._object_extend(this, arguments[idx]);
630 
631     // Reset the observable initialized status so that we can setup any new observables.
632     this._observableInited = NO;
633     this.initObservable();
634 
635     // Call initMixin
636     for (idx = 0; idx < len; idx++) {
637       init = arguments[idx].initMixin;
638       if (init) init.call(this);
639     }
640     return this;
641   },
642 
643   /**
644     This method is invoked automatically whenever a new object is
645     instantiated.  You can override this method as you like to setup your
646     new object.
647 
648     Within your object, be sure to call sc_super() to ensure that the
649     built-in init method is also called or your observers and computed
650     properties may not be configured.
651 
652     Although the default init() method returns the receiver, the return
653     value is ignored.
654   */
655   init: function () {
656     //@if(debug)
657     // Provide some developer support for the deprecation of `awake`.
658     if (this.awake !== SC.Object.prototype.awake) SC.warn("Developer Warning: `awake` has been deprecated and will not be called. Override `init` and call sc_super(); instead.");
659     //@endif
660     this.initObservable();
661     return this;
662   },
663 
664   /**
665     This is set to YES once this object has been destroyed.
666 
667     @type Boolean
668   */
669   isDestroyed: NO,
670 
671   /**
672     Call this method when you are finished with an object to teardown its
673     contents.  Because JavaScript is garbage collected, you do not usually
674     need to call this method.  However, you may choose to do so for certain
675     objects, especially views, in order to let them reclaim memory they
676     consume immediately.
677 
678     If you would like to perform additional cleanup when an object is
679     finished, you may override this method.  Be sure to call sc_super().
680 
681     @returns {SC.Object} receiver
682   */
683   destroy: function () {
684     if (this.get('isDestroyed')) return this; // nothing to do
685     this.set('isDestroyed', YES);
686 
687     // destroy any mixins
688     var idx, inits = this.destroyMixin, len = (inits) ? inits.length : 0;
689     for (idx = 0; idx < len; idx++) inits[idx].call(this);
690 
691     // destroy observables.
692     this.destroyObservable();
693 
694     return this;
695   },
696 
697   /**
698     Walk like a duck. Always YES since this is an object and not a class.
699 
700     @type Boolean
701   */
702   isObject: true,
703 
704   /**
705     Returns YES if the named value is an executable function.
706 
707     @param {String} methodName the property name to check
708     @returns {Boolean}
709   */
710   respondsTo: function (methodName) {
711     return !!(this[methodName] instanceof Function);
712   },
713 
714   /**
715     Attempts to invoke the named method, passing the included two arguments.
716     Returns NO if the method is either not implemented or if the handler
717     returns NO (indicating that it did not handle the event).  This method
718     is invoked to deliver actions from menu items and to deliver events.
719     You can override this method to provide additional handling if you
720     prefer.
721 
722     @param {String} methodName
723     @param {Object} arg1
724     @param {Object} arg2
725     @returns {Boolean} YES if handled, NO if not handled
726   */
727   tryToPerform: function (methodName, arg1, arg2) {
728     return this.respondsTo(methodName) && (this[methodName](arg1, arg2) !== NO);
729   },
730 
731   /**
732     @deprecated v1.11 - use sc_super() (with build tools) or arguments.callee.base.apply(this, arguments) instead.
733 
734     EXPERIMENTAL:  You can use this to invoke a superclass implementation in
735     any method.  This does not work in Safari 2 or earlier.  If you need to
736     target these methods, you should use one of the alternatives below:
737 
738     - *With Build Tools:* sc_super();
739     - *Without Build Tools:* arguments.callee.base.apply(this, arguments);
740 
741     Example
742 
743     All of the following methods will call the superclass implementation of
744     your method:
745 
746           SC.Object.create({
747 
748             // DOES NOT WORK IN SAFARI 2 OR EARLIER
749             method1: function () {
750               this.superclass();
751             },
752 
753             // REQUIRES SC-BUILD TOOLS
754             method2: function () {
755               sc_super();
756             },
757 
758             // WORKS ANYTIME
759             method3: function () {
760               arguments.callee.base.apply(this, arguments);
761             }
762           });
763 
764     @param {*args} args any arguments you want to pass along.
765     @returns {Object} return value from super
766   */
767   superclass: function () {
768     //@if(debug)
769     SC.warn("Developer Warning: SC.Object's 'superclass' instance method is deprecated and will be removed in a future version. Use `sc_super();` instead.\n\n(Note that the unrelated `superclass` class property, which contains a class's superclass, is alive and well.)");
770     //@endif
771 
772     var caller = arguments.callee.caller;
773 
774     //@if(debug)
775     if (!caller) { throw new Error("Developer Error: superclass cannot determine the caller method: %@".fmt(this)); }
776     //@endif
777 
778     return caller.superclass ? caller.superclass.apply(this, arguments) : null;
779   },
780 
781   /**
782     returns YES if the receiver is an instance of the named class.  See also
783     kindOf().
784 
785     Example
786 
787           var ClassA = SC.Object.extend();
788           var ClassB = SC.Object.extend();
789 
790           var instA = ClassA.create();
791           var instB = ClassB.create();
792 
793           instA.instanceOf(ClassA) => YES
794           instB.instanceOf(ClassA) => NO
795 
796     @param {Class} scClass the class
797     @returns {Boolean}
798   */
799   instanceOf: function (scClass) {
800     return this.constructor === scClass;
801   },
802 
803   /**
804     Returns true if the receiver is an instance of the named class or any
805     subclass of the named class.  See also instanceOf().
806 
807     Example
808 
809           var ClassA = SC.Object.extend();
810           var ClassB = SC.Object.extend();
811 
812           var instA = ClassA.create();
813           var instB = ClassB.create();
814 
815           instA.kindOf(ClassA) => YES
816           instB.kindOf(ClassA) => YES
817 
818     @param {Class} scClass the class
819     @returns {Boolean}
820   */
821   kindOf: function (scClass) { return this.constructor.kindOf(scClass); },
822 
823   /** @private */
824   toString: function () {
825     if (!this._object_toString) {
826       // only cache the string if the klass name is available
827       var klassName = SC._object_className(this.constructor),
828           string = klassName + ":" + SC.guidFor(this);
829       if (klassName) this._object_toString = string;
830       else return string;
831     }
832     return this._object_toString;
833   },
834 
835   /** @deprecated v1.11 - use init instead. */
836   //@if(debug)
837   awake: function () {
838     SC.warn('Developer Warning: The `awake` method has been deprecated. Use `init` instead. (Be sure to call sc_super().)');
839   },
840   //@endif
841 
842   /**
843     Invokes the passed method or method name one time during the runloop.  You
844     can use this method to schedule methods that need to execute but may be
845     too expensive to execute more than once, such as methods that update the
846     DOM.
847 
848     Note that in development mode only, the object and method that call this
849     method will be recorded, for help in debugging scheduled code.
850 
851     @param {Function|String} method method or method name
852     @returns {SC.Object} receiver
853   */
854   invokeOnce: function (method) {
855     //@if(debug)
856     // If we're logging deferred calls, send along the information that needs to
857     // be recorded.
858     var originatingTarget, originatingMethod, originatingStack;
859     if (SC.LOG_DEFERRED_CALLS) {
860       originatingTarget = this;
861       originatingStack  = SC._getRecentStack();
862       originatingMethod = originatingStack[0];
863     }
864     SC.RunLoop.currentRunLoop.invokeOnce(this, method, originatingTarget, originatingMethod, originatingStack);
865     return this;
866     //@endif
867     SC.RunLoop.currentRunLoop.invokeOnce(this, method);
868     return this;
869   },
870 
871   /**
872     Invokes the passed method once at the end of the current run of the run loop,
873     before any other methods (including new events) are processed. This is useful
874     for situations where you know you need to update something, but due to
875     the way the run loop works, you can't actually do the update until the
876     run loop has completed.
877 
878     A simple example is setting the selection on a collection controller to a
879     newly created object. Because the collection controller won't have its
880     content collection updated until later in the run loop, setting the
881     selection immediately will have no effect. In this situation, you could do
882     this instead:
883 
884           // Creates a new MyRecord object and sets the selection of the
885           // myRecord collection controller to the new object.
886           createObjectAction: function (sender, evt) {
887             // create a new record and add it to the store
888             var obj = MyRecord.newRecord();
889 
890             // update the collection controller's selection
891             MyApp.myRecordCollectionController.invokeLast( function () {
892               this.set('selection', [obj]);
893             });
894           }
895 
896     Note that in development mode only, the object and method that call this
897     method will be recorded, for help in debugging scheduled code.
898 
899     @param {Function|String} method method or method name
900     @returns {SC.Object} receiver
901   */
902   invokeLast: function (method) {
903     //@if(debug)
904     // If we're logging deferred calls, send along the information that needs to
905     // be recorded.
906     var originatingTarget, originatingMethod, originatingStack;
907     if (SC.LOG_DEFERRED_CALLS) {
908       originatingTarget = this;
909       originatingStack  = SC._getRecentStack();
910       originatingMethod = originatingStack[0];
911     }
912     SC.RunLoop.currentRunLoop.invokeLast(this, method, originatingTarget, originatingMethod, originatingStack);
913     return this;
914     //@endif
915     SC.RunLoop.currentRunLoop.invokeLast(this, method);
916     return this;
917   },
918 
919   /**
920     Invokes the passed target/method pair once at the beginning of the next
921     run of the run loop, before any other methods (including events) are
922     processed.  Use this to defer painting to make views more responsive or
923     to ensure that the layer has been updated before using it.
924 
925     If you call this with the same target/method pair multiple times it will
926     only invoke the pair only once at the beginning of the next runloop.
927 
928     Note that in development mode only, the object and method that call this
929     method will be recorded, for help in debugging scheduled code.
930 
931     @param {Function|String} method method or method name
932     @returns {SC.Object} receiver
933    */
934   invokeNext: function (method) {
935     //@if(debug)
936     // If we're logging deferred calls, send along the information that needs to
937     // be recorded.
938     var originatingTarget, originatingMethod, originatingStack;
939     if (SC.LOG_DEFERRED_CALLS) {
940       originatingTarget = this;
941       originatingStack  = SC._getRecentStack();
942       originatingMethod = originatingStack[0];
943     }
944     SC.RunLoop.currentRunLoop.invokeNext(this, method, originatingTarget, originatingMethod, originatingStack);
945     return this;
946     //@endif
947     SC.RunLoop.currentRunLoop.invokeNext(this, method);
948     return this;
949   },
950 
951   /**
952     The properties named in this array will be concatenated in subclasses
953     instead of replaced.  This allows you to name special properties that
954     should contain any values you specify plus values specified by parents.
955 
956     It is used by SproutCore and is available for your use, though you
957     should limit the number of properties you include in this list as it
958     adds a slight overhead to new class and instance creation.
959 
960     @type Array
961   */
962   concatenatedProperties: ['concatenatedProperties', 'initMixin', 'destroyMixin']
963 
964 };
965 
966 // bootstrap the constructor for SC.Object.
967 SC.Object.prototype.constructor = SC.Object;
968 
969 // Add observable to mixin
970 SC.mixin(SC.Object.prototype, SC.Observable);
971 
972 // ..........................................................
973 // CLASS NAME SUPPORT
974 //
975 
976 /** @private
977   This is a way of performing brute-force introspection.  This searches
978   through all the top-level properties looking for classes.  When it finds
979   one, it saves the class path name.
980 */
981 SC.findClassNames = function () {
982   if (SC._object_foundObjectClassNames) return;
983   SC._object_foundObjectClassNames = true;
984 
985   var seen = [],
986       detectedSC = false;
987   var searchObject = function (root, object, levels) {
988 
989     var path, value, type;
990     levels--;
991 
992     // not the fastest, but safe
993     if (seen.indexOf(object) >= 0) return;
994     seen.push(object);
995 
996     for (var key in object) {
997       if (key === '__scope__') continue;
998       if (key === 'superclass') continue;
999       if (key === '__SC__') key = 'SC';
1000       if (!key.match(/^[A-Z0-9]/)) continue;
1001       if (key === 'SC') {
1002         if (detectedSC) continue;
1003         detectedSC = true;
1004       }
1005 
1006       path = (root) ? [root, key].join('.') : key;
1007       value = object[key];
1008 
1009       try {
1010         type = SC.typeOf(value);
1011       } catch (e) {
1012         // Firefox gives security errors when trying to run typeOf on certain objects
1013         break;
1014       }
1015 
1016       switch (type) {
1017       case SC.T_CLASS:
1018         if (!value._object_className) value._object_className = path;
1019         if (levels >= 0) searchObject(path, value, levels);
1020         break;
1021 
1022       case SC.T_OBJECT:
1023         if (levels >= 0) searchObject(path, value, levels);
1024         break;
1025 
1026       case SC.T_HASH:
1027         if (((root) || (path === 'SC')) && (levels >= 0)) searchObject(path, value, levels);
1028         break;
1029 
1030       default:
1031         break;
1032       }
1033     }
1034   };
1035 
1036   // Fix for IE 7 and 8 in order to detect the SC global variable. When you create
1037   // a global variable in IE, it is not added to the window object like in other
1038   // browsers. Therefore the searchObject method will not pick it up. So we have to
1039   // update the window object to have a reference to the global variable. And
1040   // doing window['SC'] does not work since the global variable already exists. For
1041   // any object that you create that is used act as a namespace, be sure to create it
1042   // like so:
1043   //
1044   //   window.MyApp = window.MyApp || SC.Object.create({ ... })
1045   //
1046   window.__SC__ = SC;
1047   searchObject(null, window, 2);
1048 };
1049 
1050 /**
1051   Same as the instance method, but lets you check instanceOf without
1052   having to first check if instanceOf exists as a method.
1053 
1054   @param {Object} scObject the object to check instance of
1055   @param {Class} scClass the class
1056   @returns {Boolean} if object1 is instance of class
1057 */
1058 SC.instanceOf = function (scObject, scClass) {
1059   return !!(scObject && scObject.constructor === scClass);
1060 };
1061 
1062 /**
1063   Same as the instance method, but lets you check kindOf without having to
1064   first check if kindOf exists as a method.
1065 
1066   @param {Object} scObject object to check kind of
1067   @param {Class} scClass the class to check
1068   @returns {Boolean} if object is an instance of class or subclass
1069 */
1070 SC.kindOf = function (scObject, scClass) {
1071   if (scObject && !scObject.isClass) scObject = scObject.constructor;
1072   return !!(scObject && scObject.kindOf && scObject.kindOf(scClass));
1073 };
1074 
1075 /** @private
1076   Returns the name of this class.  If the name is not known, triggers
1077   a search.  This can be expensive the first time it is called.
1078 
1079   This method is used to allow classes to determine their own name.
1080 */
1081 SC._object_className = function (obj) {
1082   if (SC.isReady === NO) return ''; // class names are not available until ready
1083   if (!obj._object_className) SC.findClassNames();
1084   if (obj._object_className) return obj._object_className;
1085 
1086   // if no direct classname was found, walk up class chain looking for a
1087   // match.
1088   var ret = obj;
1089   while (ret && !ret._object_className) ret = ret.superclass;
1090   return (ret && ret._object_className) ? ret._object_className : 'Anonymous';
1091 };
1092 
1093