Mercurial > jsm-in-web
view jsm-in-web.js @ 6:cb390cc6efa2
Switched to injecting code into iframes asynchronously via script tags rather than synchronously via window.eval(), b/c the latter didn't always result in proper evaluation (e.g., when "foo = 1" was eval'd, 'foo' was attached to the global object of the caller rather than the window object).
Also added fake Observers and Preferences JS modules.
author | Atul Varma <varmaa@toolness.com> |
---|---|
date | Wed, 08 Apr 2009 22:42:13 -0700 |
parents | f08437f96401 |
children | c1f1d5ffd6f2 |
line wrap: on
line source
function JsModuleRegistry(win, resourceMapper) { var self = this; var modules = {}; function loadIframe(cb) { var doc = win.document; var iframe = doc.createElement("iframe"); iframe.setAttribute("src", "blank.html"); iframe.style.display = "none"; iframe.addEventListener( "load", function onLoad() { iframe.removeEventListener("load", onLoad, true); cb(iframe); }, true ); doc.body.appendChild(iframe); } function getCodeForJsModule(url, cb) { var realUri = resourceMapper(url); jQuery.get( realUri, function(code) { code = code.replace(/Components/g, "FakeComponents"); code = ("/* " + url + " (modified for use in content space) */" + code); cb(code); } ); } function findDependencies(code) { var dependencies = []; for (var match = JsModuleRegistry.IMPORT_RE.exec(code); match != null; match = JsModuleRegistry.IMPORT_RE.exec(code)) { dependencies.push(match[1]); } return dependencies; } function injectCodeIntoWindow(code, window, cb) { var doc = window.document; var script = doc.createElement("script"); var dataUrl = "data:application/javascript,"; dataUrl += encodeURI(code); script.setAttribute("type", "application/javascript;version=1.7"); script.setAttribute("src", dataUrl); script.addEventListener( "load", function onLoad() { script.removeEventListener("load", onLoad, false); cb(); }, false); doc.body.appendChild(script); } self.import = function JSMR_import(url, obj) { //console.log("Importing " + url); var module = modules[url]; if (!module) throw new Error("Module at url not fetched: " + url); var exported = module.window.EXPORTED_SYMBOLS; if (!exported) throw new Error("EXPORTED_SYMBOLS is " + exported + " for " + url); exported.forEach( function(symbol) { //console.log("Exporting symbol " + symbol); obj[symbol] = module.window[symbol]; if (typeof(obj[symbol]) == "undefined") throw new Error("Symbol is undefined: " + symbol); }); }; self.fetch = function JSMR_fetch(url, cb) { //console.log("Fetching " + url); var jsModule = { url: url, isBeingFetched: true, hasBeenImported: false, fetchCallbacks: [] }; modules[url] = jsModule; function onMaybeDone() { if (jsModule.code && jsModule.window) { jsModule.isBeingFetched = false; //console.log("Evaluating code for " + url); injectCodeIntoWindow( jsModule.code, jsModule.window, function() { jsModule.fetchCallbacks.forEach( function(fetchCb) { fetchCb(); }); jsModule.fetchCallbacks = null; cb(jsModule); }); } } getCodeForJsModule( url, function(code) { var dependencies = findDependencies(code); function doneResolvingDeps() { jsModule.code = code; jsModule.dependencies = dependencies; onMaybeDone(); } if (dependencies.length) { var depsLeft = dependencies.length; function onDepResolved() { depsLeft -= 1; if (!depsLeft) doneResolvingDeps(); } dependencies.forEach( function(dependency) { var depMod = modules[dependency]; if (depMod) { if (!depMod.isBeingFetched) onDepResolved(); else depMod.fetchCallbacks.push(onDepResolved); } else self.fetch(dependency, onDepResolved); }); } else doneResolvingDeps(); }); loadIframe( function(iframe) { var window = iframe.contentWindow; jsModule.window = window; window.FakeComponents = { interfaces: {}, classes: {}, utils: { "import": function FCU_import(url, obj) { if (!obj) obj = window; self.import(url, obj); } } }; onMaybeDone(); }); }; self.modules = modules; } var gJsmReg; $(window).ready( function() { function mapWeaveResourceUri(uri) { switch (uri) { case "resource://gre/modules/XPCOMUtils.jsm": return "fake_XPCOMUtils.js"; case "resource://weave/ext/Preferences.js": return "fake_Preferences.js"; case "resource://weave/ext/Observers.js": return "fake_Observers.js"; case "resource://weave/constants.js": return "modules/constants.js.in"; default: var match = uri.match(/resource:\/\/weave\/(.*)/); if (match) { var filename = match[1]; filename = "modules/" + filename; return filename; } else throw new Error("Don't know how to map " + uri); } } gJsmReg = new JsModuleRegistry(window, mapWeaveResourceUri); gJsmReg.fetch( "resource://weave/engines.js", function(jsm) { gJsmReg.import(jsm.url, {}); console.log("Import successful."); }); }); JsModuleRegistry.IMPORT_RE = (/\.import\(['"]([^'"]+)/g);