2018-02-12 16:01:20 +00:00
|
|
|
/**
|
|
|
|
* @author Richard Davey <rich@photonstorm.com>
|
2022-02-28 14:29:51 +00:00
|
|
|
* @copyright 2022 Photon Storm Ltd.
|
2019-05-10 15:15:04 +00:00
|
|
|
* @license {@link https://opensource.org/licenses/MIT|MIT License}
|
2018-02-12 16:01:20 +00:00
|
|
|
*/
|
|
|
|
|
2017-02-23 03:10:48 +00:00
|
|
|
// 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.
|
2017-11-14 12:12:58 +00:00
|
|
|
|
2017-02-23 03:10:48 +00:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2017-02-28 01:10:25 +00:00
|
|
|
// This might be a regular property, or it may be a getter/setter the user defined in a class.
|
2017-02-23 03:10:48 +00:00
|
|
|
if (def && hasGetterOrSetter(def))
|
|
|
|
{
|
|
|
|
if (typeof def.enumerable === 'undefined')
|
|
|
|
{
|
|
|
|
def.enumerable = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof def.configurable === 'undefined')
|
|
|
|
{
|
|
|
|
def.configurable = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return def;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-02-28 01:10:25 +00:00
|
|
|
return false;
|
2017-02-23 03:10:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-02-19 00:47:44 +00:00
|
|
|
/**
|
|
|
|
* Extends the given `myClass` object's prototype with the properties of `definition`.
|
|
|
|
*
|
2019-05-09 14:32:53 +00:00
|
|
|
* @function extend
|
2020-11-30 14:23:50 +00:00
|
|
|
* @ignore
|
2019-02-19 00:47:44 +00:00
|
|
|
* @param {Object} ctor The constructor object to mix into.
|
|
|
|
* @param {Object} definition A dictionary of functions for the class.
|
|
|
|
* @param {boolean} isClassDescriptor Is the definition a class descriptor?
|
|
|
|
* @param {Object} [extend] The parent constructor object.
|
|
|
|
*/
|
2017-02-28 01:10:25 +00:00
|
|
|
function extend (ctor, definition, isClassDescriptor, extend)
|
2017-02-23 03:10:48 +00:00
|
|
|
{
|
|
|
|
for (var k in definition)
|
|
|
|
{
|
|
|
|
if (!definition.hasOwnProperty(k))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
var def = getProperty(definition, k, isClassDescriptor);
|
|
|
|
|
2017-02-28 01:10:25 +00:00
|
|
|
if (def !== false)
|
2017-02-23 03:10:48 +00:00
|
|
|
{
|
|
|
|
// If Extends is used, we will check its prototype to see if the final variable exists.
|
2017-11-14 12:12:58 +00:00
|
|
|
|
2017-02-28 01:10:25 +00:00
|
|
|
var parent = extend || ctor;
|
2017-02-23 03:10:48 +00:00
|
|
|
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-19 00:47:44 +00:00
|
|
|
/**
|
|
|
|
* Applies the given `mixins` to the prototype of `myClass`.
|
|
|
|
*
|
2019-05-09 14:32:53 +00:00
|
|
|
* @function mixin
|
2020-11-30 14:23:50 +00:00
|
|
|
* @ignore
|
2019-02-19 00:47:44 +00:00
|
|
|
* @param {Object} myClass The constructor object to mix into.
|
|
|
|
* @param {Object|Array<Object>} mixins The mixins to apply to the constructor.
|
|
|
|
*/
|
2017-02-23 03:10:48 +00:00
|
|
|
function mixin (myClass, mixins)
|
|
|
|
{
|
|
|
|
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.
|
|
|
|
*
|
2019-02-19 00:47:44 +00:00
|
|
|
* @class Phaser.Class
|
2017-02-23 03:10:48 +00:00
|
|
|
* @constructor
|
|
|
|
* @param {Object} definition a dictionary of functions for the class
|
|
|
|
* @example
|
|
|
|
*
|
|
|
|
* var MyClass = new Phaser.Class({
|
2017-11-14 12:12:58 +00:00
|
|
|
*
|
2017-02-23 03:10:48 +00:00
|
|
|
* 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;
|
|
|
|
}
|
2017-11-14 12:12:58 +00:00
|
|
|
else if (definition.Extends)
|
2017-02-23 03:10:48 +00:00
|
|
|
{
|
2017-11-14 12:12:58 +00:00
|
|
|
var base = definition.Extends;
|
2017-02-23 03:10:48 +00:00
|
|
|
|
2017-11-14 12:12:58 +00:00
|
|
|
initialize = function ()
|
2017-02-23 03:10:48 +00:00
|
|
|
{
|
2017-11-14 12:12:58 +00:00
|
|
|
base.apply(this, arguments);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
initialize = function () {};
|
2017-02-23 03:10:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|