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