-
Notifications
You must be signed in to change notification settings - Fork 193
/
Copy pathmodules.js
135 lines (122 loc) · 4.8 KB
/
modules.js
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
// Copyright (c) 2009 by Kris Maglione <[email protected]>
//
// This work is licensed for reuse under an MIT license. Details are
// given in the License.txt file included with this file.
/**
* @class ModuleBase
* The base class for all modules.
*/
const ModuleBase = Class("ModuleBase", {
/**
* @property {[string]} A list of module prerequisites which
* must be initialized before this module is loaded.
*/
requires: [],
toString: function () "[module " + this.constructor.name + "]"
});
/**
* @constructor Module
*
* Constructs a new ModuleBase class and makes arrangements for its
* initialization. Arguments marked as optional must be either
* entirely elided, or they must have the exact type specified.
* Loading semantics are as follows:
*
* - A module is guaranteed not to be initialized before any of its
* prerequisites as listed in its {@see ModuleBase#requires} member.
* - A module is considered initialized once it's been instantiated,
* its {@see Class#init} method has been called, and its
* instance has been installed into the top-level {@see modules}
* object.
* - Once the module has been initialized, its module-dependent
* initialization functions will be called as described hereafter.
* @param {string} name The module's name as it will appear in the
* top-level {@see modules} object.
* @param {ModuleBase} base The base class for this module.
* @optional
* @param {Object} prototype The prototype for instances of this
* object. The object itself is copied and not used as a prototype
* directly.
* @param {Object} classProperties The class properties for the new
* module constructor.
* @optional
* @param {Object} moduleInit The module initialization functions
* for the new module. Each function is called as soon as the named module
* has been initialized, but after the module itself. The constructors are
* guaranteed to be called in the same order that the dependent modules
* were initialized.
* @optional
*
* @returns {function} The constructor for the resulting module.
*/
function Module(name, prototype, classProperties, moduleInit) {
var base = ModuleBase;
if (callable(prototype))
base = Array.splice(arguments, 1, 1)[0];
const module = Class(name, base, prototype, classProperties);
module.INIT = moduleInit || {};
module.requires = prototype.requires || [];
Module.list.push(module);
Module.constructors[name] = module;
return module;
}
Module.list = [];
Module.constructors = {};
window.addEventListener("load", function onload() {
window.removeEventListener("load", onload, false);
function dump(str) window.dump(String.replace(str, /\n?$/, "\n").replace(/^/m, Config.prototype.name.toLowerCase() + ": "))
const start = Date.now();
const deferredInit = { load: [] };
const seen = new Set();
const loaded = [];
function load(module, prereq) {
try {
if (module.name in modules)
return;
if (seen.has(module.name))
throw Error("Module dependency loop");
seen.add(module.name);
for (let dep of module.requires)
load(Module.constructors[dep], module.name);
dump("Load" + (isstring(prereq) ? " " + prereq + " dependency: " : ": ") + module.name);
modules[module.name] = module();
loaded.push(module.name);
function init(mod, module)
function () module.INIT[mod].call(modules[module.name], modules[mod])
for (let mod of loaded) {
try {
if (mod in module.INIT)
init(mod, module)();
delete module.INIT[mod];
}
catch (e) {
if (modules.liberator)
liberator.echoerr(e);
}
}
for (let mod of Object.keys(module.INIT)) {
deferredInit[mod] = deferredInit[mod] || [];
deferredInit[mod].push(init(mod, module));
}
for (let fn of deferredInit[module.name] || [])
fn();
}
catch (e) {
dump("Loading " + (module && module.name) + ": " + e + "\n");
if (e.stack)
dump(e.stack);
}
}
Module.list.forEach(load);
deferredInit.load.forEach(call);
for (let module of Module.list)
delete module.INIT;
dump("Loaded in " + (Date.now() - start) + "ms\n");
}, false);
window.addEventListener("unload", function onunload() {
window.removeEventListener("unload", onunload, false);
for (let [, mod] in iter(modules))
if (mod instanceof ModuleBase && "destroy" in mod)
mod.destroy();
}, false);
// vim: set fdm=marker sw=4 ts=4 et: