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 getNewID : function(){ 39 return this.id++; 40 }, 41 42 getNewInstanceId : function(){ 43 return this.instanceId++; 44 } 45 }; 46 47 /* Managed JavaScript Inheritance 48 * Based on John Resig's Simple JavaScript Inheritance http://ejohn.org/blog/simple-javascript-inheritance/ 49 * MIT Licensed. 50 */ 51 (function () { 52 var fnTest = /\b_super\b/; 53 54 /** 55 * The base Class implementation (does nothing) 56 * @class 57 */ 58 cc.Class = function () { 59 }; 60 61 /** 62 * Create a new Class that inherits from this Class 63 * @static 64 * @param {object} props 65 * @return {function} 66 */ 67 cc.Class.extend = function (props) { 68 var _super = this.prototype; 69 70 // Instantiate a base Class (but only create the instance, 71 // don't run the init constructor) 72 var prototype = Object.create(_super); 73 74 var classId = ClassManager.getNewID(); 75 ClassManager[classId] = _super; 76 // Copy the properties over onto the new prototype. We make function 77 // properties non-eumerable as this makes typeof === 'function' check 78 // unneccessary in the for...in loop used 1) for generating Class() 79 // 2) for cc.clone and perhaps more. It is also required to make 80 // these function properties cacheable in Carakan. 81 var desc = { writable: true, enumerable: false, configurable: true }; 82 83 prototype.__instanceId = null; 84 85 // The dummy Class constructor 86 function Class() { 87 this.__instanceId = ClassManager.getNewInstanceId(); 88 // All construction is actually done in the init method 89 if (this.ctor) 90 this.ctor.apply(this, arguments); 91 } 92 93 Class.id = classId; 94 // desc = { writable: true, enumerable: false, configurable: true, 95 // value: XXX }; Again, we make this non-enumerable. 96 desc.value = classId; 97 Object.defineProperty(prototype, '__pid', desc); 98 99 // Populate our constructed prototype object 100 Class.prototype = prototype; 101 102 // Enforce the constructor to be what we expect 103 desc.value = Class; 104 Object.defineProperty(Class.prototype, 'constructor', desc); 105 106 // Copy getter/setter 107 this.__getters__ && (Class.__getters__ = cc.clone(this.__getters__)); 108 this.__setters__ && (Class.__setters__ = cc.clone(this.__setters__)); 109 110 for(var idx = 0, li = arguments.length; idx < li; ++idx) { 111 var prop = arguments[idx]; 112 for (var name in prop) { 113 var isFunc = (typeof prop[name] === "function"); 114 var override = (typeof _super[name] === "function"); 115 var hasSuperCall = fnTest.test(prop[name]); 116 117 if (isFunc && override && hasSuperCall) { 118 desc.value = (function (name, fn) { 119 return function () { 120 var tmp = this._super; 121 122 // Add a new ._super() method that is the same method 123 // but on the super-Class 124 this._super = _super[name]; 125 126 // The method only need to be bound temporarily, so we 127 // remove it when we're done executing 128 var ret = fn.apply(this, arguments); 129 this._super = tmp; 130 131 return ret; 132 }; 133 })(name, prop[name]); 134 Object.defineProperty(prototype, name, desc); 135 } else if (isFunc) { 136 desc.value = prop[name]; 137 Object.defineProperty(prototype, name, desc); 138 } else { 139 prototype[name] = prop[name]; 140 } 141 142 if (isFunc) { 143 // Override registered getter/setter 144 var getter, setter, propertyName; 145 if (this.__getters__ && this.__getters__[name]) { 146 propertyName = this.__getters__[name]; 147 for (var i in this.__setters__) { 148 if (this.__setters__[i] === propertyName) { 149 setter = i; 150 break; 151 } 152 } 153 cc.defineGetterSetter(prototype, propertyName, prop[name], prop[setter] ? prop[setter] : prototype[setter], name, setter); 154 } 155 if (this.__setters__ && this.__setters__[name]) { 156 propertyName = this.__setters__[name]; 157 for (var i in this.__getters__) { 158 if (this.__getters__[i] === propertyName) { 159 getter = i; 160 break; 161 } 162 } 163 cc.defineGetterSetter(prototype, propertyName, prop[getter] ? prop[getter] : prototype[getter], prop[name], getter, name); 164 } 165 } 166 } 167 } 168 169 // And make this Class extendable 170 Class.extend = cc.Class.extend; 171 172 //add implementation method 173 Class.implement = function (prop) { 174 for (var name in prop) { 175 prototype[name] = prop[name]; 176 } 177 }; 178 return Class; 179 }; 180 })(); 181 182 /** 183 * Common getter setter configuration function 184 * @function 185 * @param {Object} proto A class prototype or an object to config<br/> 186 * @param {String} prop Property name 187 * @param {function} getter Getter function for the property 188 * @param {function} setter Setter function for the property 189 * @param {String} getterName Name of getter function for the property 190 * @param {String} setterName Name of setter function for the property 191 */ 192 cc.defineGetterSetter = function (proto, prop, getter, setter, getterName, setterName){ 193 if (proto.__defineGetter__) { 194 getter && proto.__defineGetter__(prop, getter); 195 setter && proto.__defineSetter__(prop, setter); 196 } else if (Object.defineProperty) { 197 var desc = { enumerable: false, configurable: true }; 198 getter && (desc.get = getter); 199 setter && (desc.set = setter); 200 Object.defineProperty(proto, prop, desc); 201 } else { 202 throw new Error("browser does not support getters"); 203 } 204 205 if(!getterName && !setterName) { 206 // Lookup getter/setter function 207 var hasGetter = (getter != null), hasSetter = (setter != undefined), props = Object.getOwnPropertyNames(proto); 208 for (var i = 0; i < props.length; i++) { 209 var name = props[i]; 210 211 if( (proto.__lookupGetter__ ? proto.__lookupGetter__(name) 212 : Object.getOwnPropertyDescriptor(proto, name)) 213 || typeof proto[name] !== "function" ) 214 continue; 215 216 var func = proto[name]; 217 if (hasGetter && func === getter) { 218 getterName = name; 219 if(!hasSetter || setterName) break; 220 } 221 if (hasSetter && func === setter) { 222 setterName = name; 223 if(!hasGetter || getterName) break; 224 } 225 } 226 } 227 228 // Found getter/setter 229 var ctor = proto.constructor; 230 if (getterName) { 231 if (!ctor.__getters__) { 232 ctor.__getters__ = {}; 233 } 234 ctor.__getters__[getterName] = prop; 235 } 236 if (setterName) { 237 if (!ctor.__setters__) { 238 ctor.__setters__ = {}; 239 } 240 ctor.__setters__[setterName] = prop; 241 } 242 }; 243 244 /** 245 * Create a new object and copy all properties in an exist object to the new object 246 * @function 247 * @param {object|Array} obj The source object 248 * @return {Array|object} The created object 249 */ 250 cc.clone = function (obj) { 251 // Cloning is better if the new object is having the same prototype chain 252 // as the copied obj (or otherwise, the cloned object is certainly going to 253 // have a different hidden class). Play with C1/C2 of the 254 // PerformanceVirtualMachineTests suite to see how this makes an impact 255 // under extreme conditions. 256 // 257 // Object.create(Object.getPrototypeOf(obj)) doesn't work well because the 258 // prototype lacks a link to the constructor (Carakan, V8) so the new 259 // object wouldn't have the hidden class that's associated with the 260 // constructor (also, for whatever reasons, utilizing 261 // Object.create(Object.getPrototypeOf(obj)) + Object.defineProperty is even 262 // slower than the original in V8). Therefore, we call the constructor, but 263 // there is a big caveat - it is possible that the this.init() in the 264 // constructor would throw with no argument. It is also possible that a 265 // derived class forgets to set "constructor" on the prototype. We ignore 266 // these possibities for and the ultimate solution is a standardized 267 // Object.clone(<object>). 268 var newObj = (obj.constructor) ? new obj.constructor : {}; 269 270 // Assuming that the constuctor above initialized all properies on obj, the 271 // following keyed assignments won't turn newObj into dictionary mode 272 // becasue they're not *appending new properties* but *assigning existing 273 // ones* (note that appending indexed properties is another story). See 274 // CCClass.js for a link to the devils when the assumption fails. 275 for (var key in obj) { 276 var copy = obj[key]; 277 // Beware that typeof null == "object" ! 278 if (((typeof copy) === "object") && copy && 279 !(copy instanceof cc.Node) && !(copy instanceof HTMLElement)) { 280 newObj[key] = cc.clone(copy); 281 } else { 282 newObj[key] = copy; 283 } 284 } 285 return newObj; 286 }; 287 288 cc.inject = function(srcPrototype, destPrototype){ 289 for(var key in srcPrototype) 290 destPrototype[key] = srcPrototype[key]; 291 }; 292 293