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 var cc = cc || {}; 28 29 /** 30 * @namespace 31 * @name ClassManager 32 */ 33 var ClassManager = { 34 id : (0|(Math.random()*998)), 35 36 instanceId : (0|(Math.random()*998)), 37 38 compileSuper : function(func, name, id){ 39 //make the func to a string 40 var str = func.toString(); 41 //find parameters 42 var pstart = str.indexOf('('), pend = str.indexOf(')'); 43 var params = str.substring(pstart+1, pend); 44 params = params.trim(); 45 46 //find function body 47 var bstart = str.indexOf('{'), bend = str.lastIndexOf('}'); 48 var str = str.substring(bstart+1, bend); 49 50 //now we have the content of the function, replace this._super 51 //find this._super 52 while(str.indexOf('this._super') !== -1) 53 { 54 var sp = str.indexOf('this._super'); 55 //find the first '(' from this._super) 56 var bp = str.indexOf('(', sp); 57 58 //find if we are passing params to super 59 var bbp = str.indexOf(')', bp); 60 var superParams = str.substring(bp+1, bbp); 61 superParams = superParams.trim(); 62 var coma = superParams? ',':''; 63 64 //replace this._super 65 str = str.substring(0, sp)+ 'ClassManager['+id+'].'+name+'.call(this'+coma+str.substring(bp+1); 66 } 67 return Function(params, str); 68 }, 69 70 getNewID : function(){ 71 return this.id++; 72 }, 73 74 getNewInstanceId : function(){ 75 return this.instanceId++; 76 } 77 }; 78 ClassManager.compileSuper.ClassManager = ClassManager; 79 80 /* Managed JavaScript Inheritance 81 * Based on John Resig's Simple JavaScript Inheritance http://ejohn.org/blog/simple-javascript-inheritance/ 82 * MIT Licensed. 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 * @static 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 223 /** 224 * Common getter setter configuration function 225 * @function 226 * @param {Object} proto A class prototype or an object to config<br/> 227 * @param {String} prop Property name 228 * @param {function} getter Getter function for the property 229 * @param {function} setter Setter function for the property 230 * @param {String} getterName Name of getter function for the property 231 * @param {String} setterName Name of setter function for the property 232 */ 233 cc.defineGetterSetter = function (proto, prop, getter, setter, getterName, setterName){ 234 if (proto.__defineGetter__) { 235 getter && proto.__defineGetter__(prop, getter); 236 setter && proto.__defineSetter__(prop, setter); 237 } else if (Object.defineProperty) { 238 var desc = { enumerable: false, configurable: true }; 239 getter && (desc.get = getter); 240 setter && (desc.set = setter); 241 Object.defineProperty(proto, prop, desc); 242 } else { 243 throw new Error("browser does not support getters"); 244 } 245 246 if(!getterName && !setterName) { 247 // Lookup getter/setter function 248 var hasGetter = (getter != null), hasSetter = (setter != undefined), props = Object.getOwnPropertyNames(proto); 249 for (var i = 0; i < props.length; i++) { 250 var name = props[i]; 251 252 if( (proto.__lookupGetter__ ? proto.__lookupGetter__(name) 253 : Object.getOwnPropertyDescriptor(proto, name)) 254 || typeof proto[name] !== "function" ) 255 continue; 256 257 var func = proto[name]; 258 if (hasGetter && func === getter) { 259 getterName = name; 260 if(!hasSetter || setterName) break; 261 } 262 if (hasSetter && func === setter) { 263 setterName = name; 264 if(!hasGetter || getterName) break; 265 } 266 } 267 } 268 269 // Found getter/setter 270 var ctor = proto.constructor; 271 if (getterName) { 272 if (!ctor.__getters__) { 273 ctor.__getters__ = {}; 274 } 275 ctor.__getters__[getterName] = prop; 276 } 277 if (setterName) { 278 if (!ctor.__setters__) { 279 ctor.__setters__ = {}; 280 } 281 ctor.__setters__[setterName] = prop; 282 } 283 }; 284 285 /** 286 * Create a new object and copy all properties in an exist object to the new object 287 * @function 288 * @param {object|Array} obj The source object 289 * @return {Array|object} The created object 290 */ 291 cc.clone = function (obj) { 292 // Cloning is better if the new object is having the same prototype chain 293 // as the copied obj (or otherwise, the cloned object is certainly going to 294 // have a different hidden class). Play with C1/C2 of the 295 // PerformanceVirtualMachineTests suite to see how this makes an impact 296 // under extreme conditions. 297 // 298 // Object.create(Object.getPrototypeOf(obj)) doesn't work well because the 299 // prototype lacks a link to the constructor (Carakan, V8) so the new 300 // object wouldn't have the hidden class that's associated with the 301 // constructor (also, for whatever reasons, utilizing 302 // Object.create(Object.getPrototypeOf(obj)) + Object.defineProperty is even 303 // slower than the original in V8). Therefore, we call the constructor, but 304 // there is a big caveat - it is possible that the this.init() in the 305 // constructor would throw with no argument. It is also possible that a 306 // derived class forgets to set "constructor" on the prototype. We ignore 307 // these possibities for and the ultimate solution is a standardized 308 // Object.clone(<object>). 309 var newObj = (obj.constructor) ? new obj.constructor : {}; 310 311 // Assuming that the constuctor above initialized all properies on obj, the 312 // following keyed assignments won't turn newObj into dictionary mode 313 // becasue they're not *appending new properties* but *assigning existing 314 // ones* (note that appending indexed properties is another story). See 315 // CCClass.js for a link to the devils when the assumption fails. 316 for (var key in obj) { 317 var copy = obj[key]; 318 // Beware that typeof null == "object" ! 319 if (((typeof copy) === "object") && copy && 320 !(copy instanceof cc.Node) && !(copy instanceof HTMLElement)) { 321 newObj[key] = cc.clone(copy); 322 } else { 323 newObj[key] = copy; 324 } 325 } 326 return newObj; 327 }; 328 329 cc.inject = function(srcPrototype, destPrototype){ 330 for(var key in srcPrototype) 331 destPrototype[key] = srcPrototype[key]; 332 }; 333 334