// Taken from klasse by mattdesl https://github.com/mattdesl/klasse function hasGetterOrSetter (def) { return (!!def.get && typeof def.get === 'function') || (!!def.set && typeof def.set === 'function'); } function getProperty (definition, k, isClassDescriptor) { // This may be a lightweight object, OR it might be a property that was defined previously. // For simple class descriptors we can just assume its NOT previously defined. var def = (isClassDescriptor) ? definition[k] : Object.getOwnPropertyDescriptor(definition, k); if (!isClassDescriptor && def.value && typeof def.value === 'object') { def = def.value; } // This might be a regular property, or it may be a getter / setter the user defined in a class. if (def && hasGetterOrSetter(def)) { if (typeof def.enumerable === 'undefined') { def.enumerable = true; } if (typeof def.configurable === 'undefined') { def.configurable = true; } return def; } else { // if (typeof definition[k] === 'object') // { // return -1; // } // else // { return false; // } } } function hasNonConfigurable (obj, k) { var prop = Object.getOwnPropertyDescriptor(obj, k); if (!prop) { return false; } if (prop.value && typeof prop.value === 'object') { prop = prop.value; } if (prop.configurable === false) { return true; } return false; } function extend (ctor, definition, isClassDescriptor, extendCallback) { for (var k in definition) { if (!definition.hasOwnProperty(k)) { continue; } if (typeof definition[k] === 'object') { // Here goes nothing ... extend(ctor, def, false, extendCallback); } var def = getProperty(definition, k, isClassDescriptor); // Object ... if (def === -1) { console.log(k, def); // Iterate the object, and see if any children are getters / setters // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty def = definition[k]; var entry = {}; entry[k] = {}; // ctor.prototype[k] = {}; // Object.defineProperty(ctor.prototype, k, { value: {} }); console.log('iterating', def); for (var key in def) { var child = def[key]; console.log('->', key, child); if (child && hasGetterOrSetter(child)) { if (typeof child.enumerable === 'undefined') { child.enumerable = true; } if (typeof child.configurable === 'undefined') { child.configurable = true; } Object.defineProperty(entry[k], key, child); } else { Object.defineProperty(entry[k], key, { value: child, writable: true, configurable: true, enumerable: true }); // ctor.prototype[k][key] = { value: child }; } } // ctor.prototype[k] = def; console.dir(entry); Object.defineProperties(ctor.prototype, entry); } else if (def !== false) { // If Extends is used, we will check its prototype to see if the final variable exists. var parent = extendCallback || ctor; if (hasNonConfigurable(parent.prototype, k)) { // Just skip the final property if (Class.ignoreFinals) { continue; } // We cannot re-define a property that is configurable=false. // So we will consider them final and throw an error. This is by // default so it is clear to the developer what is happening. // You can set ignoreFinals to true if you need to extend a class // which has configurable=false; it will simply not re-define final properties. throw new Error('cannot override final property \'' + k + '\', set Class.ignoreFinals = true to skip'); } Object.defineProperty(ctor.prototype, k, def); } else { ctor.prototype[k] = definition[k]; } } } function mixin (myClass, mixins) { console.log(myClass); if (!mixins) { return; } if (!Array.isArray(mixins)) { mixins = [ mixins ]; } for (var i = 0; i < mixins.length; i++) { extend(myClass, mixins[i].prototype || mixins[i]); } } /** * Creates a new class with the given descriptor. * The constructor, defined by the name `initialize`, * is an optional function. If unspecified, an anonymous * function will be used which calls the parent class (if * one exists). * * You can also use `Extends` and `Mixins` to provide subclassing * and inheritance. * * @class Class * @constructor * @param {Object} definition a dictionary of functions for the class * @example * * var MyClass = new Phaser.Class({ * * initialize: function() { * this.foo = 2.0; * }, * * bar: function() { * return this.foo + 5; * } * }); */ function Class (definition) { if (!definition) { definition = {}; } // The variable name here dictates what we see in Chrome debugger var initialize; var Extends; if (definition.initialize) { if (typeof definition.initialize !== 'function') { throw new Error('initialize must be a function'); } initialize = definition.initialize; // Usually we should avoid 'delete' in V8 at all costs. // However, its unlikely to make any performance difference // here since we only call this on class creation (i.e. not object creation). delete definition.initialize; } else { if (definition.Extends) { var base = definition.Extends; initialize = function () { base.apply(this, arguments); }; } else { initialize = function () {}; } } if (definition.Extends) { initialize.prototype = Object.create(definition.Extends.prototype); initialize.prototype.constructor = initialize; // For getOwnPropertyDescriptor to work, we need to act directly on the Extends (or Mixin) Extends = definition.Extends; delete definition.Extends; } else { initialize.prototype.constructor = initialize; } // Grab the mixins, if they are specified... var mixins = null; if (definition.Mixins) { mixins = definition.Mixins; delete definition.Mixins; } // First, mixin if we can. mixin(initialize, mixins); // Now we grab the actual definition which defines the overrides. extend(initialize, definition, true, Extends); return initialize; } Class.extend = extend; Class.mixin = mixin; Class.ignoreFinals = false; module.exports = Class;