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     compileSuper : function(func, name, id){
 39         //make the func to a string
 40         var str = func.toString();
 41         //find parameters
 42         var pstart = str.indexOf('('), pend = str.indexOf(')');
 43         var params = str.substring(pstart+1, pend);
 44         params = params.trim();
 45 
 46         //find function body
 47         var bstart = str.indexOf('{'), bend = str.lastIndexOf('}');
 48         var str = str.substring(bstart+1, bend);
 49 
 50         //now we have the content of the function, replace this._super
 51         //find this._super
 52         while(str.indexOf('this._super')!= -1)
 53         {
 54             var sp = str.indexOf('this._super');
 55             //find the first '(' from this._super)
 56             var bp = str.indexOf('(', sp);
 57 
 58             //find if we are passing params to super
 59             var bbp = str.indexOf(')', bp);
 60             var superParams = str.substring(bp+1, bbp);
 61             superParams = superParams.trim();
 62             var coma = superParams? ',':'';
 63 
 64             //replace this._super
 65             str = str.substring(0, sp)+  'ClassManager['+id+'].'+name+'.call(this'+coma+str.substring(bp+1);
 66         }
 67         return Function(params, str);
 68     },
 69 
 70     getNewID : function(){
 71         return this.id++;
 72     },
 73 
 74     getNewInstanceId : function(){
 75         return this.instanceId++;
 76     }
 77 };
 78 ClassManager.compileSuper.ClassManager = ClassManager;
 79 
 80 /* Managed JavaScript Inheritance
 81  * Based on John Resig's Simple JavaScript Inheritance http://ejohn.org/blog/simple-javascript-inheritance/
 82  * MIT Licensed.
 83  */
 84 (function () {
 85     var fnTest = /\b_super\b/;
 86     var config = cc.game.config;
 87     var releaseMode = config[cc.game.CONFIG_KEY.classReleaseMode];
 88     if(releaseMode) {
 89         console.log("release Mode");
 90     }
 91 
 92     /**
 93      * The base Class implementation (does nothing)
 94      * @class
 95      */
 96     cc.Class = function () {
 97     };
 98 
 99     /**
100      * Create a new Class that inherits from this Class
101      * @static
102      * @param {object} props
103      * @return {function}
104      */
105     cc.Class.extend = function (props) {
106         var _super = this.prototype;
107 
108         // Instantiate a base Class (but only create the instance,
109         // don't run the init constructor)
110         var prototype = Object.create(_super);
111 
112         var classId = ClassManager.getNewID();
113         ClassManager[classId] = _super;
114         // Copy the properties over onto the new prototype. We make function
115         // properties non-eumerable as this makes typeof === 'function' check
116         // unneccessary in the for...in loop used 1) for generating Class()
117         // 2) for cc.clone and perhaps more. It is also required to make
118         // these function properties cacheable in Carakan.
119         var desc = { writable: true, enumerable: false, configurable: true };
120 
121 	    prototype.__instanceId = null;
122 
123 	    // The dummy Class constructor
124 	    function Class() {
125 		    this.__instanceId = ClassManager.getNewInstanceId();
126 		    // All construction is actually done in the init method
127 		    if (this.ctor)
128 			    this.ctor.apply(this, arguments);
129 	    }
130 
131 	    Class.id = classId;
132 	    // desc = { writable: true, enumerable: false, configurable: true,
133 	    //          value: XXX }; Again, we make this non-enumerable.
134 	    desc.value = classId;
135 	    Object.defineProperty(prototype, '__pid', desc);
136 
137 	    // Populate our constructed prototype object
138 	    Class.prototype = prototype;
139 
140 	    // Enforce the constructor to be what we expect
141 	    desc.value = Class;
142 	    Object.defineProperty(Class.prototype, 'constructor', desc);
143 
144 	    // Copy getter/setter
145 	    this.__getters__ && (Class.__getters__ = cc.clone(this.__getters__));
146 	    this.__setters__ && (Class.__setters__ = cc.clone(this.__setters__));
147 
148         for(var idx = 0, li = arguments.length; idx < li; ++idx) {
149             var prop = arguments[idx];
150             for (var name in prop) {
151                 var isFunc = (typeof prop[name] === "function");
152                 var override = (typeof _super[name] === "function");
153                 var hasSuperCall = fnTest.test(prop[name]);
154 
155                 if (releaseMode && isFunc && override && hasSuperCall) {
156                     desc.value = ClassManager.compileSuper(prop[name], name, classId);
157                     Object.defineProperty(prototype, name, desc);
158                 } else if (isFunc && override && hasSuperCall) {
159                     desc.value = (function (name, fn) {
160                         return function () {
161                             var tmp = this._super;
162 
163                             // Add a new ._super() method that is the same method
164                             // but on the super-Class
165                             this._super = _super[name];
166 
167                             // The method only need to be bound temporarily, so we
168                             // remove it when we're done executing
169                             var ret = fn.apply(this, arguments);
170                             this._super = tmp;
171 
172                             return ret;
173                         };
174                     })(name, prop[name]);
175                     Object.defineProperty(prototype, name, desc);
176                 } else if (isFunc) {
177                     desc.value = prop[name];
178                     Object.defineProperty(prototype, name, desc);
179                 } else {
180                     prototype[name] = prop[name];
181                 }
182 
183                 if (isFunc) {
184                     // Override registered getter/setter
185                     var getter, setter, propertyName;
186                     if (this.__getters__ && this.__getters__[name]) {
187                         propertyName = this.__getters__[name];
188                         for (var i in this.__setters__) {
189                             if (this.__setters__[i] == propertyName) {
190                                 setter = i;
191                                 break;
192                             }
193                         }
194                         cc.defineGetterSetter(prototype, propertyName, prop[name], prop[setter] ? prop[setter] : prototype[setter], name, setter);
195                     }
196                     if (this.__setters__ && this.__setters__[name]) {
197                         propertyName = this.__setters__[name];
198                         for (var i in this.__getters__) {
199                             if (this.__getters__[i] == propertyName) {
200                                 getter = i;
201                                 break;
202                             }
203                         }
204                         cc.defineGetterSetter(prototype, propertyName, prop[getter] ? prop[getter] : prototype[getter], prop[name], getter, name);
205                     }
206                 }
207             }
208         }
209 
210         // And make this Class extendable
211         Class.extend = cc.Class.extend;
212 
213         //add implementation method
214         Class.implement = function (prop) {
215             for (var name in prop) {
216                 prototype[name] = prop[name];
217             }
218         };
219         return Class;
220     };
221 })();
222 
223 /**
224  * Common getter setter configuration function
225  * @function
226  * @param {Object}   proto      A class prototype or an object to config<br/>
227  * @param {String}   prop       Property name
228  * @param {function} getter     Getter function for the property
229  * @param {function} setter     Setter function for the property
230  * @param {String}   getterName Name of getter function for the property
231  * @param {String}   setterName Name of setter function for the property
232  */
233 cc.defineGetterSetter = function (proto, prop, getter, setter, getterName, setterName){
234     if (proto.__defineGetter__) {
235         getter && proto.__defineGetter__(prop, getter);
236         setter && proto.__defineSetter__(prop, setter);
237     } else if (Object.defineProperty) {
238         var desc = { enumerable: false, configurable: true };
239         getter && (desc.get = getter);
240         setter && (desc.set = setter);
241         Object.defineProperty(proto, prop, desc);
242     } else {
243         throw new Error("browser does not support getters");
244     }
245 
246     if(!getterName && !setterName) {
247         // Lookup getter/setter function
248         var hasGetter = (getter != null), hasSetter = (setter != undefined), props = Object.getOwnPropertyNames(proto);
249         for (var i = 0; i < props.length; i++) {
250             var name = props[i];
251 
252             if( (proto.__lookupGetter__ ? proto.__lookupGetter__(name)
253                                         : Object.getOwnPropertyDescriptor(proto, name))
254                 || typeof proto[name] !== "function" )
255                 continue;
256 
257             var func = proto[name];
258             if (hasGetter && func === getter) {
259                 getterName = name;
260                 if(!hasSetter || setterName) break;
261             }
262             if (hasSetter && func === setter) {
263                 setterName = name;
264                 if(!hasGetter || getterName) break;
265             }
266         }
267     }
268 
269     // Found getter/setter
270     var ctor = proto.constructor;
271     if (getterName) {
272         if (!ctor.__getters__) {
273             ctor.__getters__ = {};
274         }
275         ctor.__getters__[getterName] = prop;
276     }
277     if (setterName) {
278         if (!ctor.__setters__) {
279             ctor.__setters__ = {};
280         }
281         ctor.__setters__[setterName] = prop;
282     }
283 };
284 
285 /**
286  * Create a new object and copy all properties in an exist object to the new object
287  * @function
288  * @param {object|Array} obj The source object
289  * @return {Array|object} The created object
290  */
291 cc.clone = function (obj) {
292     // Cloning is better if the new object is having the same prototype chain
293     // as the copied obj (or otherwise, the cloned object is certainly going to
294     // have a different hidden class). Play with C1/C2 of the
295     // PerformanceVirtualMachineTests suite to see how this makes an impact
296     // under extreme conditions.
297     //
298     // Object.create(Object.getPrototypeOf(obj)) doesn't work well because the
299     // prototype lacks a link to the constructor (Carakan, V8) so the new
300     // object wouldn't have the hidden class that's associated with the
301     // constructor (also, for whatever reasons, utilizing
302     // Object.create(Object.getPrototypeOf(obj)) + Object.defineProperty is even
303     // slower than the original in V8). Therefore, we call the constructor, but
304     // there is a big caveat - it is possible that the this.init() in the
305     // constructor would throw with no argument. It is also possible that a
306     // derived class forgets to set "constructor" on the prototype. We ignore
307     // these possibities for and the ultimate solution is a standardized
308     // Object.clone(<object>).
309     var newObj = (obj.constructor) ? new obj.constructor : {};
310 
311     // Assuming that the constuctor above initialized all properies on obj, the
312     // following keyed assignments won't turn newObj into dictionary mode
313     // becasue they're not *appending new properties* but *assigning existing
314     // ones* (note that appending indexed properties is another story). See
315     // CCClass.js for a link to the devils when the assumption fails.
316     for (var key in obj) {
317         var copy = obj[key];
318         // Beware that typeof null == "object" !
319         if (((typeof copy) == "object") && copy &&
320             !(copy instanceof cc.Node) && !(copy instanceof HTMLElement)) {
321             newObj[key] = cc.clone(copy);
322         } else {
323             newObj[key] = copy;
324         }
325     }
326     return newObj;
327 };
328 
329