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