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