1 import RegisterLoader
from 'es-module-loader/core/register-loader.js';
2 import { InternalModuleNamespace as ModuleNamespace
} from 'es-module-loader/core/loader-polyfill.js';
4 import { baseURI
, global
, isBrowser
} from 'es-module-loader/core/common.js';
5 import { resolveIfNotPlain
} from 'es-module-loader/core/resolve.js';
9 // <script type="module"> support
11 if (typeof document
!= 'undefined' && document
.getElementsByTagName
) {
12 var handleError = function(err
) {
13 // dispatch an error event so that we can display in errors in browsers
14 // that don't yet support unhandledrejection
15 if (window
.onunhandledrejection
=== undefined) {
17 var evt
= new Event('error');
18 } catch (_eventError
) {
19 var evt
= document
.createEvent('Event');
20 evt
.initEvent('error', true, true);
22 evt
.message
= err
.message
;
24 evt
.filename
= err
.fileName
;
25 evt
.lineno
= err
.lineNumber
;
26 evt
.colno
= err
.columnNumber
;
27 } else if (err
.sourceURL
) {
28 evt
.filename
= err
.sourceURL
;
29 evt
.lineno
= err
.line
;
30 evt
.colno
= err
.column
;
33 window
.dispatchEvent(evt
);
36 // throw so it still shows up in the console
40 var ready = function() {
41 document
.removeEventListener('DOMContentLoaded', ready
, false );
45 var scripts
= document
.getElementsByTagName('script');
46 for (var i
= 0; i
< scripts
.length
; i
++) {
47 var script
= scripts
[i
];
48 if (script
.type
== 'module' && !script
.loaded
) {
51 loader
.import(script
.src
).catch(handleError
);
53 // anonymous modules supported via a custom naming scheme and registry
55 var uri
= './<anon' + ++anonCnt
+ '>.js';
56 if (script
.id
!== ""){
57 uri
= "./" + script
.id
;
60 var anonName
= resolveIfNotPlain(uri
, baseURI
);
61 anonSources
[anonName
] = script
.innerHTML
;
62 loader
.import(anonName
).catch(handleError
);
69 if (document
.readyState
=== 'complete')
72 document
.addEventListener('DOMContentLoaded', ready
, false);
75 function BrowserESModuleLoader(baseKey
) {
77 this.baseKey
= resolveIfNotPlain(baseKey
, baseURI
) || resolveIfNotPlain('./' + baseKey
, baseURI
);
79 RegisterLoader
.call(this);
83 // ensure System.register is available
84 global
.System
= global
.System
|| {};
85 if (typeof global
.System
.register
== 'function')
86 var prevRegister
= global
.System
.register
;
87 global
.System
.register = function() {
88 loader
.register
.apply(loader
, arguments
);
90 prevRegister
.apply(this, arguments
);
93 BrowserESModuleLoader
.prototype = Object
.create(RegisterLoader
.prototype);
95 // normalize is never given a relative name like "./x", that part is already handled
96 BrowserESModuleLoader
.prototype[RegisterLoader
.resolve
] = function(key
, parent
) {
97 var resolved
= RegisterLoader
.prototype[RegisterLoader
.resolve
].call(this, key
, parent
|| this.baseKey
) || key
;
99 throw new RangeError('ES module loader does not resolve plain module names, resolving "' + key
+ '" to ' + parent
);
104 function xhrFetch(url
, resolve
, reject
) {
105 var xhr
= new XMLHttpRequest();
106 var load = function(source
) {
107 resolve(xhr
.responseText
);
109 var error = function() {
110 reject(new Error('XHR error' + (xhr
.status
? ' (' + xhr
.status
+ (xhr
.statusText
? ' ' + xhr
.statusText
: '') + ')' : '') + ' loading ' + url
));
113 xhr
.onreadystatechange = function () {
114 if (xhr
.readyState
=== 4) {
115 // in Chrome on file:/// URLs, status is 0
116 if (xhr
.status
== 0) {
117 if (xhr
.responseText
) {
121 // when responseText is empty, wait for load or error event
122 // to inform if it is a 404 or empty file
123 xhr
.addEventListener('error', error
);
124 xhr
.addEventListener('load', load
);
127 else if (xhr
.status
=== 200) {
135 xhr
.open("GET", url
, true);
139 var WorkerPool = function (script
, size
) {
140 var current
= document
.currentScript
;
141 // IE doesn't support currentScript
143 // Find an entry with out basename
144 var scripts
= document
.getElementsByTagName('script');
145 for (var i
= 0; i
< scripts
.length
; i
++) {
146 if (scripts
[i
].src
.indexOf("browser-es-module-loader.js") !== -1) {
147 current
= scripts
[i
];
152 throw Error("Could not find own <script> element");
154 script
= current
.src
.substr(0, current
.src
.lastIndexOf("/")) + "/" + script
;
155 this._workers
= new Array(size
);
159 this.onmessage
= undefined;
160 this._stopTimeout
= undefined;
161 for (var i
= 0; i
< size
; i
++) {
162 var wrkr
= new Worker(script
);
165 wrkr
.onmessage
= this._onmessage
.bind(this, wrkr
);
166 wrkr
.onerror
= this._onerror
.bind(this);
167 this._workers
[i
] = wrkr
;
172 WorkerPool
.prototype = {
173 postMessage: function (msg
) {
174 if (this._stopTimeout
!== undefined) {
175 clearTimeout(this._stopTimeout
);
176 this._stopTimeout
= undefined;
178 var wrkr
= this._workers
[this._ind
% this._size
];
181 wrkr
.postMessage(msg
);
185 _onmessage: function (wrkr
, evt
) {
188 this.onmessage(evt
, wrkr
);
192 _onerror: function(err
) {
194 var evt
= new Event('error');
195 } catch (_eventError
) {
196 var evt
= document
.createEvent('Event');
197 evt
.initEvent('error', true, true);
199 evt
.message
= err
.message
;
200 evt
.filename
= err
.filename
;
201 evt
.lineno
= err
.lineno
;
202 evt
.colno
= err
.colno
;
203 evt
.error
= err
.error
;
204 window
.dispatchEvent(evt
);
207 _checkJobs: function () {
208 if (this._jobs
=== 0 && this._stopTimeout
=== undefined) {
209 // wait for 2s of inactivity before stopping (that should be enough for local loading)
210 this._stopTimeout
= setTimeout(this._stop
.bind(this), 2000);
215 this._workers
.forEach(function(wrkr
) {
221 var promiseMap
= new Map();
222 var babelWorker
= new WorkerPool('babel-worker.js', 3);
223 babelWorker
.onmessage = function (evt
) {
224 var promFuncs
= promiseMap
.get(evt
.data
.key
);
225 promFuncs
.resolve(evt
.data
);
226 promiseMap
.delete(evt
.data
.key
);
229 // instantiate just needs to run System.register
230 // so we fetch the source, convert into the Babel System module format, then evaluate it
231 BrowserESModuleLoader
.prototype[RegisterLoader
.instantiate
] = function(key
, processAnonRegister
) {
234 // load as ES with Babel converting into System.register
235 return new Promise(function(resolve
, reject
) {
237 if (anonSources
[key
]) {
238 resolve(anonSources
[key
])
239 anonSources
[key
] = undefined;
241 // otherwise we fetch
243 xhrFetch(key
, resolve
, reject
);
246 .then(function(source
) {
247 // check our cache first
248 var cacheEntry
= localStorage
.getItem(key
);
250 cacheEntry
= JSON
.parse(cacheEntry
);
251 // TODO: store a hash instead
252 if (cacheEntry
.source
=== source
) {
253 return Promise
.resolve({key
: key
, code
: cacheEntry
.code
, source
: cacheEntry
.source
});
256 return new Promise(function (resolve
, reject
) {
257 promiseMap
.set(key
, {resolve
: resolve
, reject
: reject
});
258 babelWorker
.postMessage({key
: key
, source
: source
});
260 }).then(function (data
) {
261 // evaluate without require, exports and module variables
262 // we leave module in for now to allow module.require access
264 var cacheEntry
= JSON
.stringify({source
: data
.source
, code
: data
.code
});
265 localStorage
.setItem(key
, cacheEntry
);
267 if (window
.console
) {
268 window
.console
.warn('Unable to cache transpiled version of ' + key
+ ': ' + e
);
271 (0, eval
)(data
.code
+ '\n//# sourceURL=' + data
.key
+ '!transpiled');
272 processAnonRegister();
276 // create a default loader instance in the browser
278 loader
= new BrowserESModuleLoader();
280 export default BrowserESModuleLoader
;