forked from phaserjs/phaser
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathClass.js
More file actions
226 lines (187 loc) · 5.47 KB
/
Class.js
File metadata and controls
226 lines (187 loc) · 5.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
// 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
{
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, extend)
{
for (var k in definition)
{
if (!definition.hasOwnProperty(k))
{
continue;
}
var def = getProperty(definition, k, isClassDescriptor);
if (def !== false)
{
// If Extends is used, we will check its prototype to see if the final variable exists.
var parent = extend || 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)
{
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;