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 */ 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 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