view jsm-in-web.js @ 7:c1f1d5ffd6f2 default tip

An engine can now be created.
author Atul Varma <varmaa@toolness.com>
date Thu, 09 Apr 2009 08:30:19 -0700
parents cb390cc6efa2
children
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) {
        var obj = {};
        gJsmReg.import(jsm.url, obj);
        gJsmReg.import("resource://weave/util.js", obj);
        obj.Utils.jsonLoad = function fakeJsonLoad() {};
        var engine = new obj.Engine();
        console.log("Import successful.");
      });
  });

JsModuleRegistry.IMPORT_RE = (/\.import\(['"]([^'"]+)/g);