1 | 'use strict';
|
2 |
|
3 | var STYLE_REGEX = /(url\s*\(\s*[\\"']*)([^)'"]+)([\\"']*\s*\))/gi;
|
4 | var IMPORT_REGEX = /(@import\s*[\\"']*)([^)'";]+)([\\"']*\s*;?)/gi;
|
5 | var srcsetSplit = /\s*(\S*\s+[\d.]+[wx]),|(?:\s*,(?:\s+|(?=https?:)))/;
|
6 | var MaxRunningFetches = 15;
|
7 | var DataURLPrefix = 'data:';
|
8 | var seen = {};
|
9 |
|
10 | var queue = [];
|
11 | var runningFetches = 0;
|
12 |
|
13 | var currentResolver = null;
|
14 |
|
15 | var config = {
|
16 | havePromise: typeof self.Promise !== 'undefined',
|
17 | haveFetch: typeof self.fetch !== 'undefined',
|
18 | proxyMode: false,
|
19 | mod: null,
|
20 | prefix: null,
|
21 | prefixMod: null,
|
22 | relative: null,
|
23 | rwRe: null,
|
24 | defaultFetchOptions: {
|
25 | cache: 'force-cache',
|
26 | mode: 'cors'
|
27 | }
|
28 | };
|
29 |
|
30 | if (!config.havePromise) {
|
31 |
|
32 | self.Promise = function(executor) {
|
33 | executor(noop, noop);
|
34 | };
|
35 | self.Promise.prototype.then = function(cb) {
|
36 | if (cb) cb();
|
37 | return this;
|
38 | };
|
39 | self.Promise.prototype.catch = function() {
|
40 | return this;
|
41 | };
|
42 | self.Promise.all = function(values) {
|
43 | return new Promise(noop);
|
44 | };
|
45 | }
|
46 |
|
47 | if (!config.haveFetch) {
|
48 |
|
49 | self.fetch = function(url) {
|
50 | return new Promise(function(resolve) {
|
51 | var xhr = new XMLHttpRequest();
|
52 | xhr.open('GET', url, true);
|
53 | xhr.onreadystatechange = function() {
|
54 | if (xhr.readyState === 4) {
|
55 | if (!config.havePromise) {
|
56 | fetchDone();
|
57 | }
|
58 | resolve();
|
59 | }
|
60 | };
|
61 | xhr.send();
|
62 | });
|
63 | };
|
64 | }
|
65 |
|
66 | if (location.search.indexOf('init') !== -1) {
|
67 | (function() {
|
68 | var init;
|
69 | if (typeof self.URL === 'function') {
|
70 | var loc = new self.URL(location.href);
|
71 | init = JSON.parse(loc.searchParams.get('init'));
|
72 | } else {
|
73 | var search = decodeURIComponent(location.search.split('?')[1]).split('&');
|
74 | init = JSON.parse(search[0].substr(search[0].indexOf('=') + 1));
|
75 | init.prefix = decodeURIComponent(init.prefix);
|
76 | init.baseURI = decodeURIComponent(init.prefix);
|
77 | }
|
78 | config.prefix = init.prefix;
|
79 | config.mod = init.mod;
|
80 | config.prefixMod = init.prefix + init.mod;
|
81 | config.rwRe = new RegExp(init.rwRe);
|
82 | config.relative = init.prefix.split(location.origin)[1];
|
83 | config.schemeless = '/' + config.relative;
|
84 | })();
|
85 | } else {
|
86 | config.proxyMode = true;
|
87 | config.defaultFetchOptions.mode = 'no-cors';
|
88 | }
|
89 |
|
90 | self.onmessage = function(event) {
|
91 | var data = event.data;
|
92 | switch (data.type) {
|
93 | case 'values':
|
94 | autoFetch(data);
|
95 | break;
|
96 | case 'fetch-all':
|
97 | justFetch(data);
|
98 | break;
|
99 | }
|
100 | };
|
101 |
|
102 | function noop() {}
|
103 |
|
104 | function fetchDone() {
|
105 | runningFetches -= 1;
|
106 | fetchFromQ();
|
107 | }
|
108 |
|
109 | function fetchErrored(err) {
|
110 | console.warn('Fetch Failed: ' + err);
|
111 | fetchDone();
|
112 | }
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
124 | function fetchURL(toBeFetched) {
|
125 | runningFetches += 1;
|
126 |
|
127 | var url;
|
128 | var options = config.defaultFetchOptions;
|
129 |
|
130 | if (typeof toBeFetched === 'object') {
|
131 | url = toBeFetched.url;
|
132 | options = toBeFetched.options;
|
133 | } else {
|
134 | url = toBeFetched;
|
135 | }
|
136 |
|
137 | fetch(url, options)
|
138 | .then(fetchDone)
|
139 | .catch(fetchErrored);
|
140 | }
|
141 |
|
142 | function queueOrFetch(toBeFetched) {
|
143 | var url = typeof toBeFetched === 'object' ? toBeFetched.url : toBeFetched;
|
144 | if (!url || url.indexOf(DataURLPrefix) === 0 || seen[url] != null) {
|
145 | return;
|
146 | }
|
147 | seen[url] = true;
|
148 | if (runningFetches >= MaxRunningFetches) {
|
149 | queue.push(toBeFetched);
|
150 | return;
|
151 | }
|
152 | fetchURL(toBeFetched);
|
153 | }
|
154 |
|
155 | function fetchFromQ() {
|
156 | while (queue.length && runningFetches < MaxRunningFetches) {
|
157 | fetchURL(queue.shift());
|
158 | }
|
159 | }
|
160 |
|
161 | function maybeResolveURL(url, base) {
|
162 |
|
163 |
|
164 | try {
|
165 | var _url = new URL(url, base);
|
166 | return _url.href;
|
167 | } catch (e) {
|
168 | return null;
|
169 | }
|
170 | }
|
171 |
|
172 | function safeResolve(url, resolver) {
|
173 |
|
174 |
|
175 | var resolvedURL = url;
|
176 | if (resolver) {
|
177 | try {
|
178 | var _url = new URL(url, resolver);
|
179 | return _url.href;
|
180 | } catch (e) {
|
181 | resolvedURL = url;
|
182 | }
|
183 | }
|
184 | return resolvedURL;
|
185 | }
|
186 |
|
187 | function maybeFixUpRelSchemelessPrefix(url) {
|
188 |
|
189 |
|
190 | if (url.indexOf(config.relative) === 0) {
|
191 | return url.replace(config.relative, config.prefix);
|
192 | }
|
193 | if (url.indexOf(config.schemeless) === 0) {
|
194 | return url.replace(config.schemeless, config.prefix);
|
195 | }
|
196 | return null;
|
197 | }
|
198 |
|
199 | function maybeFixUpURL(url, resolveOpts) {
|
200 |
|
201 | if (config.rwRe.test(url)) {
|
202 | return url;
|
203 | }
|
204 | var mod = resolveOpts.mod || 'mp_';
|
205 |
|
206 | var maybeFixed = maybeFixUpRelSchemelessPrefix(url);
|
207 | if (maybeFixed != null) {
|
208 | return maybeFixed;
|
209 | }
|
210 |
|
211 | if (resolveOpts.tagSrc != null) {
|
212 | maybeFixed = maybeResolveURL(url, resolveOpts.tagSrc);
|
213 | if (maybeFixed != null) {
|
214 | return config.prefix + mod + '/' + maybeFixed;
|
215 | }
|
216 | }
|
217 |
|
218 | if (resolveOpts.docBaseURI) {
|
219 | maybeFixed = maybeResolveURL(url, resolveOpts.docBaseURI);
|
220 | if (maybeFixed != null) {
|
221 | return config.prefix + mod + '/' + maybeFixed;
|
222 | }
|
223 | }
|
224 |
|
225 | return config.prefixMod + '/' + url;
|
226 | }
|
227 |
|
228 | function urlExtractor(match, n1, n2, n3, offset, string) {
|
229 |
|
230 | queueOrFetch(n2);
|
231 | return n1 + n2 + n3;
|
232 | }
|
233 |
|
234 | function urlExtractorProxyMode(match, n1, n2, n3, offset, string) {
|
235 |
|
236 |
|
237 |
|
238 |
|
239 | queueOrFetch(safeResolve(n2, currentResolver));
|
240 | return n1 + n2 + n3;
|
241 | }
|
242 |
|
243 | function handleMedia(mediaRules) {
|
244 |
|
245 | if (mediaRules == null || mediaRules.length === 0) return;
|
246 | for (var i = 0; i < mediaRules.length; i++) {
|
247 | mediaRules[i]
|
248 | .replace(STYLE_REGEX, urlExtractor)
|
249 | .replace(IMPORT_REGEX, urlExtractor);
|
250 | }
|
251 | }
|
252 |
|
253 | function handleMediaProxyMode(mediaRules) {
|
254 |
|
255 | if (mediaRules == null || mediaRules.length === 0) return;
|
256 | for (var i = 0; i < mediaRules.length; i++) {
|
257 |
|
258 |
|
259 |
|
260 | currentResolver = mediaRules[i].resolve;
|
261 | mediaRules[i].cssText
|
262 | .replace(STYLE_REGEX, urlExtractorProxyMode)
|
263 | .replace(IMPORT_REGEX, urlExtractorProxyMode);
|
264 | }
|
265 | }
|
266 |
|
267 | function handleSrc(srcValues, context) {
|
268 | var resolveOpts = { docBaseURI: context.docBaseURI, mod: null };
|
269 | if (srcValues.value) {
|
270 | resolveOpts.mod = srcValues.mod;
|
271 | return queueOrFetch(maybeFixUpURL(srcValues.value.trim(), resolveOpts));
|
272 | }
|
273 | var len = srcValues.values.length;
|
274 | for (var i = 0; i < len; i++) {
|
275 | var value = srcValues.values[i];
|
276 | resolveOpts.mod = value.mod;
|
277 | queueOrFetch(maybeFixUpURL(value.src, resolveOpts));
|
278 | }
|
279 | }
|
280 |
|
281 | function handleSrcProxyMode(srcValues) {
|
282 |
|
283 |
|
284 | if (srcValues == null || srcValues.length === 0) return;
|
285 | var srcVal;
|
286 | for (var i = 0; i < srcValues.length; i++) {
|
287 | srcVal = srcValues[i];
|
288 | queueOrFetch(safeResolve(srcVal.src, srcVal.resolve));
|
289 | }
|
290 | }
|
291 |
|
292 | function extractSrcSetNotPreSplit(ssV, resolveOpts) {
|
293 | if (!ssV) return;
|
294 |
|
295 | var srcsetValues = ssV.split(srcsetSplit);
|
296 | for (var i = 0; i < srcsetValues.length; i++) {
|
297 |
|
298 | if (srcsetValues[i]) {
|
299 | var value = srcsetValues[i].trim().split(' ')[0];
|
300 | var maybeResolvedURL = maybeFixUpURL(value.trim(), resolveOpts);
|
301 | queueOrFetch(maybeResolvedURL);
|
302 | }
|
303 | }
|
304 | }
|
305 |
|
306 | function extractSrcset(srcsets) {
|
307 |
|
308 | for (var i = 0; i < srcsets.length; i++) {
|
309 |
|
310 | var url = srcsets[i].split(' ')[0];
|
311 | queueOrFetch(url);
|
312 | }
|
313 | }
|
314 |
|
315 | function handleSrcset(srcset, context) {
|
316 | if (srcset == null) return;
|
317 | var resolveOpts = {
|
318 | docBaseURI: context.docBaseURI,
|
319 | mod: null,
|
320 | tagSrc: null
|
321 | };
|
322 | if (srcset.value) {
|
323 |
|
324 |
|
325 | resolveOpts.mod = srcset.mod;
|
326 | if (!srcset.presplit) {
|
327 |
|
328 | return extractSrcSetNotPreSplit(srcset.value, resolveOpts);
|
329 | }
|
330 |
|
331 | return extractSrcset(srcset.value);
|
332 | }
|
333 |
|
334 | var len = srcset.values.length;
|
335 | for (var i = 0; i < len; i++) {
|
336 | var ssv = srcset.values[i];
|
337 | resolveOpts.mod = ssv.mod;
|
338 | resolveOpts.tagSrc = ssv.tagSrc;
|
339 | extractSrcSetNotPreSplit(ssv.srcset, resolveOpts);
|
340 | }
|
341 | }
|
342 |
|
343 | function handleSrcsetProxyMode(srcsets) {
|
344 |
|
345 |
|
346 | if (srcsets == null) return;
|
347 | var length = srcsets.length;
|
348 | var extractedSrcSet, srcsetValue, ssSplit, j;
|
349 | for (var i = 0; i < length; i++) {
|
350 | extractedSrcSet = srcsets[i];
|
351 | ssSplit = extractedSrcSet.srcset.split(srcsetSplit);
|
352 | for (j = 0; j < ssSplit.length; j++) {
|
353 | if (ssSplit[j]) {
|
354 | srcsetValue = ssSplit[j].trim();
|
355 | if (srcsetValue) {
|
356 | queueOrFetch(
|
357 | safeResolve(srcsetValue.split(' ')[0], extractedSrcSet.resolve)
|
358 | );
|
359 | }
|
360 | }
|
361 | }
|
362 | }
|
363 | }
|
364 |
|
365 | function autoFetch(data) {
|
366 |
|
367 |
|
368 | if (data.media) {
|
369 | if (config.proxyMode) {
|
370 | handleMediaProxyMode(data.media);
|
371 | } else {
|
372 | handleMedia(data.media);
|
373 | }
|
374 | }
|
375 |
|
376 | if (data.src) {
|
377 | if (config.proxyMode) {
|
378 | handleSrcProxyMode(data.src);
|
379 | } else {
|
380 | handleSrc(data.src, data.context || { docBaseURI: null });
|
381 | }
|
382 | }
|
383 |
|
384 | if (data.srcset) {
|
385 | if (config.proxyMode) {
|
386 | handleSrcsetProxyMode(data.srcset);
|
387 | } else {
|
388 | handleSrcset(data.srcset, data.context || { docBaseURI: null });
|
389 | }
|
390 | }
|
391 | }
|
392 |
|
393 | function justFetch(data) {
|
394 |
|
395 | if (data == null || data.values == null) return;
|
396 | for (var i = 0; i < data.values.length; ++i) {
|
397 | queueOrFetch(data.values[i]);
|
398 | }
|
399 | }
|