UNPKG

25.3 kBJavaScriptView Raw
1// Generated by CoffeeScript 1.12.7
2(function() {
3 "use strict";
4 var BaseClass, ExtendablePropertyMixin, Log, MinimalBaseObject, StandardLib, Unique, WebpackHotLoader, callStack, capitalize, concatInto, decapitalize, functionName, getModuleBeingDefined, getSuperclass, inspectedObjectLiteral, isFunction, isPlainArray, isPlainObject, isString, log, merge, mergeInto, neq, nextUniqueObjectId, object, objectName,
5 extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
6 hasProp = {}.hasOwnProperty,
7 indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
8
9 StandardLib = require('art-standard-lib');
10
11 WebpackHotLoader = require('./WebpackHotLoader');
12
13 capitalize = StandardLib.capitalize, decapitalize = StandardLib.decapitalize, log = StandardLib.log, isFunction = StandardLib.isFunction, objectName = StandardLib.objectName, isPlainObject = StandardLib.isPlainObject, functionName = StandardLib.functionName, isString = StandardLib.isString, isPlainArray = StandardLib.isPlainArray, Unique = StandardLib.Unique, callStack = StandardLib.callStack, Log = StandardLib.Log, log = StandardLib.log, inspectedObjectLiteral = StandardLib.inspectedObjectLiteral, MinimalBaseObject = StandardLib.MinimalBaseObject, getModuleBeingDefined = StandardLib.getModuleBeingDefined, concatInto = StandardLib.concatInto, mergeInto = StandardLib.mergeInto, merge = StandardLib.merge, neq = StandardLib.neq, isString = StandardLib.isString, object = StandardLib.object, getSuperclass = StandardLib.getSuperclass;
14
15 nextUniqueObjectId = Unique.nextUniqueObjectId;
16
17 ExtendablePropertyMixin = require('./ExtendablePropertyMixin');
18
19 module.exports = BaseClass = (function(superClass) {
20 var createWithPostCreate, getSingleton, imprintObject, nonImprintableProps, thoroughDeleteProperty, warnedAboutIncludeOnce;
21
22 extend(BaseClass, superClass);
23
24 BaseClass.resetStats = function() {
25 BaseClass.objectsCreated = 0;
26 return BaseClass.objectsCreatedByType = {};
27 };
28
29 BaseClass.resetStats();
30
31 BaseClass._name = null;
32
33
34 /*
35 NOTE: only hasOwnProperties are considered! Inherited properties are not touched.
36 IN:
37 targetObject: object will be altered to be an "imprint" of fromObject
38 fromObject: object pattern used to imprint targetObject
39 preserveState:
40 false:
41 targetObject has every property updated to exactly match fromObject
42
43 This includes:
44 1. delete properties in targetObject that are not in fromObject
45 2. add every property in fromObject but not in targetObject
46 3. overwriting every property in targetObject also in fromObject
47
48 true:
49 Attempts to preserve the state of targetObject while updating its functionality.
50 This means properties which are functions in either object are updated.
51
52 WARNING: This is a grey area for JavaScript. It is not entirely clear what is
53 state and what is 'functionality'. I, SBD, have made the following heuristic decisions:
54
55 Imprint actions taken when preserving State:
56
57 1. DO NOTHING to properties in targetObject that are not in fromObject
58 2. add every property in fromObject but not in targetObject
59 3. properties in targetObject that are also in fromObject are updated
60 if one of the following are true:
61 - isFunction fromObject[propName]
62 - isFunction targetObject[propName]
63 - propName does NOT start with "_"
64 NOTE: property existance is detected using Object.getOwnPropertyDescriptor
65 */
66
67 thoroughDeleteProperty = function(object, propName) {
68 Object.defineProperty(object, propName, {
69 configurable: true,
70 writable: false,
71 value: 1
72 });
73 return delete object[propName];
74 };
75
76 nonImprintableProps = ["__proto__", "prototype"];
77
78 BaseClass.imprintObject = imprintObject = function(targetObject, sourceObject, preserveState, returnActionsTaken) {
79 var addedProps, changedProps, i, j, len, len1, neqResult, removedProps, sourcePropDescriptor, sourcePropName, sourcePropertyNames, sourceValue, sourceValueIsFunction, targetPropDescriptor, targetPropName, targetPropertyNames, targetValue, targetValueIsFunction;
80 if (preserveState == null) {
81 preserveState = false;
82 }
83 targetPropertyNames = Object.getOwnPropertyNames(targetObject);
84 sourcePropertyNames = Object.getOwnPropertyNames(sourceObject);
85 if (returnActionsTaken) {
86 addedProps = removedProps = changedProps = void 0;
87 }
88 if (!preserveState) {
89 for (i = 0, len = targetPropertyNames.length; i < len; i++) {
90 targetPropName = targetPropertyNames[i];
91 if (!(!(indexOf.call(sourcePropertyNames, targetPropName) >= 0))) {
92 continue;
93 }
94 if (returnActionsTaken) {
95 (removedProps != null ? removedProps : removedProps = []).push(targetPropName);
96 }
97 thoroughDeleteProperty(targetObject, targetPropName);
98 }
99 }
100 for (j = 0, len1 = sourcePropertyNames.length; j < len1; j++) {
101 sourcePropName = sourcePropertyNames[j];
102 if (!(!(indexOf.call(nonImprintableProps, sourcePropName) >= 0))) {
103 continue;
104 }
105 targetPropDescriptor = Object.getOwnPropertyDescriptor(targetObject, sourcePropName);
106 sourcePropDescriptor = Object.getOwnPropertyDescriptor(sourceObject, sourcePropName);
107 sourceValueIsFunction = isFunction(sourceValue = sourcePropDescriptor.value);
108 targetValueIsFunction = isFunction(targetValue = targetPropDescriptor != null ? targetPropDescriptor.value : void 0);
109 if (!preserveState || !targetPropDescriptor || sourceValueIsFunction || targetValueIsFunction || !sourcePropName.match(/^_/)) {
110 if (returnActionsTaken) {
111 if (!targetPropDescriptor) {
112 if (sourcePropName !== "_name") {
113 (addedProps != null ? addedProps : addedProps = []).push(sourcePropName);
114 }
115 } else {
116 if (neqResult = neq(sourceValue, targetValue, true)) {
117 (changedProps != null ? changedProps : changedProps = []).push(sourcePropName);
118 }
119 }
120 }
121 Object.defineProperty(targetObject, sourcePropName, sourcePropDescriptor);
122 }
123 }
124 if (returnActionsTaken) {
125 return (removedProps || changedProps || addedProps) && merge({
126 removedProps: removedProps,
127 changedProps: changedProps,
128 addedProps: addedProps
129 });
130 } else {
131 return sourceObject;
132 }
133 };
134
135
136 /*
137 imprints both the class and its prototype.
138
139 preserved in spite of imprintObject's rules:
140 @namespace
141 @::constructor
142 */
143
144 BaseClass.imprintFromClass = function(updatedKlass, returnActionsTaken) {
145 var _name, classUpdates, namespace, namespacePath, oldConstructor, prototypeUpdates, ref;
146 if (updatedKlass !== this) {
147 ref = this, namespace = ref.namespace, namespacePath = ref.namespacePath, _name = ref._name;
148 oldConstructor = this.prototype.constructor;
149 classUpdates = imprintObject(this, updatedKlass, true, returnActionsTaken);
150 prototypeUpdates = imprintObject(this.prototype, updatedKlass.prototype, false, returnActionsTaken);
151 this.prototype.constructor = oldConstructor;
152 this.namespace = namespace;
153 this.namespacePath = namespacePath;
154 this._name = _name;
155 }
156 if (returnActionsTaken) {
157 return merge({
158 "class": classUpdates,
159 prototype: prototypeUpdates
160 });
161 } else {
162 return this;
163 }
164 };
165
166 BaseClass.getHotReloadKey = function() {
167 return this.getName();
168 };
169
170
171 /*
172 IN:
173 _module should be the CommonJS 'module'
174 klass: class object which extends BaseClass
175
176 liveClass:
177 On the first load, liveClass gets set.
178 Each subsequent hot-load UPDATES liveClass,
179 but liveClass always points to the initially created class object.
180
181 OUT: the result of the call to liveClass.postCreate()
182
183 postCreate is passed:
184 hotReloaded: # true if this is anything but the initial load
185 classModuleState:
186 liveClass: # the original liveClass
187 hotUpdatedFromClass: # the most recently hot-loaded class
188 hotReloadVersion: # number starting at 0 and incremented with each hot reload
189 _module: # the CommonJs module
190
191 EFFECTS:
192 The following two methods are invoked on liveClass:
193
194 if hot-reloading
195 liveClass.imprintFromClass klass
196
197 * always:
198 liveClass.postCreate hotReloaded, classModuleState, _module
199 */
200
201 BaseClass.createWithPostCreate = createWithPostCreate = function(a, b) {
202 var _module, klass;
203 klass = b ? (_module = a, b) : a;
204 _module || (_module = getModuleBeingDefined() || global.__definingModule);
205 if (!(klass != null ? klass.postCreate : void 0)) {
206 return klass;
207 }
208 if (!(_module != null ? _module.hot : void 0)) {
209 return klass.postCreate({
210 hotReloadEnabled: false,
211 hotReloaded: false,
212 classModuleState: {},
213 module: _module
214 }) || klass;
215 }
216 return WebpackHotLoader.runHot(_module, function(moduleState) {
217 var classModuleState, hotReloadKey, hotReloaded, liveClass, obj1, updates;
218 hotReloadKey = klass.getHotReloadKey();
219 if (classModuleState = moduleState[hotReloadKey]) {
220 liveClass = classModuleState.liveClass;
221 hotReloaded = true;
222 classModuleState.hotReloadVersion++;
223 classModuleState.hotUpdatedFromClass = klass;
224 liveClass.namespace._setChildNamespaceProps(liveClass.getName(), klass);
225 klass._name = liveClass._name;
226 liveClass.classModuleState = classModuleState;
227 updates = liveClass.imprintFromClass(klass, true);
228 log((
229 obj1 = {},
230 obj1["Art.ClassSystem.BaseClass " + (typeof liveClass.getName === "function" ? liveClass.getName() : void 0) + " HotReload"] = {
231 version: classModuleState.hotReloadVersion,
232 updates: updates
233 },
234 obj1
235 ));
236 } else {
237 hotReloaded = false;
238 klass._hotClassModuleState = moduleState[hotReloadKey] = klass.classModuleState = classModuleState = {
239 liveClass: liveClass = klass,
240 hotUpdatedFromClass: null,
241 hotReloadVersion: 0
242 };
243 }
244 return liveClass.postCreate({
245 hotReloadEnabled: true,
246 hotReloaded: hotReloaded,
247 classModuleState: classModuleState,
248 module: _module
249 });
250 });
251 };
252
253 BaseClass.createHotWithPostCreate = function(a, b) {
254 log.error("createHotWithPostCreate is DEPRICATED");
255 return createWithPostCreate(a, b);
256 };
257
258
259 /*
260 called every load
261 IN: options:
262 NOTE: hot-loading inputs are only set if this class created as follows:
263 createHotWithPostCreate module, class Foo extends BaseClass
264
265 hotReload: true/false
266 true if this class was hot-reloaded
267
268 hotReloadEnabled: true/false
269
270 classModuleState:
271 liveClass: the first-loaded version of the class.
272 This is the official version of the class at all times.
273 The hot-reloaded version of the class is "imprinted" onto the liveClass
274 but otherwise is not used (but can be accessed via classModuleState.hotUpdatedFromClass)
275 hotUpdatedFromClass: The most recently loaded version of the class.
276 hotReloadVersion: number, starting at 1, and counting up each load
277
278 classModuleState is a plain-object specific to the class and its CommonJS module. If there is
279 more than one hot-loaded class in the same module, each will have its own classModuleState.
280
281 SBD NOTE: Though we could allow clients to add fields to classModuleState, I think it works
282 just as well, and is cleaner, if any state is stored in the actual class objects and
283 persisted via postCreate.
284
285 module: the CommonJs module object.
286
287 {hotReloadEnabled, hotReloaded, classModuleState, module} = options
288 */
289
290 BaseClass.postCreate = function(options) {
291 if (this.getIsAbstractClass()) {
292 return this.postCreateAbstractClass(options);
293 } else {
294 return this.postCreateConcreteClass(options);
295 }
296 };
297
298 BaseClass.setNamespace = function(ns) {
299 return this._namespace = ns;
300 };
301
302 BaseClass.postCreateAbstractClass = function(options) {
303 return this;
304 };
305
306 BaseClass.postCreateConcreteClass = function(options) {
307 return this;
308 };
309
310 function BaseClass() {
311 this.__uniqueId = null;
312 }
313
314 BaseClass.implementsInterface = function(object, methods) {
315 var i, len, method;
316 for (i = 0, len = methods.length; i < len; i++) {
317 method = methods[i];
318 if (typeof object[method] !== "function") {
319 return false;
320 }
321 }
322 return true;
323 };
324
325
326 /*
327 mix-in class methods
328 Define getters/setters example:
329 class MyMixin
330 included: ->
331 @getter foo: -> @_foo
332 @setter foo: (v) -> @_foo = v
333
334 NOTE! This will NOT include any properties you defined with getter or setter!
335 NOTE! This only copies over values if there aren't already values in the included-into class
336 This somewhat mirrors Ruby's include where the included-into-class's methods take precidence.
337 However, if you include two modules in a row, the first module gets priority here.
338 In ruby the second module gets priority (I believe).
339
340 DEPRICATED!!!
341 Time to do it "right" - and it's just a simple pattern:
342 Justin Fagnani figured this out. Thanks!
343 Read More:
344 http://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/
345
346 To define a mixin:
347
348 MyMixin = (superClass) ->
349 class MyMixin extends superClass
350 ... write your mixin as-if it were part of the normal inheritance hierachy
351
352 To use a mixin:
353
354 class MyClass extends MyMixin MySuperClass
355
356 To use two mixins:
357
358 class MyClass extends MyMixin1 MyMixin2 MySuperClass
359 */
360
361 warnedAboutIncludeOnce = false;
362
363 BaseClass.include = function(obj) {
364 var key, ref, value;
365 log.error("DEPRICATED: BaseClass.include. Use pattern.");
366 if (!warnedAboutIncludeOnce) {
367 warnedAboutIncludeOnce = true;
368 console.warn("Mixin pattern:\n\n To define a mixin:\n\n MyMixin = (superClass) ->\n class MyMixin extends superClass\n ... write your mixin as-if it were part of the normal inheritance hierachy\n\n To use a mixin:\n\n class MyClass extends MyMixin MySuperClass\n\n To use two mixins:\n\n class MyClass extends MyMixin1 MyMixin2 MySuperClass");
369 }
370 for (key in obj) {
371 value = obj[key];
372 if (key !== 'included') {
373 if (!this[key]) {
374 this[key] = value;
375 }
376 }
377 }
378 ref = obj.prototype;
379 for (key in ref) {
380 value = ref[key];
381 if (key) {
382 if (!this.prototype[key]) {
383 this.prototype[key] = value;
384 }
385 }
386 }
387 if (typeof obj.included === "function") {
388 obj.included(this);
389 }
390 return this;
391 };
392
393 BaseClass.getNamespacePath = function() {
394 var ref;
395 if ((ref = this.namespacePath) != null ? ref.match(this.getName()) : void 0) {
396 return this.namespacePath;
397 } else {
398 return this.namespacePath = "(no parent namespace)." + (this.getName());
399 }
400 };
401
402 BaseClass.getNamespacePathWithExtendsInfo = function() {
403 return (this.getNamespacePath()) + " extends " + (getSuperclass(this).getNamespacePath());
404 };
405
406 BaseClass.getClassName = function(klass) {
407 if (klass == null) {
408 klass = this;
409 }
410 return (typeof klass.getName === "function" ? klass.getName() : void 0) || klass.name;
411 };
412
413
414 /*
415 inspect: ->
416 IN: ()
417 OUT: string
418
419 Can override with same or alternate, recursion-block-supported signature:
420 IN: (inspector) ->
421 OUT: if inspector then null else string
422
423 To handle the case where the inspector is not set, we
424 recommneded declaring your 'inspect' as follows:
425 inspect: (inspector) ->
426 return StandardLib.inspect @ unless inspector
427 * ...
428 * custom code which writes all output to inspector.put
429 * and uses inspector.inspect for inspecting sub-objects
430 * ...
431 null
432
433 EFFECT:
434 call inspector.put one or multiple times with strings to add to the inspected output
435 call inspector.inspect foo to sub-inspect other objects WITH RECURSION BLOCK
436
437 * Example 1:
438 inspect: (inspector) ->
439 return StandardLib.inspect @ unless inspector
440 inspector.put @getNamespacePath()
441
442 * Example 2:
443 inspect: ->
444 @getNamespacePath()
445 */
446
447 BaseClass.inspect = function() {
448 return this.getNamespacePath();
449 };
450
451 BaseClass.prototype.inspect = function() {
452 return "<" + this["class"].namespacePath + ">";
453 };
454
455
456 /*
457 getInspectedObjects: -> plainObjects
458
459 usually implemented this way:
460 @getter inspectedObjects: -> plainObjects or objects which implement "inspect"
461
462 TODO: I think I want to refactor inspectedObjects to ONLY return near-JSON-compatible objects:
463 1. strings
464 2. maps
465 3. arrays
466
467 Everything else should be rendered to a string. In general, strings should Eval to the object
468 they represent:
469
470 toInspectedObject(null): 'null' # null becomes a string
471 toInspectedObject(true): 'true' # true becomes a string
472 toInspectedObject(false): 'false' # false becomes a string
473 toInspectedObject(undefined): 'undefined' # undefined becomes a string
474 toInspectedObject('hi'): '"hi"' # ESCAPED
475 toInspectedObject((a) -> a): 'function(a){return a;}'
476 toInspectedObject(rgbColor()) "rgbColor('#000000')"
477
478 NOTE: inspectedObjects differs from plainObjects. The latter should be 100% JSON,
479 and should return actual values where JSON allows, otherwise, return JSON data structures
480 that encode the object's information in a human-readable format, ideally one that can be
481 used as an input to the constructor of the object's class to recreate the original object.
482
483 plainObjects:
484 null: null
485 true: true
486 false: false
487 'str': 'str' # NOT escaped
488 undefined: null
489 ((a) -> a): 'function(a){return a;}'
490 rgbColor(): r: 0, g: 0, b: 0, a: 0
491
492 You can provide this function for fine-grained control of what Inspector2 outputs and hence
493 what DomConsole displays.
494
495 If you would like for a string to appear without quotes, use:
496 {inspect: -> 'your string without quotes here'}
497 */
498
499 BaseClass.getter({
500 inspectObjects: function() {
501 console.warn("inspectObjects/getInspectObjects is DEPRICATED. Use: inspectedObjects/getInspectedObjects");
502 return this.getInspectedObjects();
503 },
504 inspectedObjects: function() {
505 var ref;
506 return inspectedObjectLiteral("<" + ((ref = this["class"]) != null ? ref.getNamespacePath() : void 0) + ">");
507 }
508 });
509
510 BaseClass.classGetter({
511 inspectedObjects: function() {
512 return inspectedObjectLiteral("class " + (this.getNamespacePath()));
513 }
514 });
515
516
517 /*
518 Define this class as an abstract class. Implicitly it means
519 any class it extends is also abstract, at least in this context.
520
521 Definition: Abstract classes are not intended to every be instantiated.
522 i.e.: never do: new MyAbstractClass
523
524 TODO: in Debug mode, in the constructor:
525 throw new Error "cannot instantiate abstract classes" if @class.getIsAbstractClass()
526 */
527
528 BaseClass.abstractClass = function() {
529 if (this.getIsSingletonClass()) {
530 throw new Error("abstract classes cannot also be singleton");
531 }
532 return this._firstAbstractAncestor = this;
533 };
534
535 BaseClass.classGetter({
536 superclass: function() {
537 return getSuperclass(this);
538 },
539 isAbstractClass: function() {
540 return !(this.prototype instanceof this._firstAbstractAncestor);
541 },
542 isConcreteClass: function() {
543 return !this.getIsAbstractClass();
544 },
545 abstractPrototype: function() {
546 return this._firstAbstractAncestor.prototype;
547 },
548 firstAbstractAncestor: function() {
549 return this._firstAbstractAncestor;
550 },
551 isSingletonClass: function() {
552 var ref;
553 return ((ref = this._singleton) != null ? ref["class"] : void 0) === this;
554 },
555 concretePrototypeProperties: function() {
556 var abstractClassPrototype;
557 abstractClassPrototype = this.getAbstractClass().prototype;
558 return object(this.prototype, {
559 when: function(v, k) {
560 return k !== "constructor" && abstractClassPrototype[k] !== v;
561 }
562 });
563 }
564 });
565
566 BaseClass.getAbstractClass = function() {
567 return this._firstAbstractAncestor;
568 };
569
570 BaseClass.abstractClass();
571
572 BaseClass.propertyIsAbstract = function(propName) {
573 return this.getAbstractClass().prototype[propName] === this.prototype[propName];
574 };
575
576 BaseClass.propertyIsConcrete = function(propName) {
577 return this.getAbstractClass().prototype[propName] !== this.prototype[propName];
578 };
579
580
581 /*
582 SBD2017: this is the new path for singleton classes.
583 WHY: We can elliminate the need to DECLARE classes singleton.
584 Instead, we can just access the singleton for any class, if needed.
585 TODO: once we are 100% CaffeineScript, switch this to a @classGetter
586 */
587
588 BaseClass.getSingleton = getSingleton = function() {
589 var ref;
590 if (((ref = this._singleton) != null ? ref["class"] : void 0) === this) {
591 return this._singleton;
592 } else {
593 if (this.getIsAbstractClass()) {
594 throw new Error("singleton classes cannot be abstract");
595 }
596 return this._singleton = new this;
597 }
598 };
599
600
601 /*
602 creates the classGetter "singleton" which returns a single instance of the current class.
603
604 IN: args are passed to the singleton constructor
605 OUT: null
606
607 The singleton instance is created on demand the first time it is accessed.
608
609 SBD2017: Possibly depricated; maybe we just need a singleton getter for everyone?
610 The problem is coffeescript doesn't properly inherit class getters.
611 BUT ES6 and CaffeineScript DO. So, when we switch over, I think we can do this.
612 */
613
614 BaseClass.singletonClass = function() {
615 var obj1;
616 if (this.getIsAbstractClass()) {
617 throw new Error("singleton classes cannot be abstract");
618 }
619 this.classGetter((
620 obj1 = {
621 singleton: getSingleton
622 },
623 obj1["" + (decapitalize(functionName(this)))] = function() {
624 return this.getSingleton();
625 },
626 obj1
627 ));
628 return null;
629 };
630
631 BaseClass.getter({
632 className: function() {
633 return this["class"].getClassName();
634 },
635 "class": function() {
636 return this.constructor;
637 },
638 keys: function() {
639 return Object.keys(this);
640 },
641 namespacePath: function() {
642 return this["class"].getNamespacePath();
643 },
644 classPathName: function() {
645 return this.namespacePath;
646 },
647 classPathNameAndId: function() {
648 return this.classPathName + ":" + this.objectId;
649 },
650 uniqueId: function() {
651 return this.__uniqueId || (this.__uniqueId = nextUniqueObjectId());
652 },
653 objectId: function() {
654 return this.__uniqueId || (this.__uniqueId = nextUniqueObjectId());
655 }
656 });
657
658 BaseClass.prototype.freeze = function() {
659 this.getUniqueId();
660 Object.freeze(this);
661 return this;
662 };
663
664 BaseClass.prototype.implementsInterface = function(methods) {
665 return Function.BaseClass.implementsInterface(this, methods);
666 };
667
668 BaseClass.prototype.tap = function(f) {
669 f(this);
670 return this;
671 };
672
673 return BaseClass;
674
675 })(ExtendablePropertyMixin(MinimalBaseObject));
676
677}).call(this);
678
679//# sourceMappingURL=BaseClass.js.map