]> git.proxmox.com Git - mirror_novnc.git/blame - vendor/browser-es-module-loader/src/browser-es-module-loader.js
Better currentScript fallback
[mirror_novnc.git] / vendor / browser-es-module-loader / src / browser-es-module-loader.js
CommitLineData
399fa2ee
SR
1import RegisterLoader from 'es-module-loader/core/register-loader.js';
2import { InternalModuleNamespace as ModuleNamespace } from 'es-module-loader/core/loader-polyfill.js';
3
4import { baseURI, global, isBrowser } from 'es-module-loader/core/common.js';
5import { resolveIfNotPlain } from 'es-module-loader/core/resolve.js';
6
7var loader;
8
9// <script type="module"> support
10var anonSources = {};
11if (typeof document != 'undefined' && document.getElementsByTagName) {
493ad1a2 12 var handleError = function(err) {
36653517
PO
13 // dispatch an error event so that we can display in errors in browsers
14 // that don't yet support unhandledrejection
999b5da7
PO
15 if (window.onunhandledrejection === undefined) {
16 try {
17 var evt = new Event('error');
18 } catch (_eventError) {
19 var evt = document.createEvent('Event');
20 evt.initEvent('error', true, true);
21 }
22 evt.message = err.message;
5d00fd9b
PO
23 if (err.fileName) {
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;
31 }
999b5da7
PO
32 evt.error = err;
33 window.dispatchEvent(evt);
36653517 34 }
36653517
PO
35
36 // throw so it still shows up in the console
37 throw err;
38 }
39
493ad1a2 40 var ready = function() {
399fa2ee
SR
41 document.removeEventListener('DOMContentLoaded', ready, false );
42
43 var anonCnt = 0;
44
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) {
49 script.loaded = true;
50 if (script.src) {
36653517 51 loader.import(script.src).catch(handleError);
399fa2ee
SR
52 }
53 // anonymous modules supported via a custom naming scheme and registry
54 else {
50c31776 55 var uri = './<anon' + ++anonCnt + '>.js';
399fa2ee
SR
56 if (script.id !== ""){
57 uri = "./" + script.id;
58 }
59
60 var anonName = resolveIfNotPlain(uri, baseURI);
61 anonSources[anonName] = script.innerHTML;
36653517 62 loader.import(anonName).catch(handleError);
399fa2ee
SR
63 }
64 }
65 }
66 }
67
68 // simple DOM ready
69 if (document.readyState === 'complete')
70 setTimeout(ready);
71 else
72 document.addEventListener('DOMContentLoaded', ready, false);
73}
74
75function BrowserESModuleLoader(baseKey) {
76 if (baseKey)
77 this.baseKey = resolveIfNotPlain(baseKey, baseURI) || resolveIfNotPlain('./' + baseKey, baseURI);
78
79 RegisterLoader.call(this);
80
81 var loader = this;
82
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);
89 if (prevRegister)
90 prevRegister.apply(this, arguments);
91 };
92}
93BrowserESModuleLoader.prototype = Object.create(RegisterLoader.prototype);
94
95// normalize is never given a relative name like "./x", that part is already handled
96BrowserESModuleLoader.prototype[RegisterLoader.resolve] = function(key, parent) {
97 var resolved = RegisterLoader.prototype[RegisterLoader.resolve].call(this, key, parent || this.baseKey) || key;
98 if (!resolved)
99 throw new RangeError('ES module loader does not resolve plain module names, resolving "' + key + '" to ' + parent);
100
101 return resolved;
102};
103
104function xhrFetch(url, resolve, reject) {
105 var xhr = new XMLHttpRequest();
493ad1a2 106 var load = function(source) {
399fa2ee
SR
107 resolve(xhr.responseText);
108 }
493ad1a2 109 var error = function() {
399fa2ee
SR
110 reject(new Error('XHR error' + (xhr.status ? ' (' + xhr.status + (xhr.statusText ? ' ' + xhr.statusText : '') + ')' : '') + ' loading ' + url));
111 }
112
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) {
118 load();
119 }
120 else {
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);
125 }
126 }
127 else if (xhr.status === 200) {
128 load();
129 }
130 else {
131 error();
132 }
133 }
134 };
135 xhr.open("GET", url, true);
136 xhr.send(null);
137}
138
139var WorkerPool = function (script, size) {
edb78799
PO
140 var current = document.currentScript;
141 // IE doesn't support currentScript
142 if (!current) {
d1316334 143 // Find an entry with out basename
edb78799 144 var scripts = document.getElementsByTagName('script');
d1316334
PO
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];
148 break;
149 }
150 }
151 if (!current)
152 throw Error("Could not find own <script> element");
edb78799
PO
153 }
154 script = current.src.substr(0, current.src.lastIndexOf("/")) + "/" + script;
399fa2ee
SR
155 this._workers = new Array(size);
156 this._ind = 0;
157 this._size = size;
158 this._jobs = 0;
159 this.onmessage = undefined;
160 this._stopTimeout = undefined;
858ea4a7
PO
161 for (var i = 0; i < size; i++) {
162 var wrkr = new Worker(script);
399fa2ee
SR
163 wrkr._count = 0;
164 wrkr._ind = i;
165 wrkr.onmessage = this._onmessage.bind(this, wrkr);
36efb978 166 wrkr.onerror = this._onerror.bind(this);
399fa2ee
SR
167 this._workers[i] = wrkr;
168 }
169
170 this._checkJobs();
171};
172WorkerPool.prototype = {
173 postMessage: function (msg) {
174 if (this._stopTimeout !== undefined) {
175 clearTimeout(this._stopTimeout);
176 this._stopTimeout = undefined;
177 }
858ea4a7 178 var wrkr = this._workers[this._ind % this._size];
399fa2ee
SR
179 wrkr._count++;
180 this._jobs++;
181 wrkr.postMessage(msg);
182 this._ind++;
183 },
184
185 _onmessage: function (wrkr, evt) {
186 wrkr._count--;
187 this._jobs--;
188 this.onmessage(evt, wrkr);
189 this._checkJobs();
190 },
191
36efb978
PO
192 _onerror: function(err) {
193 try {
194 var evt = new Event('error');
195 } catch (_eventError) {
196 var evt = document.createEvent('Event');
197 evt.initEvent('error', true, true);
198 }
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);
205 },
206
399fa2ee
SR
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);
211 }
212 },
213
214 _stop: function () {
152c3995 215 this._workers.forEach(function(wrkr) {
399fa2ee 216 wrkr.terminate();
152c3995 217 });
399fa2ee
SR
218 }
219};
220
221var promiseMap = new Map();
aadcf47d 222var babelWorker = new WorkerPool('babel-worker.js', 3);
399fa2ee
SR
223babelWorker.onmessage = function (evt) {
224 var promFuncs = promiseMap.get(evt.data.key);
225 promFuncs.resolve(evt.data);
226 promiseMap.delete(evt.data.key);
227};
228
229// instantiate just needs to run System.register
230// so we fetch the source, convert into the Babel System module format, then evaluate it
231BrowserESModuleLoader.prototype[RegisterLoader.instantiate] = function(key, processAnonRegister) {
232 var loader = this;
233
234 // load as ES with Babel converting into System.register
235 return new Promise(function(resolve, reject) {
236 // anonymous module
237 if (anonSources[key]) {
238 resolve(anonSources[key])
239 anonSources[key] = undefined;
240 }
241 // otherwise we fetch
242 else {
243 xhrFetch(key, resolve, reject);
244 }
245 })
246 .then(function(source) {
247 // check our cache first
1433360a
PO
248 var cacheEntry = localStorage.getItem(key);
249 if (cacheEntry) {
250 cacheEntry = JSON.parse(cacheEntry);
399fa2ee 251 // TODO: store a hash instead
1433360a
PO
252 if (cacheEntry.source === source) {
253 return Promise.resolve({key: key, code: cacheEntry.code, source: cacheEntry.source});
399fa2ee
SR
254 }
255 }
256 return new Promise(function (resolve, reject) {
257 promiseMap.set(key, {resolve: resolve, reject: reject});
258 babelWorker.postMessage({key: key, source: source});
259 });
260 }).then(function (data) {
261 // evaluate without require, exports and module variables
262 // we leave module in for now to allow module.require access
55d5353f 263 try {
39193b28
PO
264 var cacheEntry = JSON.stringify({source: data.source, code: data.code});
265 localStorage.setItem(key, cacheEntry);
55d5353f
PO
266 } catch (e) {
267 if (window.console) {
268 window.console.warn('Unable to cache transpiled version of ' + key + ': ' + e);
777df7c2 269 }
d6c17390 270 }
399fa2ee
SR
271 (0, eval)(data.code + '\n//# sourceURL=' + data.key + '!transpiled');
272 processAnonRegister();
273 });
274};
275
276// create a default loader instance in the browser
277if (isBrowser)
278 loader = new BrowserESModuleLoader();
279
280export default BrowserESModuleLoader;