Skip to content

Instantly share code, notes, and snippets.

@guybedford
Forked from lukehoban/gist:2246758
Created May 9, 2013 07:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guybedford/5546050 to your computer and use it in GitHub Desktop.
Save guybedford/5546050 to your computer and use it in GitHub Desktop.
(function() {
window.Loader = function(parent, options) {
this.global = options.global || {};
this.baseURL = options.baseURL || parent.baseUrl;
this._parent = parent || null;
this._strict = options.strict || false;
this.normalize = options.normalize || parent.normalize;
this.resolve = options.resolve || parent.resolve;
this.fetch = options.fetch || parent.fetch;
this.translate = options.translate || parent.translate;
this.link = options.link || parent.link;
this._modules = {};
}
Loader.prototype = {
load: function(name, callback, errback) {
name = this.normalize(name);
if (this._modules[name])
return callback(this._modules[name]);
var opt = this.resolve(name);
var url = opt.name;
var self = this;
this.fetch(url, {
fulfill: function(source) {
source = self.translate(source, opt);
if (self._strict)
source = "'use strict';\n" + source;
var link = self.link(source, opts);
// 1. module
if (link instanceof Module) {
self._modules[name] = link;
return callback(link);
}
var imports, execute;
// 2. specified imports and execute
if (typeof link == 'object') {
imports = link.imports || [];
execute = link.execute;
}
// 3. undefined -> default
else
imports = self._parseImports(source);
// stops an unnecessary load cascade
if (errback.called)
return;
execute = execute || function() {
var deps = {};
for (var i = 0; i < arguments.length; i++)
deps[imports[i]] = arguments[i];
try {
var exports = self.eval(self._parseExports(source, null));
}
catch(e) {
return errback(e);
}
callback(self._modules[name] = new Module(exports));
}
if (!imports.length)
return execute();
var deps = [];
var depCnt = 0;
for (var i = 0; i < imports.length; i++) (function(i) {
self.load(imports[i], function(module) {
depCnt++;
deps[i] = module;
if (depCnt == imports.length)
return execute.apply(self, deps);
}, function() {
errback();
errback.called = true;
});
})(i);
},
reject: errback;
redirect: function() {}
});
},
_parseImports: function(source) {
// given the source, get the list of import module names to fetch
// supports:
// import "moduleName" as varName;
return [];
},
_parseExports: function(source, deps) {
// adjusts source code so that exports becomes the output object
// also inserts the deps as the import modules
// supports:
// export someThing
return source + '(exports)';
},
eval: function(source) {
var __loader = this;
// neeeeeed to use esprima about here
/*
module "moduleName" {
__code__
export = __something__;
__more code__
}
->
(function() {
__code__
var __export = __somethng__;
__more code__
return __export;
})();
__loader.set("moduleName", new Module({
});
*/
/*
import
*/
// must support imports
// no export support though
var __global = this.global;
eval('(function(window) {' + source + '}).call(__global, __global);');
},
get: function(name) {
return this._modules[this.resolve(name, {}).name];
},
set: function(name, module) {
if (typeof name != 'string') {
for (var p in name)
this.set(p, name[p]);
return;
}
this._modules[this.resolve(name, {}).name] = Module(module);
}
};
window.Module = function(o) {
if (o instanceof Module)
return o;
if (this.constructor != window.Module)
return new Module(o);
for (var key in o)
if (!o.hasOwnProperty || o.hasOwnProperty(key))
Object.defineProperty(this, key, {
configurable: false,
enumerable: true,
get: function() {
return this[key];
}
});
}
var absUrlRegEx = /^\/|([^\:\/]*:)/;
var isUrl = function(name) {
return name.substr(name.length - 3, 3) == '.js' || name.match(absUrlRegEx);
}
window.System = new Loader(null, {
global: window,
baseURL: document.URL.substring(0, document.URL.lastIndexOf('\/') + 1),
strict: false,
normalize: function(name, parentName) {
if (isUrl(name))
return name;
if (name.substr(0, 2) == './') {
var parentParts = parentName.split('/');
if (!parentParts.length)
return name.substr(2);
parentParts.pop();
parentParts.push(name.substr(2));
return parentParts.join('/');
}
if (name.substr(0, 3) == '../') {
var parentParts = parentName.split('/');
if (!parentParts.length)
throw "Path below baseUrl";
parentParts.pop();
return this.normalize(name.substr(3), parentParts.join('/'));
}
return name;
},
resolve: function(name) {
for (var r in this.resolvers)
if (this.resolvers[r].indexOf(name) != -1)
return { name: r };
if (isUrl(name))
return { name: name };
return {
name: this.baseURL + (this.baseUrl.substr(this.baseUrl.length - 1, 1) != '/' ? '/' : '') + name + '.js');
};
},
fetch: function(url, options) {
var xhr = new XMLHttpRequest();
if (!xhr.withCredentials && typeof XDomainRequest != 'undefined')
xhr = new XDomainRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200)
options.fulfill(xhr.responseText);
else
options.reject(xhr.statusText);
}
}
xhr.open('GET', url, true);
xhr.send(null);
},
translate: function(source, options) {
return source;
},
link: function(source, options) {}
});
window.System.resolvers = {};
window.System.ondemand = function(resolvers) {
this.resolvers = this.resolvers || {};
for (var r in resolvers) {
this.resolvers[r] = this.resolvers[r] || [];
if (resolvers[r] instanceof Array)
this.resolvers[r] = this.resolvers[r].concat(resolvers[r]);
else
this.resolvers[r].push(resolvers[r]);
}
}
// run the system loader on <script type="text/javascript:es6"></script> and also <script src="" type="tet/javascript:es6"></script>
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment