]> git.proxmox.com Git - mirror_novnc.git/blame - vendor/browser-es-module-loader/src/browser-es-module-loader.js
Optimize ES6 Module Loader Polyfill
[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) {
12 function ready() {
13 document.removeEventListener('DOMContentLoaded', ready, false );
14
15 var anonCnt = 0;
16
17 var scripts = document.getElementsByTagName('script');
18 for (var i = 0; i < scripts.length; i++) {
19 var script = scripts[i];
20 if (script.type == 'module' && !script.loaded) {
21 script.loaded = true;
22 if (script.src) {
23 loader.import(script.src);
24 }
25 // anonymous modules supported via a custom naming scheme and registry
26 else {
27 var uri = './<anon' + ++anonCnt + '>';
28 if (script.id !== ""){
29 uri = "./" + script.id;
30 }
31
32 var anonName = resolveIfNotPlain(uri, baseURI);
33 anonSources[anonName] = script.innerHTML;
34 loader.import(anonName);
35 }
36 }
37 }
38 }
39
40 // simple DOM ready
41 if (document.readyState === 'complete')
42 setTimeout(ready);
43 else
44 document.addEventListener('DOMContentLoaded', ready, false);
45}
46
47function BrowserESModuleLoader(baseKey) {
48 if (baseKey)
49 this.baseKey = resolveIfNotPlain(baseKey, baseURI) || resolveIfNotPlain('./' + baseKey, baseURI);
50
51 RegisterLoader.call(this);
52
53 var loader = this;
54
55 // ensure System.register is available
56 global.System = global.System || {};
57 if (typeof global.System.register == 'function')
58 var prevRegister = global.System.register;
59 global.System.register = function() {
60 loader.register.apply(loader, arguments);
61 if (prevRegister)
62 prevRegister.apply(this, arguments);
63 };
64}
65BrowserESModuleLoader.prototype = Object.create(RegisterLoader.prototype);
66
67// normalize is never given a relative name like "./x", that part is already handled
68BrowserESModuleLoader.prototype[RegisterLoader.resolve] = function(key, parent) {
69 var resolved = RegisterLoader.prototype[RegisterLoader.resolve].call(this, key, parent || this.baseKey) || key;
70 if (!resolved)
71 throw new RangeError('ES module loader does not resolve plain module names, resolving "' + key + '" to ' + parent);
72
73 return resolved;
74};
75
76function xhrFetch(url, resolve, reject) {
77 var xhr = new XMLHttpRequest();
78 function load(source) {
79 resolve(xhr.responseText);
80 }
81 function error() {
82 reject(new Error('XHR error' + (xhr.status ? ' (' + xhr.status + (xhr.statusText ? ' ' + xhr.statusText : '') + ')' : '') + ' loading ' + url));
83 }
84
85 xhr.onreadystatechange = function () {
86 if (xhr.readyState === 4) {
87 // in Chrome on file:/// URLs, status is 0
88 if (xhr.status == 0) {
89 if (xhr.responseText) {
90 load();
91 }
92 else {
93 // when responseText is empty, wait for load or error event
94 // to inform if it is a 404 or empty file
95 xhr.addEventListener('error', error);
96 xhr.addEventListener('load', load);
97 }
98 }
99 else if (xhr.status === 200) {
100 load();
101 }
102 else {
103 error();
104 }
105 }
106 };
107 xhr.open("GET", url, true);
108 xhr.send(null);
109}
110
111var WorkerPool = function (script, size) {
112 this._workers = new Array(size);
113 this._ind = 0;
114 this._size = size;
115 this._jobs = 0;
116 this.onmessage = undefined;
117 this._stopTimeout = undefined;
118 for (let i = 0; i < size; i++) {
119 let wrkr = new Worker(script);
120 wrkr._count = 0;
121 wrkr._ind = i;
122 wrkr.onmessage = this._onmessage.bind(this, wrkr);
123 this._workers[i] = wrkr;
124 }
125
126 this._checkJobs();
127};
128WorkerPool.prototype = {
129 postMessage: function (msg) {
130 if (this._stopTimeout !== undefined) {
131 clearTimeout(this._stopTimeout);
132 this._stopTimeout = undefined;
133 }
134 let wrkr = this._workers[this._ind % this._size];
135 wrkr._count++;
136 this._jobs++;
137 wrkr.postMessage(msg);
138 this._ind++;
139 },
140
141 _onmessage: function (wrkr, evt) {
142 wrkr._count--;
143 this._jobs--;
144 this.onmessage(evt, wrkr);
145 this._checkJobs();
146 },
147
148 _checkJobs: function () {
149 if (this._jobs === 0 && this._stopTimeout === undefined) {
150 // wait for 2s of inactivity before stopping (that should be enough for local loading)
151 this._stopTimeout = setTimeout(this._stop.bind(this), 2000);
152 }
153 },
154
155 _stop: function () {
156 for (let wrkr of this._workers) {
157 wrkr.terminate();
158 }
159 }
160};
161
162var promiseMap = new Map();
163var babelWorker = new WorkerPool('vendor/browser-es-module-loader/dist/babel-worker.js', 3);
164babelWorker.onmessage = function (evt) {
165 var promFuncs = promiseMap.get(evt.data.key);
166 promFuncs.resolve(evt.data);
167 promiseMap.delete(evt.data.key);
168};
169
170// instantiate just needs to run System.register
171// so we fetch the source, convert into the Babel System module format, then evaluate it
172BrowserESModuleLoader.prototype[RegisterLoader.instantiate] = function(key, processAnonRegister) {
173 var loader = this;
174
175 // load as ES with Babel converting into System.register
176 return new Promise(function(resolve, reject) {
177 // anonymous module
178 if (anonSources[key]) {
179 resolve(anonSources[key])
180 anonSources[key] = undefined;
181 }
182 // otherwise we fetch
183 else {
184 xhrFetch(key, resolve, reject);
185 }
186 })
187 .then(function(source) {
188 // check our cache first
189 const cacheEntryTrans = localStorage.getItem(key+'!transpiled');
190 if (cacheEntryTrans) {
191 const cacheEntryRaw = localStorage.getItem(key+'!raw');
192 // TODO: store a hash instead
193 if (cacheEntryRaw === source) {
194 return Promise.resolve({key: key, code: cacheEntryTrans, source: source});
195 }
196 }
197 return new Promise(function (resolve, reject) {
198 promiseMap.set(key, {resolve: resolve, reject: reject});
199 babelWorker.postMessage({key: key, source: source});
200 });
201 }).then(function (data) {
202 // evaluate without require, exports and module variables
203 // we leave module in for now to allow module.require access
204 localStorage.setItem(key+'!raw', data.source);
205 localStorage.setItem(data.key+'!transpiled', data.code);
206 (0, eval)(data.code + '\n//# sourceURL=' + data.key + '!transpiled');
207 processAnonRegister();
208 });
209};
210
211// create a default loader instance in the browser
212if (isBrowser)
213 loader = new BrowserESModuleLoader();
214
215export default BrowserESModuleLoader;