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