]>
git.wh0rd.org - tt-rss.git/blob - lib/dojo/_base/declare.js
2 Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
3 Available via Academic Free License >= 2.1 OR the modified BSD license.
4 see: http://dojotoolkit.org/license for details
8 if(!dojo
._hasResource
["dojo._base.declare"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9 dojo
._hasResource
["dojo._base.declare"] = true;
10 dojo
.provide("dojo._base.declare");
12 dojo
.require("dojo._base.lang");
13 dojo
.require("dojo._base.array");
16 var d
= dojo
, mix
= d
._mixin
, op
= Object
.prototype, opts
= op
.toString
,
17 xtor
= new Function
, counter
= 0, cname
= "constructor";
19 function err(msg
){ throw new Error("declare: " + msg
); }
21 // C3 Method Resolution Order (see http://www.python.org/download/releases/2.3/mro/)
22 function c3mro(bases
){
23 var result
= [], roots
= [{cls
: 0, refs
: []}], nameMap
= {}, clsCount
= 1,
24 l
= bases
.length
, i
= 0, j
, lin
, base
, top
, proto
, rec
, name
, refs
;
26 // build a list of bases naming them if needed
30 err("mixin #" + i
+ " is unknown. Did you use dojo.require to pull it in?");
31 }else if(opts
.call(base
) != "[object Function]"){
32 err("mixin #" + i
+ " is not a callable constructor.");
34 lin
= base
._meta
? base
._meta
.bases
: [base
];
36 // add bases to the name map
37 for(j
= lin
.length
- 1; j
>= 0; --j
){
38 proto
= lin
[j
].prototype;
39 if(!proto
.hasOwnProperty("declaredClass")){
40 proto
.declaredClass
= "uniqName_" + (counter
++);
42 name
= proto
.declaredClass
;
43 if(!nameMap
.hasOwnProperty(name
)){
44 nameMap
[name
] = {count
: 0, refs
: [], cls
: lin
[j
]};
48 if(top
&& top
!== rec
){
55 roots
[0].refs
.push(top
);
58 // remove classes without external references recursively
63 // optimization: follow a single-linked chain
64 while(refs
= top
.refs
, refs
.length
== 1){
66 if(!top
|| --top
.count
){
67 // branch or end of chain => do not end to roots
76 for(i
= 0, l
= refs
.length
; i
< l
; ++i
){
85 err("can't build consistent linearization");
88 // calculate the superclass offset
91 base
._meta
&& base
=== result
[result
.length
- base
._meta
.bases
.length
] ?
92 base
._meta
.bases
.length
: 1 : 0;
97 function inherited(args
, a
, f
){
98 var name
, chains
, bases
, caller
, meta
, base
, proto
, opf
, pos
,
99 cache
= this._inherited
= this._inherited
|| {};
102 if(typeof args
== "string"){
109 caller
= args
.callee
;
110 name
= name
|| caller
.nom
;
112 err("can't deduce a name to call inherited()");
115 meta
= this.constructor._meta
;
121 if(cache
.c
!== caller
){
126 if(meta
.hidden
[name
] !== caller
){
128 chains
= meta
.chains
;
129 if(chains
&& typeof chains
[name
] == "string"){
130 err("calling chained method with inherited: " + name
);
135 proto
= base
.prototype;
136 if(meta
&& (proto
[name
] === caller
&& proto
.hasOwnProperty(name
) || meta
.hidden
[name
] === caller
)){
139 }while(base
= bases
[++pos
]); // intentional assignment
140 pos
= base
? pos
: -1;
146 proto
= base
.prototype;
147 if(base
._meta
&& proto
.hasOwnProperty(name
)){
152 proto
= base
.prototype;
154 if(f
&& (base
._meta
? proto
.hasOwnProperty(name
) : f
!== opf
)){
157 }while(base
= bases
[++pos
]); // intentional assignment
160 f
= base
&& f
|| op
[name
];
163 if(cache
.c
!== caller
){
166 meta
= bases
[0]._meta
;
167 if(meta
&& meta
.ctor
!== caller
){
169 chains
= meta
.chains
;
170 if(!chains
|| chains
.constructor !== "manual"){
171 err("calling chained constructor with inherited");
174 while(base
= bases
[++pos
]){ // intentional assignment
176 if(meta
&& meta
.ctor
=== caller
){
180 pos
= base
? pos
: -1;
184 while(base
= bases
[++pos
]){ // intentional assignment
186 f
= meta
? meta
.ctor
: base
;
194 // cache the found super method
198 // now we have the result
200 return a
=== true ? f
: f
.apply(this, a
|| args
);
202 // intentionally if a super method was not found
205 function getInherited(name
, args
){
206 if(typeof name
== "string"){
207 return this.inherited(name
, args
, true);
209 return this.inherited(name
, true);
212 // emulation of "instanceof"
213 function isInstanceOf(cls
){
214 var bases
= this.constructor._meta
.bases
;
215 for(var i
= 0, l
= bases
.length
; i
< l
; ++i
){
216 if(bases
[i
] === cls
){
220 return this instanceof cls
;
223 function mixOwn(target
, source
){
224 var name
, i
= 0, l
= d
._extraNames
.length
;
225 // add props adding metadata for incoming functions skipping a constructor
227 if(name
!= cname
&& source
.hasOwnProperty(name
)){
228 target
[name
] = source
[name
];
231 // process unenumerable methods on IE
233 name
= d
._extraNames
[i
];
234 if(name
!= cname
&& source
.hasOwnProperty(name
)){
235 target
[name
] = source
[name
];
240 // implementation of safe mixin function
241 function safeMixin(target
, source
){
242 var name
, t
, i
= 0, l
= d
._extraNames
.length
;
243 // add props adding metadata for incoming functions skipping a constructor
246 if((t
!== op
[name
] || !(name
in op
)) && name
!= cname
){
247 if(opts
.call(t
) == "[object Function]"){
248 // non-trivial function method => attach its name
254 // process unenumerable methods on IE
256 name
= d
._extraNames
[i
];
258 if((t
!== op
[name
] || !(name
in op
)) && name
!= cname
){
259 if(opts
.call(t
) == "[object Function]"){
260 // non-trivial function method => attach its name
269 function extend(source
){
270 safeMixin(this.prototype, source
);
274 // chained constructor compatible with the legacy dojo.declare()
275 function chainedConstructor(bases
, ctorSpecial
){
277 var a
= arguments
, args
= a
, a0
= a
[0], f
, i
, m
,
278 l
= bases
.length
, preArgs
;
280 if(!(this instanceof a
.callee
)){
281 // not called via new, so force it
285 //this._inherited = {};
286 // perform the shaman's rituals of the original dojo.declare()
287 // 1) call two types of the preamble
288 if(ctorSpecial
&& (a0
&& a0
.preamble
|| this.preamble
)){
290 preArgs
= new Array(bases
.length
);
291 // prepare parameters
294 // process the preamble of the 1st argument
299 a
= f
.apply(this, a
) || a
;
302 // process the preamble of this class
303 f
= bases
[i
].prototype;
304 f
= f
.hasOwnProperty("preamble") && f
.preamble
;
306 a
= f
.apply(this, a
) || a
;
308 // one peculiarity of the preamble:
309 // it is called if it is not needed,
310 // e.g., there is no constructor to call
311 // let's watch for the last constructor
312 // (see ticket #9795)
319 // 2) call all non-trivial constructors using prepared arguments
320 for(i
= l
- 1; i
>= 0; --i
){
325 f
.apply(this, preArgs
? preArgs
[i
] : a
);
328 // 3) continue the original ritual: call the postscript
337 // chained constructor compatible with the legacy dojo.declare()
338 function singleConstructor(ctor
, ctorSpecial
){
340 var a
= arguments
, t
= a
, a0
= a
[0], f
;
342 if(!(this instanceof a
.callee
)){
343 // not called via new, so force it
347 //this._inherited = {};
348 // perform the shaman's rituals of the original dojo.declare()
349 // 1) call two types of the preamble
353 // process the preamble of the 1st argument
356 t
= f
.apply(this, t
) || t
;
361 // process the preamble of this class
363 // one peculiarity of the preamble:
364 // it is called even if it is not needed,
365 // e.g., there is no constructor to call
366 // let's watch for the last constructor
367 // (see ticket #9795)
370 // 2) call a constructor
374 // 3) continue the original ritual: call the postscript
382 // plain vanilla constructor (can use inherited() to call its base constructor)
383 function simpleConstructor(bases
){
385 var a
= arguments
, i
= 0, f
, m
;
387 if(!(this instanceof a
.callee
)){
388 // not called via new, so force it
392 //this._inherited = {};
393 // perform the shaman's rituals of the original dojo.declare()
394 // 1) do not call the preamble
395 // 2) call the top constructor (it can use this.inherited())
396 for(; f
= bases
[i
]; ++i
){ // intentional assignment
404 // 3) call the postscript
412 function chain(name
, bases
, reversed
){
414 var b
, m
, f
, i
= 0, step
= 1;
416 i
= bases
.length
- 1;
419 for(; b
= bases
[i
]; i
+= step
){ // intentional assignment
421 f
= (m
? m
.hidden
: b
.prototype)[name
];
423 f
.apply(this, arguments
);
430 // return a new object that inherits from ctor.prototype but
431 // without actually running ctor on the object.
432 function forceNew(ctor
){
433 // create object with correct prototype using a do-nothing
435 xtor
.prototype = ctor
.prototype;
437 xtor
.prototype = null; // clean up
442 // just like 'new ctor()' except that the constructor and its arguments come
443 // from args, which must be an array or an arguments object
444 function applyNew(args
){
445 // create an object with ctor's prototype but without
446 // calling ctor on it.
447 var ctor
= args
.callee
, t
= forceNew(ctor
);
448 // execute the real constructor on the new object
453 d
.declare = function(className
, superclass
, props
){
455 if(typeof className
!= "string"){
457 superclass
= className
;
462 var proto
, i
, t
, ctor
, name
, bases
, chains
, mixins
= 1, parents
= superclass
;
465 if(opts
.call(superclass
) == "[object Array]"){
467 bases
= c3mro(superclass
);
469 mixins
= bases
.length
- t
;
470 superclass
= bases
[mixins
];
474 if(opts
.call(superclass
) == "[object Function]"){
475 t
= superclass
._meta
;
476 bases
= bases
.concat(t
? t
.bases
: superclass
);
478 err("base class is not a callable constructor.");
480 }else if(superclass
!== null){
481 err("unknown base class. Did you use dojo.require to pull it in?")
485 for(i
= mixins
- 1;; --i
){
486 proto
= forceNew(superclass
);
488 // stop if nothing to add (the last base)
493 (t
._meta
? mixOwn
: mix
)(proto
, t
.prototype);
494 // chain in new constructor
496 ctor
.superclass
= superclass
;
497 ctor
.prototype = proto
;
498 superclass
= proto
.constructor = ctor
;
503 // add all properties
504 safeMixin(proto
, props
);
506 t
= props
.constructor;
507 if(t
!== op
.constructor){
509 proto
.constructor = t
;
512 // collect chains and flags
513 for(i
= mixins
- 1; i
; --i
){ // intentional assignment
516 chains
= mix(chains
|| {}, t
.chains
);
519 if(proto
["-chains-"]){
520 chains
= mix(chains
|| {}, proto
["-chains-"]);
524 t
= !chains
|| !chains
.hasOwnProperty(cname
);
525 bases
[0] = ctor
= (chains
&& chains
.constructor === "manual") ? simpleConstructor(bases
) :
526 (bases
.length
== 1 ? singleConstructor(props
.constructor, t
) : chainedConstructor(bases
, t
));
528 // add meta information to the constructor
529 ctor
._meta
= {bases
: bases
, hidden
: props
, chains
: chains
,
530 parents
: parents
, ctor
: props
.constructor};
531 ctor
.superclass
= superclass
&& superclass
.prototype;
532 ctor
.extend
= extend
;
533 ctor
.prototype = proto
;
534 proto
.constructor = ctor
;
536 // add "standard" methods to the prototype
537 proto
.getInherited
= getInherited
;
538 proto
.inherited
= inherited
;
539 proto
.isInstanceOf
= isInstanceOf
;
541 // add name if specified
543 proto
.declaredClass
= className
;
544 d
.setObject(className
, ctor
);
547 // build chains and add them to the prototype
550 if(proto
[name
] && typeof chains
[name
] == "string" && name
!= cname
){
551 t
= proto
[name
] = chain(name
, bases
, chains
[name
] === "after");
556 // chained methods do not return values
557 // no need to chain "invisible" functions
559 return ctor
; // Function
562 d
.safeMixin
= safeMixin
;
565 dojo.declare = function(className, superclass, props){
567 // Create a feature-rich constructor from compact notation.
568 // className: String?:
569 // The optional name of the constructor (loosely, a "class")
570 // stored in the "declaredClass" property in the created prototype.
571 // It will be used as a global name for a created constructor.
572 // superclass: Function|Function[]:
573 // May be null, a Function, or an Array of Functions. This argument
574 // specifies a list of bases (the left-most one is the most deepest
577 // An object whose properties are copied to the created prototype.
578 // Add an instance-initialization function by making it a property
579 // named "constructor".
581 // New constructor function.
583 // Create a constructor using a compact notation for inheritance and
584 // prototype extension.
586 // Mixin ancestors provide a type of multiple inheritance.
587 // Prototypes of mixin ancestors are copied to the new class:
588 // changes to mixin prototypes will not affect classes to which
589 // they have been mixed in.
591 // Ancestors can be compound classes created by this version of
592 // dojo.declare. In complex cases all base classes are going to be
593 // linearized according to C3 MRO algorithm
594 // (see http://www.python.org/download/releases/2.3/mro/ for more
597 // "className" is cached in "declaredClass" property of the new class,
598 // if it was supplied. The immediate super class will be cached in
599 // "superclass" property of the new class.
601 // Methods in "props" will be copied and modified: "nom" property
602 // (the declared name of the method) will be added to all copied
603 // functions to help identify them for the internal machinery. Be
604 // very careful, while reusing methods: if you use the same
605 // function under different names, it can produce errors in some
608 // It is possible to use constructors created "manually" (without
609 // dojo.declare) as bases. They will be called as usual during the
610 // creation of an instance, their methods will be chained, and even
611 // called by "this.inherited()".
613 // Special property "-chains-" governs how to chain methods. It is
614 // a dictionary, which uses method names as keys, and hint strings
615 // as values. If a hint string is "after", this method will be
616 // called after methods of its base classes. If a hint string is
617 // "before", this method will be called before methods of its base
620 // If "constructor" is not mentioned in "-chains-" property, it will
621 // be chained using the legacy mode: using "after" chaining,
622 // calling preamble() method before each constructor, if available,
623 // and calling postscript() after all constructors were executed.
624 // If the hint is "after", it is chained as a regular method, but
625 // postscript() will be called after the chain of constructors.
626 // "constructor" cannot be chained "before", but it allows
627 // a special hint string: "manual", which means that constructors
628 // are not going to be chained in any way, and programmer will call
629 // them manually using this.inherited(). In the latter case
630 // postscript() will be called after the construction.
632 // All chaining hints are "inherited" from base classes and
633 // potentially can be overridden. Be very careful when overriding
634 // hints! Make sure that all chained methods can work in a proposed
635 // manner of chaining.
637 // Once a method was chained, it is impossible to unchain it. The
638 // only exception is "constructor". You don't need to define a
639 // method in order to supply a chaining hint.
641 // If a method is chained, it cannot use this.inherited() because
642 // all other methods in the hierarchy will be called automatically.
644 // Usually constructors and initializers of any kind are chained
645 // using "after" and destructors of any kind are chained as
646 // "before". Note that chaining assumes that chained methods do not
647 // return any value: any returned value will be discarded.
650 // | dojo.declare("my.classes.bar", my.classes.foo, {
651 // | // properties to be added to the class prototype
653 // | // initialization function
654 // | constructor: function(){
655 // | this.myComplicatedObject = new ReallyComplicatedObject();
657 // | // other functions
658 // | someMethod: function(){
664 // | var MyBase = dojo.declare(null, {
665 // | // constructor, properties, and methods go here
668 // | var MyClass1 = dojo.declare(MyBase, {
669 // | // constructor, properties, and methods go here
672 // | var MyClass2 = dojo.declare(MyBase, {
673 // | // constructor, properties, and methods go here
676 // | var MyDiamond = dojo.declare([MyClass1, MyClass2], {
677 // | // constructor, properties, and methods go here
682 // | var F = function(){ console.log("raw constructor"); };
683 // | F.prototype.method = function(){
684 // | console.log("raw method");
686 // | var A = dojo.declare(F, {
687 // | constructor: function(){
688 // | console.log("A.constructor");
690 // | method: function(){
691 // | console.log("before calling F.method...");
692 // | this.inherited(arguments);
693 // | console.log("...back in A");
696 // | new A().method();
698 // | // raw constructor
699 // | // A.constructor
700 // | // before calling F.method...
705 // | var A = dojo.declare(null, {
707 // | destroy: "before"
710 // | var B = dojo.declare(A, {
711 // | constructor: function(){
712 // | console.log("B.constructor");
714 // | destroy: function(){
715 // | console.log("B.destroy");
718 // | var C = dojo.declare(B, {
719 // | constructor: function(){
720 // | console.log("C.constructor");
722 // | destroy: function(){
723 // | console.log("C.destroy");
726 // | new C().destroy();
728 // | // B.constructor
729 // | // C.constructor
734 // | var A = dojo.declare(null, {
736 // | constructor: "manual"
739 // | var B = dojo.declare(A, {
740 // | constructor: function(){
742 // | // call the base constructor with new parameters
743 // | this.inherited(arguments, [1, 2, 3]);
749 // | var A = dojo.declare(null, {
754 // | console.log("A.m1");
757 // | console.log("A.m2");
760 // | var B = dojo.declare(A, {
765 // | console.log("B.m1");
768 // | console.log("B.m2");
771 // | var x = new B();
780 return new Function(); // Function
785 dojo.safeMixin = function(target, source){
787 // Mix in properties skipping a constructor and decorating functions
788 // like it is done by dojo.declare.
790 // Target object to accept new properties.
792 // Source object for new properties.
794 // This function is used to mix in properties like dojo._mixin does,
795 // but it skips a constructor property and decorates functions like
796 // dojo.declare does.
798 // It is meant to be used with classes and objects produced with
799 // dojo.declare. Functions mixed in with dojo.safeMixin can use
800 // this.inherited() like normal methods.
802 // This function is used to implement extend() method of a constructor
803 // produced with dojo.declare().
806 // | var A = dojo.declare(null, {
808 // | console.log("A.m1");
811 // | console.log("A.m2");
814 // | var B = dojo.declare(A, {
816 // | this.inherited(arguments);
817 // | console.log("B.m1");
822 // | this.inherited(arguments);
823 // | console.log("B.m2");
826 // | var x = new B();
827 // | dojo.safeMixin(x, {
829 // | this.inherited(arguments);
830 // | console.log("X.m1");
833 // | this.inherited(arguments);
834 // | console.log("X.m2");
846 Object.inherited = function(name, args, newArgs){
848 // Calls a super method.
850 // The optional method name. Should be the same as the caller's
851 // name. Usually "name" is specified in complex dynamic cases, when
852 // the calling method was dynamically added, undecorated by
853 // dojo.declare, and it cannot be determined.
855 // The caller supply this argument, which should be the original
858 // If "true", the found function will be returned without
860 // If Array, it will be used to call a super method. Otherwise
861 // "args" will be used.
863 // Whatever is returned by a super method, or a super method itself,
864 // if "true" was specified as newArgs.
866 // This method is used inside method of classes produced with
867 // dojo.declare to call a super method (next in the chain). It is
868 // used for manually controlled chaining. Consider using the regular
869 // chaining, because it is faster. Use "this.inherited()" only in
872 // This method cannot me called from automatically chained
873 // constructors including the case of a special (legacy)
874 // constructor chaining. It cannot be called from chained methods.
876 // If "this.inherited()" cannot find the next-in-chain method, it
877 // does nothing and returns "undefined". The last method in chain
878 // can be a default method implemented in Object, which will be
881 // If "name" is specified, it is assumed that the method that
882 // received "args" is the parent method for this call. It is looked
883 // up in the chain list and if it is found the next-in-chain method
884 // is called. If it is not found, the first-in-chain method is
887 // If "name" is not specified, it will be derived from the calling
888 // method (using a methoid property "nom").
891 // | var B = dojo.declare(A, {
892 // | method1: function(a, b, c){
893 // | this.inherited(arguments);
895 // | method2: function(a, b){
896 // | return this.inherited(arguments, [a + b]);
899 // | // next method is not in the chain list because it is added
900 // | // manually after the class was created.
901 // | B.prototype.method3 = function(){
902 // | console.log("This is a dynamically-added method.");
903 // | this.inherited("method3", arguments);
906 // | var B = dojo.declare(A, {
907 // | method: function(a, b){
908 // | var super = this.inherited(arguments, true);
911 // | console.log("there is no super method");
914 // | return super.apply(this, arguments);
922 Object.getInherited = function(name, args){
924 // Returns a super method.
926 // The optional method name. Should be the same as the caller's
927 // name. Usually "name" is specified in complex dynamic cases, when
928 // the calling method was dynamically added, undecorated by
929 // dojo.declare, and it cannot be determined.
931 // The caller supply this argument, which should be the original
934 // Returns a super method (Function) or "undefined".
936 // This method is a convenience method for "this.inherited()".
937 // It uses the same algorithm but instead of executing a super
938 // method, it returns it, or "undefined" if not found.
941 // | var B = dojo.declare(A, {
942 // | method: function(a, b){
943 // | var super = this.getInherited(arguments);
946 // | console.log("there is no super method");
949 // | return super.apply(this, arguments);
957 Object.isInstanceOf = function(cls){
959 // Checks the inheritance chain to see if it is inherited from this
962 // Class constructor.
964 // "true", if this object is inherited from this class, "false"
967 // This method is used with instances of classes produced with
968 // dojo.declare to determine of they support a certain interface or
969 // not. It models "instanceof" operator.
972 // | var A = dojo.declare(null, {
973 // | // constructor, properties, and methods go here
976 // | var B = dojo.declare(null, {
977 // | // constructor, properties, and methods go here
980 // | var C = dojo.declare([A, B], {
981 // | // constructor, properties, and methods go here
984 // | var D = dojo.declare(A, {
985 // | // constructor, properties, and methods go here
989 // | var a = new A(), b = new B(), c = new C(), d = new D();
991 // | console.log(a.isInstanceOf(A)); // true
992 // | console.log(b.isInstanceOf(A)); // false
993 // | console.log(c.isInstanceOf(A)); // true
994 // | console.log(d.isInstanceOf(A)); // true
996 // | console.log(a.isInstanceOf(B)); // false
997 // | console.log(b.isInstanceOf(B)); // true
998 // | console.log(c.isInstanceOf(B)); // true
999 // | console.log(d.isInstanceOf(B)); // false
1001 // | console.log(a.isInstanceOf(C)); // false
1002 // | console.log(b.isInstanceOf(C)); // false
1003 // | console.log(c.isInstanceOf(C)); // true
1004 // | console.log(d.isInstanceOf(C)); // false
1006 // | console.log(a.isInstanceOf(D)); // false
1007 // | console.log(b.isInstanceOf(D)); // false
1008 // | console.log(c.isInstanceOf(D)); // false
1009 // | console.log(d.isInstanceOf(D)); // true
1010 return {}; // Object
1015 Object.extend = function(source){
1017 // Adds all properties and methods of source to constructor's
1018 // prototype, making them available to all instances created with
1019 // constructor. This method is specific to constructors created with
1022 // Source object which properties are going to be copied to the
1023 // constructor's prototype.
1025 // Adds source properties to the constructor's prototype. It can
1026 // override existing properties.
1028 // This method is similar to dojo.extend function, but it is specific
1029 // to constructors produced by dojo.declare. It is implemented
1030 // using dojo.safeMixin, and it skips a constructor property,
1031 // and properly decorates copied functions.
1034 // | var A = dojo.declare(null, {
1035 // | m1: function(){},
1036 // | s1: "Popokatepetl"
1039 // | m1: function(){},
1040 // | m2: function(){},