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 /* Managed JavaScript Inheritance
 27  * Based on John Resig's Simple JavaScript Inheritance http://ejohn.org/blog/simple-javascript-inheritance/
 28  * MIT Licensed.
 29  */
 30 
 31 /**
 32  * @namespace
 33  * @name cc
 34  */
 35 var cc = cc || {};
 36 
 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 (function () {
 86     var fnTest = /\b_super\b/;
 87     var config = cc.game.config;
 88     var releaseMode = config[cc.game.CONFIG_KEY.classReleaseMode];
 89     if(releaseMode) {
 90         console.log("release Mode");
 91     }
 92 
 93     /**
 94      * The base Class implementation (does nothing)
 95      * @class
 96      */
 97     cc.Class = function () {
 98     };
 99 
100     /**
101      * Create a new Class that inherits from this Class
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     Function.prototype.bind = Function.prototype.bind || function (bind) {
223         var self = this;
224         return function () {
225             var args = Array.prototype.slice.call(arguments);
226             return self.apply(bind || null, args);
227         };
228     };
229 })();
230 
231 /**
232  * Common getter setter configuration function
233  * @function
234  * @param {Object}   proto      A class prototype or an object to config<br/>
235  * @param {String}   prop       Property name
236  * @param {function} getter     Getter function for the property
237  * @param {function} setter     Setter function for the property
238  * @param {String}   getterName Name of getter function for the property
239  * @param {String}   setterName Name of setter function for the property
240  */
241 cc.defineGetterSetter = function (proto, prop, getter, setter, getterName, setterName){
242     if (proto.__defineGetter__) {
243         getter && proto.__defineGetter__(prop, getter);
244         setter && proto.__defineSetter__(prop, setter);
245     } else if (Object.defineProperty) {
246         var desc = { enumerable: false, configurable: true };
247         getter && (desc.get = getter);
248         setter && (desc.set = setter);
249         Object.defineProperty(proto, prop, desc);
250     } else {
251         throw new Error("browser does not support getters");
252     }
253 
254     if(!getterName && !setterName) {
255         // Lookup getter/setter function
256         var hasGetter = (getter != null), hasSetter = (setter != undefined), props = Object.getOwnPropertyNames(proto);
257         for (var i = 0; i < props.length; i++) {
258             var name = props[i];
259 
260             if( (proto.__lookupGetter__ ? proto.__lookupGetter__(name)
261                                         : Object.getOwnPropertyDescriptor(proto, name))
262                 || typeof proto[name] !== "function" )
263                 continue;
264 
265             var func = proto[name];
266             if (hasGetter && func === getter) {
267                 getterName = name;
268                 if(!hasSetter || setterName) break;
269             }
270             if (hasSetter && func === setter) {
271                 setterName = name;
272                 if(!hasGetter || getterName) break;
273             }
274         }
275     }
276 
277     // Found getter/setter
278     var ctor = proto.constructor;
279     if (getterName) {
280         if (!ctor.__getters__) {
281             ctor.__getters__ = {};
282         }
283         ctor.__getters__[getterName] = prop;
284     }
285     if (setterName) {
286         if (!ctor.__setters__) {
287             ctor.__setters__ = {};
288         }
289         ctor.__setters__[setterName] = prop;
290     }
291 };
292 
293 /**
294  * copy an new object
295  * @function
296  * @param {object|Array} obj source object
297  * @return {Array|object}
298  */
299 cc.clone = function (obj) {
300     // Cloning is better if the new object is having the same prototype chain
301     // as the copied obj (or otherwise, the cloned object is certainly going to
302     // have a different hidden class). Play with C1/C2 of the
303     // PerformanceVirtualMachineTests suite to see how this makes an impact
304     // under extreme conditions.
305     //
306     // Object.create(Object.getPrototypeOf(obj)) doesn't work well because the
307     // prototype lacks a link to the constructor (Carakan, V8) so the new
308     // object wouldn't have the hidden class that's associated with the
309     // constructor (also, for whatever reasons, utilizing
310     // Object.create(Object.getPrototypeOf(obj)) + Object.defineProperty is even
311     // slower than the original in V8). Therefore, we call the constructor, but
312     // there is a big caveat - it is possible that the this.init() in the
313     // constructor would throw with no argument. It is also possible that a
314     // derived class forgets to set "constructor" on the prototype. We ignore
315     // these possibities for and the ultimate solution is a standardized
316     // Object.clone(<object>).
317     var newObj = (obj.constructor) ? new obj.constructor : {};
318 
319     // Assuming that the constuctor above initialized all properies on obj, the
320     // following keyed assignments won't turn newObj into dictionary mode
321     // becasue they're not *appending new properties* but *assigning existing
322     // ones* (note that appending indexed properties is another story). See
323     // CCClass.js for a link to the devils when the assumption fails.
324     for (var key in obj) {
325         var copy = obj[key];
326         // Beware that typeof null == "object" !
327         if (((typeof copy) == "object") && copy &&
328             !(copy instanceof cc.Node) && !(copy instanceof HTMLElement)) {
329             newObj[key] = cc.clone(copy);
330         } else {
331             newObj[key] = copy;
332         }
333     }
334     return newObj;
335 };
336 
337