1 | /* Javascript Object Inheritance Implementation ______ ________ |
||
2 | * (c) 2016 <[email protected]> __ / / __ \/ _/ _/ |
||
3 | * Licensed under MIT. / // / /_/ // /_/ / |
||
4 | * ------------------------------------------------------ \___/\____/___/__*/ |
||
5 | |||
6 | 'use strict'; |
||
7 | |||
8 | JOII = typeof (JOII) !== 'undefined' ? JOII : {}; |
||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
9 | JOII.ClassRegistry = {}; |
||
0 ignored issues
–
show
|
|||
10 | |||
11 | /** |
||
12 | * The ClassBuilder is responsible for creating a class definition based |
||
13 | * on the given parameters and body. We use the PrototypeBuilder to create |
||
14 | * a uniform prototype based on our own defined class body and the |
||
15 | * prototypes of inherited definitions. |
||
16 | * |
||
17 | * The resulting function will be the class definition which creates its |
||
18 | * own new 'scope' each time it's instantiated. |
||
19 | * |
||
20 | * @param string name |
||
21 | * @param object parameters |
||
22 | * @param object body |
||
23 | * @return function |
||
24 | */ |
||
25 | JOII.ClassBuilder = function() { |
||
0 ignored issues
–
show
|
|||
26 | var args = JOII.Compat.ParseArguments(arguments), |
||
0 ignored issues
–
show
|
|||
27 | name = args.name, |
||
28 | parameters = args.parameters, |
||
29 | body = args.body, |
||
30 | is_static_generated = args.is_static_generated === true; |
||
31 | |||
32 | function static_scope_in() { |
||
33 | // If 'this.__joii__' is not available, that would indicate that |
||
34 | // we've been executed like a function rather than being instantiated. |
||
35 | if (typeof (this) === 'undefined' || typeof (this.__joii__) === 'undefined') { |
||
36 | // If the method __call exists, execute it and return its result. |
||
37 | |||
38 | return definition.apply(undefined, arguments); |
||
39 | } |
||
40 | |||
41 | return new definition(); |
||
42 | } |
||
43 | /** |
||
44 | * Defines the class definition. This is the function that is executed |
||
45 | * when the class is instantiated or executed. The function will relay |
||
46 | * execution to the __construct or __call method, depending whether the |
||
47 | * class was called as a function or instantiated using the 'new' |
||
48 | * keyword. |
||
49 | * |
||
50 | * @return object The outer (public) class scope. |
||
51 | */ |
||
52 | function definition() { |
||
0 ignored issues
–
show
|
|||
53 | |||
54 | var func_in = function() { }; |
||
55 | func_in.prototype = this; |
||
56 | var scope_in_obj = new func_in(); |
||
57 | |||
58 | |||
59 | // Create an inner and outer scope. The inner scope refers to the |
||
60 | // 'this' variable, where the outer scope contains references to |
||
61 | // all objects and functions accessible from the outside. |
||
62 | var scope_in = generateInnerScope(this, arguments, scope_in_obj, false); |
||
63 | |||
64 | // for __call implementations |
||
65 | if (typeof (this) === 'undefined' || typeof (this.__joii__) === 'undefined' || typeof (scope_in) !== 'object' || typeof (scope_in.__joii__) === 'undefined') { |
||
66 | return scope_in; |
||
67 | } |
||
68 | |||
69 | |||
70 | |||
71 | |||
72 | var scope_out = generateOuterScope(this, scope_in); |
||
73 | |||
74 | // need to link the inner and outer scopes before calling constructors |
||
75 | linkAPI(scope_in, scope_out); |
||
76 | |||
77 | |||
78 | // apply meta traits |
||
79 | JOII.callMetaMixin('beforeNew', scope_in, scope_out); |
||
0 ignored issues
–
show
|
|||
80 | |||
81 | for (var meta_index in scope_in.__joii__.metadata) { |
||
82 | if (scope_in.__joii__.metadata.hasOwnProperty(meta_index) === false) continue; |
||
83 | var meta = scope_in.__joii__.metadata[meta_index]; |
||
84 | |||
85 | JOII.callMetaMixin('onNew', scope_in, scope_out, meta); |
||
0 ignored issues
–
show
|
|||
86 | } |
||
87 | |||
88 | JOII.callMetaMixin('afterNew', scope_in, scope_out); |
||
0 ignored issues
–
show
|
|||
89 | |||
90 | |||
91 | callConstructors(scope_in, arguments); |
||
92 | |||
93 | return scope_out; |
||
94 | } |
||
95 | |||
96 | function callConstructors(scope_in, args) |
||
0 ignored issues
–
show
|
|||
97 | { |
||
98 | // Does the class defintion have a constructor? If so, run it. |
||
99 | for (var c in JOII.Config.constructors) { |
||
0 ignored issues
–
show
|
|||
100 | if (JOII.Config.constructors.hasOwnProperty(c)) { |
||
0 ignored issues
–
show
|
|||
101 | var cc = JOII.Config.constructors[c]; |
||
0 ignored issues
–
show
|
|||
102 | if (typeof (scope_in[cc]) === 'function') { |
||
103 | scope_in[cc].apply(scope_in, args); |
||
104 | break; |
||
105 | } |
||
106 | } |
||
107 | } |
||
108 | |||
109 | // deserialize data |
||
110 | if (args.length == 1 && typeof args[0] == 'object' && '__joii_deserialize_object' in args[0]) { |
||
111 | scope_in.deserialize(args[0].data); |
||
112 | } |
||
113 | } |
||
114 | |||
115 | function linkAPI(scope_in, scope_out) |
||
0 ignored issues
–
show
|
|||
116 | { |
||
117 | // Create a reference to the outer scope for use in fluid interfacing. |
||
118 | scope_in.__api__ = scope_out; |
||
119 | |||
120 | // Apply the API object to inherited classes to keep the super() functionality working no matter how deep |
||
121 | // the inheritance-chain goes. |
||
122 | // This feels really 'hacky' in my opinion, but it fixes issue #19 and doesn't break any other test. |
||
123 | // As far as I can tell, there's no real performance impact on this, although I'm running this on a beast |
||
124 | // of a computer. If anyone has a more elegant solution, a pull-request would be much appreciated! |
||
125 | if (typeof scope_in.__joii__.parent !== 'undefined') { |
||
126 | var current = scope_in.__joii__.parent; |
||
127 | while (typeof current !== 'undefined') { |
||
128 | current.__api__ = scope_out; |
||
129 | current = current.__joii__.parent; |
||
130 | } |
||
131 | } |
||
132 | } |
||
133 | |||
134 | function generateInnerScope(scope, args, base_object, is_static_generated) { |
||
0 ignored issues
–
show
|
|||
135 | var scope_in = base_object || {}; |
||
136 | |||
137 | is_static_generated = is_static_generated || false; |
||
138 | |||
139 | // Create a deep copy of the inner scope because we need to |
||
140 | // dereference object-type properties. If we don't do this, object- |
||
141 | // types are treated statically throughout all instances. |
||
142 | scope_in = JOII.Compat.extend(true, {}, scope_in); |
||
0 ignored issues
–
show
|
|||
143 | |||
144 | if (typeof scope !== 'undefined') { |
||
145 | JOII.CreateProperty(scope_in, '__joii__', (scope.__joii__)); |
||
0 ignored issues
–
show
|
|||
146 | } |
||
147 | |||
148 | if (typeof scope !== 'undefined' && typeof (scope_in.__joii__) === 'object') { |
||
149 | // Can we be instantiated? |
||
150 | if (scope_in.__joii__.is_abstract === true) { |
||
151 | throw 'An abstract class cannot be instantiated.'; |
||
152 | } |
||
153 | if (!is_static_generated && scope_in.__joii__.is_static === true) { |
||
154 | throw 'A static class cannot be instantiated.'; |
||
155 | } |
||
156 | } |
||
157 | |||
158 | // If 'this.__joii__' is not available, that would indicate that |
||
159 | // we've been executed like a function rather than being instantiated. |
||
160 | if (typeof (scope) === 'undefined' || typeof (scope.__joii__) === 'undefined') { |
||
161 | // If the method __call exists, execute it and return its result. |
||
162 | |||
163 | if (typeof (static_scope_in) !== 'undefined') |
||
164 | { |
||
165 | for (var c in JOII.Config.callables) { |
||
0 ignored issues
–
show
|
|||
166 | if (JOII.Config.callables.hasOwnProperty(c)) { |
||
0 ignored issues
–
show
|
|||
167 | if (typeof (static_scope_in[JOII.Config.callables[c]]) === 'function') { |
||
0 ignored issues
–
show
|
|||
168 | var result = static_scope_in[JOII.Config.callables[c]].apply(body, args); |
||
0 ignored issues
–
show
|
|||
169 | if (result === body) { |
||
170 | throw JOII.Config.callables[c] + ' cannot return itself.'; |
||
0 ignored issues
–
show
|
|||
171 | } |
||
172 | return result; |
||
173 | } |
||
174 | } |
||
175 | } |
||
176 | } |
||
177 | throw 'This class cannot be called as a function because it\'s lacking the __call method.'; |
||
178 | } |
||
179 | |||
180 | |||
181 | |||
182 | // Are we attempting to instantiate an abstract class? |
||
183 | if (scope.__joii__.is_abstract) { |
||
184 | throw 'Cannot instantiate abstract class ' + scope.__joii__.name; |
||
185 | } |
||
186 | |||
187 | |||
188 | return scope_in; |
||
189 | |||
190 | } |
||
191 | |||
192 | function generateOuterScope(scope, scope_in, base_object) { |
||
0 ignored issues
–
show
|
|||
193 | var scope_out = base_object || {}; |
||
194 | |||
195 | if (typeof scope !== 'undefined' && typeof (scope.__joii__) === 'object') { |
||
196 | |||
197 | JOII.CreateProperty(scope_out, '__joii__', (scope.__joii__)); |
||
0 ignored issues
–
show
|
|||
198 | |||
199 | // Can we be instantiated? |
||
200 | if (scope_out.__joii__.is_abstract === true) { |
||
201 | throw 'An abstract class cannot be instantiated.'; |
||
202 | } |
||
203 | |||
204 | // The outside scope. |
||
205 | for (var i in scope) { |
||
206 | var meta = scope_in.__joii__.metadata[i]; |
||
207 | |||
208 | if (meta && 'overloads' in meta) { |
||
209 | for (var fn_meta in meta.overloads) { |
||
210 | // Test missing abstract implementations... |
||
211 | if (meta.overloads[fn_meta] && meta.overloads[fn_meta].is_abstract === true) { |
||
212 | throw 'Missing abstract member implementation of ' + i + '(' + meta.overloads[fn_meta].parameters.join(', ') + ')'; |
||
213 | } |
||
214 | } |
||
215 | } else if (meta && meta.is_abstract === true) { |
||
216 | throw 'Missing abstract member implementation of "' + i + '".'; |
||
217 | } |
||
218 | } |
||
219 | } |
||
220 | |||
221 | bindPublicMethods(scope_in, scope_out); |
||
222 | |||
223 | return scope_out; |
||
224 | } |
||
225 | |||
226 | function bindPublicMethods(from_obj, to_obj) |
||
0 ignored issues
–
show
|
|||
227 | { |
||
228 | // The outside scope. |
||
229 | for (var i in from_obj) { |
||
230 | var meta = from_obj.__joii__.metadata[i]; |
||
231 | |||
232 | // Only allow public functions in the outside scope. |
||
233 | if (typeof (from_obj[i]) === 'function' && (typeof (meta) === 'undefined' || meta.visibility === 'public') && (i !== '__call')) { |
||
234 | to_obj[i] = JOII.Compat.Bind(from_obj[i], from_obj); |
||
0 ignored issues
–
show
|
|||
235 | } |
||
236 | } |
||
237 | } |
||
238 | |||
239 | |||
240 | if (typeof (body) == 'function') { |
||
241 | body = body(static_scope_in); |
||
242 | } |
||
243 | |||
244 | if (typeof (body) != 'object') { |
||
245 | throw 'Invalid parameter types given. Expected: ([[[string], object], <object|function>]).'; |
||
246 | } |
||
247 | |||
248 | |||
249 | |||
250 | // Apply to prototype to the instantiator to allow extending the |
||
251 | // class definition upon other definitions without instantiation. |
||
252 | definition.prototype = JOII.PrototypeBuilder(name, parameters, body, false, is_static_generated); |
||
0 ignored issues
–
show
|
|||
253 | |||
254 | |||
255 | |||
256 | // Apply constants to the definition |
||
257 | for (var i in definition.prototype.__joii__.constants) { |
||
258 | JOII.CreateProperty(definition, i, definition.prototype.__joii__.constants[i], false); |
||
0 ignored issues
–
show
|
|||
259 | } |
||
260 | |||
261 | // Does the class implement an enumerator? |
||
262 | if (typeof (parameters['enum']) === 'string') { |
||
263 | var e = JOII.EnumBuilder(parameters['enum'], definition); |
||
0 ignored issues
–
show
|
|||
264 | if (parameters.expose_enum === true) { |
||
265 | var g = typeof window === 'object' ? window : global; |
||
266 | if (typeof (g[parameters['enum']]) !== 'undefined') { |
||
267 | throw 'Cannot expose Enum "' + parameters['enum'] + '" becase it already exists in the global scope.'; |
||
268 | } |
||
269 | g[parameters['enum']] = e; |
||
270 | } |
||
271 | } |
||
272 | |||
273 | // Override toString to return a class symbol. |
||
274 | var n = arguments[0]; |
||
275 | definition.toString = function() { |
||
276 | if (typeof (n) === 'string') { |
||
277 | return '[class ' + n + ']'; |
||
278 | } |
||
279 | return '[class Class]'; |
||
280 | }; |
||
281 | |||
282 | // Store defined interfaces in the metadata. |
||
283 | definition.prototype.__joii__.interfaces = parameters['implements']; |
||
284 | |||
285 | // TODO performance can be increased here by storing the parsed |
||
286 | // interfaces in the 'interfaces' array in __joii__. |
||
287 | |||
288 | // Recursive function for retrieving a list of interfaces from the |
||
289 | // current class and the rest of the inheritance tree. |
||
290 | bindGetInterfaces(definition.prototype.__joii__); |
||
291 | |||
292 | |||
293 | function bindGetInterfaces(joii) |
||
0 ignored issues
–
show
|
|||
294 | { |
||
295 | |||
296 | // Recursive function for retrieving a list of interfaces from the |
||
297 | // current class and the rest of the inheritance tree. |
||
298 | joii.getInterfaces = JOII.Compat.Bind(function() { |
||
0 ignored issues
–
show
|
|||
299 | var interfaces = [], |
||
300 | getRealInterface = JOII.Compat.Bind(function(i) { |
||
0 ignored issues
–
show
|
|||
301 | if (typeof (i) === 'function') { |
||
302 | return i; |
||
303 | } else if (typeof (i) === 'string') { |
||
304 | if (typeof (JOII.InterfaceRegistry[i]) === 'undefined') { |
||
0 ignored issues
–
show
|
|||
305 | throw 'Interface "' + i + '" does not exist.'; |
||
306 | } |
||
307 | return JOII.InterfaceRegistry[i]; |
||
0 ignored issues
–
show
|
|||
308 | } |
||
309 | }, this); |
||
310 | |||
311 | // Fetch interfaces from the parent list - if they exist. |
||
312 | if (typeof (this.parent) !== 'undefined' && typeof (this.parent.__joii__) !== 'undefined') { |
||
313 | interfaces = this.parent.__joii__.getInterfaces(); |
||
314 | } |
||
315 | |||
316 | if (typeof (this.interfaces) !== 'undefined') { |
||
317 | if (typeof (this.interfaces) === 'object') { |
||
318 | for (var i in this.interfaces) { |
||
319 | if (!this.interfaces.hasOwnProperty(i)) { |
||
320 | continue; |
||
321 | } |
||
322 | interfaces.push(getRealInterface(this.interfaces[i])); |
||
323 | } |
||
324 | } else { |
||
325 | interfaces.push(getRealInterface(this.interfaces)); |
||
326 | } |
||
327 | } |
||
328 | |||
329 | return interfaces; |
||
330 | }, joii); |
||
331 | } |
||
332 | |||
333 | // If any interfaces are implemented in this class, validate them |
||
334 | // immediately rather than doing so during instantiation. If the |
||
335 | // class is declared abstract, the validation is skipped. |
||
336 | if (parameters.abstract !== true) { |
||
337 | var interfaces = definition.prototype.__joii__.getInterfaces(); |
||
338 | for (var ii in interfaces) { |
||
339 | if (interfaces.hasOwnProperty(ii) && typeof (interfaces[ii]) === 'function') { |
||
340 | interfaces[ii](definition); |
||
341 | } |
||
342 | } |
||
343 | } |
||
344 | |||
345 | |||
346 | if (parameters['static'] !== true && !is_static_generated) { |
||
347 | |||
348 | // check to make sure serialize doesn't exist yet, or if it does - it's capable of being overloaded without breaking BC |
||
349 | if ((!('serialize' in definition.prototype.__joii__.metadata)) || (('overloads' in definition.prototype.__joii__.metadata['serialize']) && (definition.prototype.__joii__.metadata['serialize']['overloads'][0].parameters.length > 0 || definition.prototype.__joii__.metadata['serialize']['overloads'].length > 1))) { |
||
350 | |||
351 | /** |
||
352 | * Serializes all serializable properties of an object. Public members are serializable by default. |
||
353 | * |
||
354 | * @return {String} |
||
355 | */ |
||
356 | var generated_fn = function() { |
||
357 | return JSON.stringify(this.serialize(true)); |
||
358 | }; |
||
359 | // uses an inheritance style add, so it won't overwrite custom functions with the same signature |
||
360 | var serialize_meta = JOII.ParseClassProperty('public function serialize()'); |
||
0 ignored issues
–
show
|
|||
361 | JOII.addFunctionToPrototype(definition.prototype, serialize_meta, generated_fn, true); |
||
0 ignored issues
–
show
|
|||
362 | |||
363 | |||
364 | /** |
||
365 | * Serializes all serializable properties of an object. Public members are serializable by default. |
||
366 | * |
||
367 | * @return {Object} |
||
368 | */ |
||
369 | var generated_fn = function(bool_return_object) { |
||
0 ignored issues
–
show
|
|||
370 | var obj = { __joii_type: this.__joii__.name }; |
||
371 | |||
372 | for (var key in this.__joii__.metadata) { |
||
373 | var val = this.__joii__.metadata[key]; |
||
374 | |||
375 | if (val.serializable) { |
||
376 | |||
377 | var getter_name = JOII.GenerateGetterName(val); |
||
0 ignored issues
–
show
|
|||
378 | var currentValue = null; |
||
379 | if (typeof (this[getter_name]) === 'function') { |
||
380 | // use getter if it exists. This allows custom getters to translate the data properly if needed. |
||
381 | currentValue = this[getter_name](); |
||
382 | } else { |
||
383 | currentValue = this[val.name]; |
||
384 | } |
||
385 | |||
386 | if (!val.is_enum && typeof (currentValue) === 'object' && currentValue !== null) { |
||
387 | if ('serialize' in currentValue) { |
||
388 | obj[val.name] = currentValue.serialize(true); |
||
389 | } else { |
||
390 | obj[val.name] = JOII.Compat.flattenObject(currentValue); |
||
0 ignored issues
–
show
There were too many errors found in this file; checking aborted after 61%.
If JSHint finds too many errors in a file, it aborts checking altogether because it suspects a configuration issue. Further Reading: ![]() |
|||
391 | } |
||
392 | } else { |
||
393 | obj[val.name] = currentValue; |
||
394 | } |
||
395 | } |
||
396 | } |
||
397 | |||
398 | return obj; |
||
399 | }; |
||
400 | // uses an inheritance style add, so it won't overwrite custom functions with the same signature |
||
401 | var serialize_meta = JOII.ParseClassProperty('public function serialize(boolean)'); |
||
0 ignored issues
–
show
|
|||
402 | JOII.addFunctionToPrototype(definition.prototype, serialize_meta, generated_fn, true); |
||
403 | } |
||
404 | |||
405 | |||
406 | |||
407 | // check to make sure deserialize doesn't exist yet, or if it does - it's capable of being overloaded without breaking BC |
||
408 | if ((!('deserialize' in definition.prototype.__joii__.metadata)) || (('overloads' in definition.prototype.__joii__.metadata['deserialize']) && (definition.prototype.__joii__.metadata['deserialize']['overloads'][0].parameters.length > 0 || definition.prototype.__joii__.metadata['deserialize']['overloads'].length > 1))) { |
||
409 | /** |
||
410 | * Deserializes a class (called on an object instance to populate it) |
||
411 | * |
||
412 | * @param {String} |
||
413 | */ |
||
414 | var generated_fn = function(json) { |
||
0 ignored issues
–
show
|
|||
415 | this.deserialize(JSON.parse(json)); |
||
416 | }; |
||
417 | // uses an inheritance style add, so it won't overwrite custom functions with the same signature |
||
418 | var deserialize_meta = JOII.ParseClassProperty('public function deserialize(string)'); |
||
419 | JOII.addFunctionToPrototype(definition.prototype, deserialize_meta, generated_fn, true); |
||
420 | |||
421 | /** |
||
422 | * Deserializes a class (called on an object instance to populate it) |
||
423 | * |
||
424 | * @param {Object} |
||
425 | */ |
||
426 | generated_fn = function(obj) { |
||
427 | for (var key in (this.__joii__.metadata)) { |
||
428 | var val = this.__joii__.metadata[key]; |
||
429 | |||
430 | if (val.serializable) { |
||
431 | if (val.name in obj && typeof (obj[val.name]) != 'function') { |
||
432 | var setter_name = JOII.GenerateSetterName(val); |
||
433 | var getter_name = JOII.GenerateGetterName(val); |
||
434 | |||
435 | |||
436 | if (typeof (obj[val.name]) === 'object' && obj[val.name] !== null && '__joii_type' in (obj[val.name])) { |
||
437 | var name = obj[val.name].__joii_type; |
||
438 | // Check for Interface-types |
||
439 | if (typeof (JOII.InterfaceRegistry[name]) !== 'undefined') { |
||
440 | throw 'Cannot instantiate an interface.'; |
||
441 | } |
||
442 | // Check for Class-types |
||
443 | else if (typeof (JOII.ClassRegistry[name]) !== 'undefined') { |
||
444 | |||
445 | var currentValue = null; |
||
446 | if (typeof (this[getter_name]) === 'function') { |
||
447 | // use getter if it exists. This allows custom getters to translate the data properly if needed. |
||
448 | currentValue = this[getter_name](); |
||
449 | } else { |
||
450 | currentValue = this[val.name]; |
||
451 | } |
||
452 | |||
453 | if (typeof (currentValue) === 'object' && currentValue !== null && currentValue.__joii__.name === name) { |
||
454 | // try to deserialize in place if the object already exists. This avoids breaking object references. |
||
455 | currentValue.deserialize(obj[val.name]); |
||
456 | } else { |
||
457 | if (typeof (this[setter_name]) === 'function') { |
||
458 | // use setter if it exists. This allows custom setters to translate the data properly. |
||
459 | this[setter_name](JOII.ClassRegistry[name].deserialize(obj[val.name])); |
||
460 | } else { |
||
461 | // need to set directly |
||
462 | this[val.name] = JOII.ClassRegistry[name].deserialize(obj[val.name]); |
||
463 | } |
||
464 | } |
||
465 | } else { |
||
466 | throw 'Class ' + name + ' not currently in scope!'; |
||
467 | } |
||
468 | } else if (typeof (obj[val.name]) === 'object' && obj[val.name] !== null) { |
||
469 | |||
470 | var currentValue = null; |
||
0 ignored issues
–
show
|
|||
471 | if (typeof (this[getter_name]) === 'function') { |
||
472 | // use getter if it exists. This allows custom getters to translate the data properly if needed. |
||
473 | currentValue = this[getter_name](); |
||
474 | } else { |
||
475 | currentValue = this[val.name]; |
||
476 | } |
||
477 | |||
478 | // normal object. Crawl through it to find JOII objects. |
||
479 | var new_val = JOII.Compat.inflateObject(obj[val.name], currentValue); |
||
480 | |||
481 | if (typeof (this[setter_name]) === 'function') { |
||
482 | // use setter if it exists. This allows custom setters to translate the data properly. |
||
483 | this[setter_name](new_val); |
||
484 | } else { |
||
485 | this[val.name] = new_val; |
||
486 | } |
||
487 | } else { |
||
488 | if (typeof (this[setter_name]) === 'function') { |
||
489 | // use setter if it exists. This allows custom setters to translate the data properly. |
||
490 | this[setter_name](obj[val.name]); |
||
491 | } else { |
||
492 | this[val.name] = obj[val.name]; |
||
493 | } |
||
494 | } |
||
495 | } |
||
496 | } |
||
497 | } |
||
498 | }; |
||
499 | // uses an inheritance style add, so it won't overwrite custom functions with the same signature |
||
500 | deserialize_meta = JOII.ParseClassProperty('public function deserialize(object)'); |
||
501 | JOII.addFunctionToPrototype(definition.prototype, deserialize_meta, generated_fn, true); |
||
502 | |||
503 | }; |
||
0 ignored issues
–
show
|
|||
504 | |||
505 | } |
||
506 | |||
507 | |||
508 | |||
509 | |||
510 | // if it's not a static class, generate it's static backing field |
||
511 | if (!is_static_generated && typeof (parameters['enum']) !== 'string') { |
||
512 | |||
513 | var __in_joii_static_class_constructor = false; |
||
514 | |||
515 | function staticDefinition() { |
||
0 ignored issues
–
show
It is not recommended to place function declarations in blocks.
Although, declaring functions inside blocks will not necessarily lead to runtime errors, the code might not behave as you expect it as declarations are automatically declared in the top-most scope (see ECMAScript specification). For code such as: function someFunction() {
function anotherFunction() { }
}
it is recommended to make one of these two fixes: 1. Move Function Declaration outside of the blockfunction someFunction() { }
function anotherFunction() { }
2. Use a Function Expressionfunction someFunction() {
var anotherFunction = function() { };
}
![]() |
|||
516 | |||
517 | var func_in = function() { }; |
||
518 | func_in.prototype = this; |
||
519 | var scope_in_obj = new func_in(); |
||
520 | |||
521 | static_scope_in.prototype = definition; |
||
522 | |||
523 | // Create an inner static scope, for private/protected members |
||
524 | var scope_in = generateInnerScope(this, [], scope_in_obj, true); |
||
525 | |||
526 | // Create the static field, and copy it into the object we created before. |
||
527 | // Need to copy it this way, so that the object reference is still the same, |
||
528 | // since we may have passed it into the optional user function which generates the body |
||
529 | static_scope_in = JOII.Compat.extend(true, static_scope_in, scope_in); |
||
0 ignored issues
–
show
|
|||
530 | |||
531 | // bind any public static members to the outside class |
||
532 | //bindPublicMethods(static_scope_in, definition); |
||
533 | |||
534 | definition = generateOuterScope(static_scope_in, static_scope_in, definition); |
||
0 ignored issues
–
show
|
|||
535 | |||
536 | // need to link the inner and outer scopes before calling constructors |
||
537 | linkAPI(static_scope_in, definition); |
||
538 | |||
539 | // static constructors can't have parameters |
||
540 | callConstructors(static_scope_in, []); |
||
541 | |||
542 | return static_scope_in; |
||
543 | } |
||
544 | |||
545 | __in_joii_static_class_constructor = true; |
||
546 | |||
547 | // Apply to prototype to the instantiator to allow extending the |
||
548 | // class definition upon other definitions without instantiation. |
||
549 | staticDefinition.prototype = JOII.PrototypeBuilder(name, parameters, body, false, true); |
||
550 | |||
551 | // Store defined interfaces in the metadata. |
||
552 | staticDefinition.prototype.__joii__.interfaces = parameters['implements']; |
||
553 | |||
554 | bindGetInterfaces(staticDefinition.prototype.__joii__); |
||
555 | |||
556 | // If any interfaces are implemented in this class, validate them |
||
557 | // immediately rather than doing so during instantiation. If the |
||
558 | // class is declared abstract, the validation is skipped. |
||
559 | if (parameters.abstract !== true) { |
||
560 | var interfaces = staticDefinition.prototype.__joii__.getInterfaces(); |
||
0 ignored issues
–
show
|
|||
561 | for (var ii in interfaces) { |
||
0 ignored issues
–
show
|
|||
562 | if (interfaces.hasOwnProperty(ii) && typeof (interfaces[ii]) === 'function') { |
||
563 | interfaces[ii](staticDefinition); |
||
564 | } |
||
565 | } |
||
566 | } |
||
567 | |||
568 | /** |
||
569 | * Deserializes a class (called as a static method - instantiates a new object and populates it) |
||
570 | * |
||
571 | * @param {String} |
||
572 | */ |
||
573 | var generated_fn = function(json) { |
||
0 ignored issues
–
show
|
|||
574 | return this.deserialize(JSON.parse(json)); |
||
575 | }; |
||
576 | // uses an inheritance style add, so it won't overwrite custom functions with the same signature |
||
577 | var deserialize_meta = JOII.ParseClassProperty('public static function deserialize(string)'); |
||
0 ignored issues
–
show
|
|||
578 | JOII.addFunctionToPrototype(staticDefinition.prototype, deserialize_meta, generated_fn, true); |
||
579 | |||
580 | |||
581 | /** |
||
582 | * Deserializes a class (called as a static method - instantiates a new object and populates it) |
||
583 | * |
||
584 | * @param {Object} |
||
585 | */ |
||
586 | generated_fn = function(obj) { |
||
587 | var deserialize_object = { |
||
588 | '__joii_deserialize_object': true, |
||
589 | 'data': obj |
||
590 | }; |
||
591 | return new definition(deserialize_object); |
||
592 | }; |
||
593 | // uses an inheritance style add, so it won't overwrite custom functions with the same signature |
||
594 | deserialize_meta = JOII.ParseClassProperty('public static function deserialize(object)'); |
||
595 | JOII.addFunctionToPrototype(staticDefinition.prototype, deserialize_meta, generated_fn, true); |
||
596 | |||
597 | |||
598 | /** |
||
599 | * Gets the current static scope |
||
600 | * |
||
601 | * @param {String} |
||
602 | */ |
||
603 | /* |
||
604 | var generated_fn = function() { |
||
605 | return static_scope_in; |
||
606 | }; |
||
607 | // uses an inheritance style add, so it won't overwrite custom functions with the same signature |
||
608 | var deserialize_meta = JOII.ParseClassProperty('private function getStatic()'); |
||
609 | JOII.addFunctionToPrototype(definition.prototype, deserialize_meta, generated_fn, true); |
||
610 | */ |
||
611 | |||
612 | // create an object for the static members, and store a reference to it |
||
613 | inner_static_objects[name] = new staticDefinition(); |
||
614 | |||
615 | |||
616 | definition.__joii__.prototype = staticDefinition.prototype; |
||
617 | |||
618 | |||
619 | } |
||
620 | |||
621 | |||
622 | |||
623 | |||
624 | // Register the class by the given name to make it usable as a type |
||
625 | // inside property declarations. |
||
626 | if (typeof (JOII.ClassRegistry[name]) !== 'undefined') { |
||
627 | throw 'Another class named "' + name + '" already exists.'; |
||
628 | } |
||
629 | JOII.ClassRegistry[name] = definition; |
||
630 | |||
631 | |||
632 | |||
633 | definition.prototype = JOII.Compat.extend(true, {}, definition.prototype); |
||
634 | |||
635 | return definition; |
||
636 | }; |
||
637 |