1 /****************************************************************************
  2  Copyright (c) 2008-2010 Ricardo Quesada
  3  Copyright (c) 2011-2012 cocos2d-x.org
  4  Copyright (c) 2013-2014 Chukong Technologies Inc.
  5 
  6  http://www.cocos2d-x.org
  7 
  8  Permission is hereby granted, free of charge, to any person obtaining a copy
  9  of this software and associated documentation files (the "Software"), to deal
 10  in the Software without restriction, including without limitation the rights
 11  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12  copies of the Software, and to permit persons to whom the Software is
 13  furnished to do so, subject to the following conditions:
 14 
 15  The above copyright notice and this permission notice shall be included in
 16  all copies or substantial portions of the Software.
 17 
 18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 24  THE SOFTWARE.
 25  ****************************************************************************/
 26 
 27 var cc = cc || {};
 28 
 29 /**
 30  * @namespace
 31  * @name ClassManager
 32  */
 33 var ClassManager = {
 34     id : (0|(Math.random()*998)),
 35 
 36     instanceId : (0|(Math.random()*998)),
 37 
 38     getNewID : function(){
 39         return this.id++;
 40     },
 41 
 42     getNewInstanceId : function(){
 43         return this.instanceId++;
 44     }
 45 };
 46 
 47 /* Managed JavaScript Inheritance
 48  * Based on John Resig's Simple JavaScript Inheritance http://ejohn.org/blog/simple-javascript-inheritance/
 49  * MIT Licensed.
 50  */
 51 (function () {
 52     var fnTest = /\b_super\b/;
 53 
 54     /**
 55      * The base Class implementation (does nothing)
 56      * @class
 57      */
 58     cc.Class = function () {
 59     };
 60 
 61     /**
 62      * Create a new Class that inherits from this Class
 63      * @static
 64      * @param {object} props
 65      * @return {function}
 66      */
 67     cc.Class.extend = function (props) {
 68         var _super = this.prototype;
 69 
 70         // Instantiate a base Class (but only create the instance,
 71         // don't run the init constructor)
 72         var prototype = Object.create(_super);
 73 
 74         var classId = ClassManager.getNewID();
 75         ClassManager[classId] = _super;
 76         // Copy the properties over onto the new prototype. We make function
 77         // properties non-eumerable as this makes typeof === 'function' check
 78         // unnecessary in the for...in loop used 1) for generating Class()
 79         // 2) for cc.clone and perhaps more. It is also required to make
 80         // these function properties cacheable in Carakan.
 81         var desc = { writable: true, enumerable: false, configurable: true };
 82 
 83 	    prototype.__instanceId = null;
 84 
 85 	    // The dummy Class constructor
 86 	    function Class() {
 87 		    this.__instanceId = ClassManager.getNewInstanceId();
 88 		    // All construction is actually done in the init method
 89 		    if (this.ctor)
 90 			    this.ctor.apply(this, arguments);
 91 	    }
 92 
 93 	    Class.id = classId;
 94 	    // desc = { writable: true, enumerable: false, configurable: true,
 95 	    //          value: XXX }; Again, we make this non-enumerable.
 96 	    desc.value = classId;
 97 	    Object.defineProperty(prototype, '__pid', desc);
 98 
 99 	    // Populate our constructed prototype object
100 	    Class.prototype = prototype;
101 
102 	    // Enforce the constructor to be what we expect
103 	    desc.value = Class;
104 	    Object.defineProperty(Class.prototype, 'constructor', desc);
105 
106 	    // Copy getter/setter
107 	    this.__getters__ && (Class.__getters__ = cc.clone(this.__getters__));
108 	    this.__setters__ && (Class.__setters__ = cc.clone(this.__setters__));
109 
110         for(var idx = 0, li = arguments.length; idx < li; ++idx) {
111             var prop = arguments[idx];
112             for (var name in prop) {
113                 var isFunc = (typeof prop[name] === "function");
114                 var override = (typeof _super[name] === "function");
115                 var hasSuperCall = fnTest.test(prop[name]);
116 
117                 if (isFunc && override && hasSuperCall) {
118                     desc.value = (function (name, fn) {
119                         return function () {
120                             var tmp = this._super;
121 
122                             // Add a new ._super() method that is the same method
123                             // but on the super-Class
124                             this._super = _super[name];
125 
126                             // The method only need to be bound temporarily, so we
127                             // remove it when we're done executing
128                             var ret = fn.apply(this, arguments);
129                             this._super = tmp;
130 
131                             return ret;
132                         };
133                     })(name, prop[name]);
134                     Object.defineProperty(prototype, name, desc);
135                 } else if (isFunc) {
136                     desc.value = prop[name];
137                     Object.defineProperty(prototype, name, desc);
138                 } else {
139                     prototype[name] = prop[name];
140                 }
141 
142                 if (isFunc) {
143                     // Override registered getter/setter
144                     var getter, setter, propertyName;
145                     if (this.__getters__ && this.__getters__[name]) {
146                         propertyName = this.__getters__[name];
147                         for (var i in this.__setters__) {
148                             if (this.__setters__[i] === propertyName) {
149                                 setter = i;
150                                 break;
151                             }
152                         }
153                         cc.defineGetterSetter(prototype, propertyName, prop[name], prop[setter] ? prop[setter] : prototype[setter], name, setter);
154                     }
155                     if (this.__setters__ && this.__setters__[name]) {
156                         propertyName = this.__setters__[name];
157                         for (var i in this.__getters__) {
158                             if (this.__getters__[i] === propertyName) {
159                                 getter = i;
160                                 break;
161                             }
162                         }
163                         cc.defineGetterSetter(prototype, propertyName, prop[getter] ? prop[getter] : prototype[getter], prop[name], getter, name);
164                     }
165                 }
166             }
167         }
168 
169         // And make this Class extendable
170         Class.extend = cc.Class.extend;
171 
172         //add implementation method
173         Class.implement = function (prop) {
174             for (var name in prop) {
175                 prototype[name] = prop[name];
176             }
177         };
178         return Class;
179     };
180 })();
181 
182 /**
183  * Common getter setter configuration function
184  * @function
185  * @param {Object}   proto      A class prototype or an object to config<br/>
186  * @param {String}   prop       Property name
187  * @param {function} getter     Getter function for the property
188  * @param {function} setter     Setter function for the property
189  * @param {String}   getterName Name of getter function for the property
190  * @param {String}   setterName Name of setter function for the property
191  */
192 cc.defineGetterSetter = function (proto, prop, getter, setter, getterName, setterName){
193     if (proto.__defineGetter__) {
194         getter && proto.__defineGetter__(prop, getter);
195         setter && proto.__defineSetter__(prop, setter);
196     } else if (Object.defineProperty) {
197         var desc = { enumerable: false, configurable: true };
198         getter && (desc.get = getter);
199         setter && (desc.set = setter);
200         Object.defineProperty(proto, prop, desc);
201     } else {
202         throw new Error("browser does not support getters");
203     }
204 
205     if(!getterName && !setterName) {
206         // Lookup getter/setter function
207         var hasGetter = (getter != null), hasSetter = (setter != undefined), props = Object.getOwnPropertyNames(proto);
208         for (var i = 0; i < props.length; i++) {
209             var name = props[i];
210 
211             if( (proto.__lookupGetter__ ? proto.__lookupGetter__(name)
212                                         : Object.getOwnPropertyDescriptor(proto, name))
213                 || typeof proto[name] !== "function" )
214                 continue;
215 
216             var func = proto[name];
217             if (hasGetter && func === getter) {
218                 getterName = name;
219                 if(!hasSetter || setterName) break;
220             }
221             if (hasSetter && func === setter) {
222                 setterName = name;
223                 if(!hasGetter || getterName) break;
224             }
225         }
226     }
227 
228     // Found getter/setter
229     var ctor = proto.constructor;
230     if (getterName) {
231         if (!ctor.__getters__) {
232             ctor.__getters__ = {};
233         }
234         ctor.__getters__[getterName] = prop;
235     }
236     if (setterName) {
237         if (!ctor.__setters__) {
238             ctor.__setters__ = {};
239         }
240         ctor.__setters__[setterName] = prop;
241     }
242 };
243 
244 /**
245  * Create a new object and copy all properties in an exist object to the new object
246  * @function
247  * @param {object|Array} obj The source object
248  * @return {Array|object} The created object
249  */
250 cc.clone = function (obj) {
251     // Cloning is better if the new object is having the same prototype chain
252     // as the copied obj (or otherwise, the cloned object is certainly going to
253     // have a different hidden class). Play with C1/C2 of the
254     // PerformanceVirtualMachineTests suite to see how this makes an impact
255     // under extreme conditions.
256     //
257     // Object.create(Object.getPrototypeOf(obj)) doesn't work well because the
258     // prototype lacks a link to the constructor (Carakan, V8) so the new
259     // object wouldn't have the hidden class that's associated with the
260     // constructor (also, for whatever reasons, utilizing
261     // Object.create(Object.getPrototypeOf(obj)) + Object.defineProperty is even
262     // slower than the original in V8). Therefore, we call the constructor, but
263     // there is a big caveat - it is possible that the this.init() in the
264     // constructor would throw with no argument. It is also possible that a
265     // derived class forgets to set "constructor" on the prototype. We ignore
266     // these possibities for and the ultimate solution is a standardized
267     // Object.clone(<object>).
268     var newObj = (obj.constructor) ? new obj.constructor : {};
269 
270     // Assuming that the constuctor above initialized all properies on obj, the
271     // following keyed assignments won't turn newObj into dictionary mode
272     // because they're not *appending new properties* but *assigning existing
273     // ones* (note that appending indexed properties is another story). See
274     // CCClass.js for a link to the devils when the assumption fails.
275     for (var key in obj) {
276         var copy = obj[key];
277         // Beware that typeof null == "object" !
278         if (((typeof copy) === "object") && copy &&
279             !(copy instanceof cc.Node) && !(copy instanceof HTMLElement)) {
280             newObj[key] = cc.clone(copy);
281         } else {
282             newObj[key] = copy;
283         }
284     }
285     return newObj;
286 };
287 
288 cc.inject = function(srcPrototype, destPrototype){
289     for(var key in srcPrototype)
290         destPrototype[key] = srcPrototype[key];
291 };
292 
293