-
Notifications
You must be signed in to change notification settings - Fork 24
/
index.js
129 lines (113 loc) · 3.57 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
((document) => {
'use strict';
const DEST_LIST = [
'cdn.jsdelivr.net',
'cdn.jsdelivr.net',
'gcore.statically.io',
'testingcf.statically.io',
'test1.statically.io'
];
const PREFIX = '//';
const SOURCE = DEST_LIST[0];
// const starTime = Date.now();
const shouldReplace = (text) => text && text.includes(PREFIX + SOURCE);
const replace = (text) => text.replace(PREFIX + SOURCE, PREFIX + fastNode);
const $ = document.querySelectorAll.bind(document);
const replaceElementSrc = () => {
let element;
let value;
for (element of $('link[rel="stylesheet"]')) {
value = element.href;
if (shouldReplace(value)) {
element.href = replace(value);
}
}
for (element of $('script')) {
value = element.src;
if (shouldReplace(value)) {
const newNode = document.createElement('script');
newNode.src = replace(value);
element.defer = true;
element.src = '';
element.before(newNode);
element.remove();
}
}
for (element of $('img')) {
value = element.src;
if (shouldReplace(value)) {
// Used to cancel loading. Without this line it will remain pending status.
element.src = '';
element.src = replace(value);
}
}
// All elements that have a style attribute
for (element of $('*[style]')) {
value = element.getAttribute('style');
if (shouldReplace(value)) {
element.setAttribute('style', replace(value));
}
}
for (element of $('style')) {
value = element.innerHTML;
if (shouldReplace(value)) {
element.innerHTML = replace(value);
}
}
};
const tryReplace = () => {
if (failed && fastNode) {
console.warn(SOURCE + ' is not available. Use ' + fastNode);
failed = false;
replaceElementSrc();
// Replace dynamically added elements
setInterval(replaceElementSrc, 500);
}
};
const checkAvailable = (url, callback) => {
let timeoutId;
const newNode = document.createElement('link');
const handleResult = (isSuccess) => {
if (!timeoutId) {
return;
}
clearTimeout(timeoutId);
timeoutId = 0;
// Used to cancel loading. Without this line it will remain pending status.
if (!isSuccess) newNode.href = 'data:text/plain;base64,';
newNode.remove();
callback(isSuccess);
};
timeoutId = setTimeout(handleResult, 1000);
newNode.addEventListener('error', () => handleResult(false));
newNode.addEventListener('load', () => handleResult(true));
newNode.rel = 'stylesheet';
newNode.text = 'text/css';
newNode.href =
url +
'/gh/PipecraftNet/jsdelivr-auto-fallback@main/empty.css?' +
Date.now();
document.head.insertAdjacentElement('afterbegin', newNode);
};
let fastNode;
let failed;
for (const url of DEST_LIST) {
checkAvailable('https://' + url, (isAvailable) => {
// console.log(url, Date.now() - starTime, Boolean(isAvailable));
if (!isAvailable && url === SOURCE) {
failed = true;
}
if (isAvailable && !fastNode) {
fastNode = url;
}
tryReplace();
});
}
// If all domains are timeout
setTimeout(() => {
if (failed && !fastNode) {
fastNode = DEST_LIST[1];
tryReplace();
}
}, 1100);
})(document);