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