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 : {}; |
||
9 | JOII.ClassRegistry = {}; |
||
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() { |
||
26 | var args = JOII.Compat.ParseArguments(arguments), |
||
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() { |
||
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); |
||
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); |
||
86 | } |
||
87 | |||
88 | JOII.callMetaMixin('afterNew', scope_in, scope_out); |
||
89 | |||
90 | |||
91 | callConstructors(scope_in, arguments); |
||
92 | |||
93 | return scope_out; |
||
94 | } |
||
95 | |||
96 | function callConstructors(scope_in, args) |
||
97 | { |
||
98 | // Does the class defintion have a constructor? If so, run it. |
||
99 | for (var c in JOII.Config.constructors) { |
||
100 | if (JOII.Config.constructors.hasOwnProperty(c)) { |
||
101 | var cc = JOII.Config.constructors[c]; |
||
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) |
||
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) { |
||
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); |
||
143 | |||
144 | if (typeof scope !== 'undefined') { |
||
145 | JOII.CreateProperty(scope_in, '__joii__', (scope.__joii__)); |
||
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) { |
||
166 | if (JOII.Config.callables.hasOwnProperty(c)) { |
||
167 | if (typeof (static_scope_in[JOII.Config.callables[c]]) === 'function') { |
||
168 | var result = static_scope_in[JOII.Config.callables[c]].apply(body, args); |
||
169 | if (result === body) { |
||
170 | throw JOII.Config.callables[c] + ' cannot return itself.'; |
||
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) { |
||
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__)); |
||
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) |
||
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); |
||
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); |
||
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); |
||
259 | } |
||
260 | |||
261 | // Does the class implement an enumerator? |
||
262 | if (typeof (parameters['enum']) === 'string') { |
||
263 | var e = JOII.EnumBuilder(parameters['enum'], definition); |
||
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) |
||
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() { |
||
299 | var interfaces = [], |
||
300 | getRealInterface = JOII.Compat.Bind(function(i) { |
||
301 | if (typeof (i) === 'function') { |
||
302 | return i; |
||
303 | } else if (typeof (i) === 'string') { |
||
304 | if (typeof (JOII.InterfaceRegistry[i]) === 'undefined') { |
||
305 | throw 'Interface "' + i + '" does not exist.'; |
||
306 | } |
||
307 | return JOII.InterfaceRegistry[i]; |
||
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()'); |
||
361 | JOII.addFunctionToPrototype(definition.prototype, serialize_meta, generated_fn, true); |
||
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) { |
||
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); |
||
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
|
|||
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)'); |
||
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) { |
||
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; |
||
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 | }; |
||
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() { |
||
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); |
||
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); |
||
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(); |
||
561 | for (var ii in interfaces) { |
||
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) { |
||
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)'); |
||
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 |
If JSHint finds too many errors in a file, it aborts checking altogether because it suspects a configuration issue.
Further Reading: