1 // Copyright (c) 2009-2011 by Kris Maglione <maglione.k@gmail.com>
3 // This work is licensed for reuse under an MIT license. Details are
4 // given in the LICENSE.txt file included with this file.
9 Components.utils.import("resource://dactyl/bootstrap.jsm");
10 defineModule("overlay", {
11 exports: ["ModuleBase"],
12 require: ["config", "io", "services", "util"]
17 * The base class for all modules.
19 var ModuleBase = Class("ModuleBase", {
21 * @property {[string]} A list of module prerequisites which
22 * must be initialized before this module is loaded.
26 toString: function () "[module " + this.constructor.className + "]"
29 var Overlay = Module("Overlay", {
30 init: function init() {
31 services["dactyl:"]; // Hack. Force module initialization.
35 util.overlayWindow(config.overlayChrome, function overlay(window) ({
36 init: function onInit(document) {
40 * Constructs a new ModuleBase class and makes arrangements for its
41 * initialization. Arguments marked as optional must be either
42 * entirely elided, or they must have the exact type specified.
43 * Loading semantics are as follows:
45 * - A module is guaranteed not to be initialized before any of its
46 * prerequisites as listed in its {@see ModuleBase#requires} member.
47 * - A module is considered initialized once it's been instantiated,
48 * its {@see Class#init} method has been called, and its
49 * instance has been installed into the top-level {@see modules}
51 * - Once the module has been initialized, its module-dependent
52 * initialization functions will be called as described hereafter.
53 * @param {string} name The module's name as it will appear in the
54 * top-level {@see modules} object.
55 * @param {ModuleBase} base The base class for this module.
57 * @param {Object} prototype The prototype for instances of this
58 * object. The object itself is copied and not used as a prototype
60 * @param {Object} classProperties The class properties for the new
63 * @param {Object} moduleInit The module initialization functions
64 * for the new module. Each function is called as soon as the named module
65 * has been initialized, but after the module itself. The constructors are
66 * guaranteed to be called in the same order that the dependent modules
70 * @returns {function} The constructor for the resulting module.
72 function Module(name) {
73 let args = Array.slice(arguments);
75 var base = ModuleBase;
76 if (callable(args[1]))
77 base = args.splice(1, 1)[0];
78 let [, prototype, classProperties, moduleInit] = args;
79 const module = Class(name, base, prototype, classProperties);
81 module.INIT = moduleInit || {};
82 module.modules = modules;
83 module.prototype.INIT = module.INIT;
84 module.requires = prototype.requires || [];
85 Module.list.push(module);
86 Module.constructors[name] = module;
90 Module.constructors = {};
92 const BASE = "resource://dactyl-content/";
94 const create = window.Object.create || (function () {
95 window.__dactyl_eval_string = "(function (proto) ({ __proto__: proto }))";
96 JSMLoader.loadSubScript(BASE + "eval.js", window);
98 let res = window.__dactyl_eval_result;
99 delete window.__dactyl_eval_string;
100 delete window.__dactyl_eval_result;
104 const jsmodules = { NAME: "jsmodules" };
105 const modules = update(create(jsmodules), {
106 yes_i_know_i_should_not_report_errors_in_these_branches_thanks: [],
108 jsmodules: jsmodules,
110 get content() this.config.browser.contentWindow || window.content,
116 load: function load(script) {
117 for (let [i, base] in Iterator(prefix)) {
119 JSMLoader.loadSubScript(base + script + ".js", modules, "UTF-8");
123 if (typeof e !== "string") {
124 util.dump("Trying: " + (base + script + ".js") + ":");
130 require(jsmodules, script);
133 util.dump("Loading script " + script + ":");
138 newContext: function newContext(proto, normal) {
140 return create(proto);
141 let sandbox = Components.utils.Sandbox(window, { sandboxPrototype: proto || modules, wantXrays: false });
143 sandbox.Object = jsmodules.Object;
144 sandbox.Math = jsmodules.Math;
145 sandbox.__proto__ = proto || modules;
149 get ownPropertyValues() array.compact(
150 Object.getOwnPropertyNames(this)
151 .map(function (name) Object.getOwnPropertyDescriptor(this, name).value, this)),
153 get moduleList() this.ownPropertyValues.filter(function (mod) mod instanceof this.ModuleBase || mod.isLocalModule, this)
155 modules.plugins = create(modules);
156 modules.modules = modules;
157 window.dactyl = { modules: modules };
159 let prefix = [BASE, "resource://dactyl-local-content/"];
161 defineModule.time("load", null, function _load() {
183 ].forEach(function (name) defineModule.time("load", name, require, null, jsmodules, name));
198 ].forEach(function (name) defineModule.time("load", name, modules.load, modules, name));
201 load: function onLoad(document) {
202 // This is getting to be horrible. --Kris
204 var { modules, Module } = window.dactyl.modules;
205 delete window.dactyl;
207 const start = Date.now();
208 const deferredInit = { load: {} };
210 const loaded = Set();
211 modules.loaded = loaded;
213 function load(module, prereq, frame) {
214 if (isString(module)) {
215 if (!Module.constructors.hasOwnProperty(module))
216 modules.load(module);
217 module = Module.constructors[module];
221 if (module.className in loaded)
223 if (module.className in seen)
224 throw Error("Module dependency loop.");
225 Set.add(seen, module.className);
227 for (let dep in values(module.requires))
228 load(Module.constructors[dep], module.className);
230 defineModule.loadLog.push("Load" + (isString(prereq) ? " " + prereq + " dependency: " : ": ") + module.className);
231 if (frame && frame.filename)
232 defineModule.loadLog.push(" from: " + util.fixURI(frame.filename) + ":" + frame.lineNumber);
234 let obj = defineModule.time(module.className, "init", module);
235 Class.replaceProperty(modules, module.className, obj);
236 loaded[module.className] = true;
238 if (loaded.dactyl && obj.signals)
239 modules.dactyl.registerObservers(obj);
241 frob(module.className);
244 util.dump("Loading " + (module && module.className) + ":");
247 return modules[module.className];
250 function deferInit(name, INIT, mod) {
251 let init = deferredInit[name] = deferredInit[name] || {};
252 let className = mod.className || mod.constructor.className;
254 init[className] = function callee() {
256 defineModule.time(className, name, INIT[name], mod,
257 modules.dactyl, modules, window);
258 callee.frobbed = true;
261 INIT[name].require = function (name) { init[name](); };
264 function frobModules() {
265 Module.list.forEach(function frobModule(mod) {
267 modules.__defineGetter__(mod.className, function () {
268 delete modules[mod.className];
269 return load(mod.className, null, Components.stack.caller);
271 Object.keys(mod.prototype.INIT)
272 .forEach(function (name) { deferInit(name, mod.prototype.INIT, mod); });
277 defineModule.modules.forEach(function defModule(mod) {
278 let names = Set(Object.keys(mod.INIT));
279 if ("init" in mod.INIT)
280 Set.add(names, "init");
282 keys(names).forEach(function (name) { deferInit(name, mod.INIT, mod); });
285 function frob(name) { values(deferredInit[name] || {}).forEach(call); }
289 modules.config.scripts.forEach(modules.load);
292 defineModule.modules.forEach(function defModule({ lazyInit, constructor: { className } }) {
295 Class.replaceProperty(modules, className, modules[className]);
298 modules.__defineGetter__(className, function () {
299 delete modules[className];
301 return modules[className] = modules[className];
305 // Module.list.forEach(load);
307 modules.times = update({}, defineModule.times);
309 defineModule.loadLog.push("Loaded in " + (Date.now() - start) + "ms");
311 modules.events.listen(window, "unload", function onUnload() {
312 window.removeEventListener("unload", onUnload.wrapped, false);
314 for each (let mod in modules.moduleList.reverse()) {
317 if ("destroy" in mod)
318 util.trapErrors("destroy", mod);
328 } catch(e){ if (!e.stack) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
330 // vim: set fdm=marker sw=4 ts=4 et ft=javascript: