-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpermalink.js
More file actions
176 lines (147 loc) · 6.62 KB
/
permalink.js
File metadata and controls
176 lines (147 loc) · 6.62 KB
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// permalink.js , project root
(function () {
function ready(fn) {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', fn, { once: true });
} else {
fn();
}
}
ready(() => {
const incSel = document.getElementById('inclusion');
const fileSel = document.getElementById('filetype');
const diffSel = document.getElementById('difference');
const acCb = document.getElementById('autoclose');
const bustCb = document.getElementById('bust');
const blCb = document.getElementById('applyBlocklist');
const winCb = document.getElementById('windowMode');
const runBtn = document.getElementById('run');
const copyBtn = document.getElementById('copy-link');
const copyToast = document.getElementById('copy-toast');
if (!incSel || !fileSel || !diffSel || !runBtn) return;
const params = new URLSearchParams(location.search);
//helpers
function truthy(v) { return v === '1' || v === 'true' || v === 'yes'; }
function setSelectImmediate(el, value) {
if (!value) return false;
el.value = value;
const ok = el.value === value;
if (ok) el.dispatchEvent(new Event('change', { bubbles: true }));
return ok;
}
function findMatchingOption(el, value) {
if (!value) return null;
const wanted = String(value).toLowerCase();
const opts = Array.from(el.options || []);
return opts.find(o =>
(String(o.value).toLowerCase() === wanted) ||
(String((o.textContent || '').trim()).toLowerCase() === wanted)
) || null;
}
//wait until the select has an option matching the value
function setSelectWhenOptionsLoaded(el, value, timeoutMs = 6000) {
return new Promise(resolve => {
if (!value) return resolve(false);
const trySet = () => {
const opt = findMatchingOption(el, value);
if (opt) {
el.value = opt.value;
el.dispatchEvent(new Event('change', { bubbles: true }));
resolve(true);
return true;
}
return false;
};
if (trySet()) return;
const mo = new MutationObserver(() => { trySet() && mo.disconnect(); });
mo.observe(el, { childList: true, subtree: true });
setTimeout(() => { try { mo.disconnect(); } catch {} resolve(false); }, timeoutMs);
});
}
function buildShareURL({ includeRunFlag = false } = {}) {
const p = new URLSearchParams();
const inc = incSel.value || '';
const file = fileSel.value || '';
const diff = diffSel.value || '';
if (inc) p.set('inc', inc);
if (file) p.set('file', file);
if (diff) p.set('diff', diff);
if (acCb) p.set('ac', acCb.checked ? '1' : '0');
if (bustCb) p.set('bust', bustCb.checked ? '1' : '0');
if (blCb) p.set('bl', blCb.checked ? '1' : '0');
if (winCb) p.set('win', winCb.checked ? '1' : '0');
if (includeRunFlag) p.set('run', '1');
return location.origin + location.pathname + '?' + p.toString();
}
function syncURL({ setRunFlag = false } = {}) {
const qsURL = buildShareURL({ includeRunFlag: setRunFlag });
const u = new URL(qsURL);
if ('?' + u.searchParams.toString() !== location.search) {
history.replaceState(null, '', qsURL);
}
}
function wireLiveSync() {
incSel.addEventListener('change', () => syncURL({ setRunFlag: false }));
fileSel.addEventListener('change', () => syncURL({ setRunFlag: false }));
diffSel.addEventListener('change', () => syncURL({ setRunFlag: false }));
acCb?.addEventListener('change', () => syncURL({ setRunFlag: false }));
bustCb?.addEventListener('change', () => syncURL({ setRunFlag: false }));
blCb?.addEventListener('change', () => syncURL({ setRunFlag: false }));
winCb?.addEventListener('change', () => syncURL({ setRunFlag: false }));
//when the user clicks Run, stamp run=1 so sharing the URL autostarts the same config
runBtn.addEventListener('click', () => syncURL({ setRunFlag: true }));
}
//strip query back to canonical base
function stripURLToBase() {
const base = location.origin + location.pathname;
if (location.href !== base) {
history.replaceState(null, '', base);
}
}
//strip to base before app.js' run() opens popups
runBtn.addEventListener('click', stripURLToBase, { capture: true });
//copy link button
copyBtn?.addEventListener('click', async () => {
const url = buildShareURL({ includeRunFlag: true });
try {
await navigator.clipboard.writeText(url);
if (copyToast) {
copyToast.style.display = 'inline';
setTimeout(() => { copyToast.style.display = 'none'; }, 1200);
}
} catch {
//fallback, show the URL in a prompt
window.prompt('Copy this link:', url);
}
});
//apply from URL
const incParam = params.get('inc') || params.get('inclusion') || '';
const fileParam = params.get('file') || params.get('filetype') || '';
const diffParam = params.get('diff') || params.get('difference') || '';
const winParam = params.get('win');
const autoRun = truthy(params.get('run')) || truthy(params.get('autorun'));
if (acCb) acCb.checked = truthy(params.get('ac') ?? (acCb.checked ? '1' : '0'));
if (bustCb) bustCb.checked = truthy(params.get('bust') ?? (bustCb.checked ? '1' : '0'));
if (blCb) blCb.checked = truthy(params.get('bl') ?? (blCb.checked ? '0' : '0'));
if (winCb) winCb.checked = truthy(winParam ?? (winCb.checked ? '0' : '0'));
//set inclusion and file immediately
setSelectImmediate(incSel, incParam);
setSelectImmediate(fileSel, fileParam);
//ensure difference is selected after options load (async)
setSelectWhenOptionsLoaded(diffSel, diffParam).then(() => {
// if URL requested an autorun, click Run now.
if (autoRun) {
//strip the URL before the programmatic click
stripURLToBase();
// one microtask later so any other listeners settle
setTimeout(() => runBtn.click(), 0);
}
});
//keep URL in sync with future changes
wireLiveSync();
//if the page was opened without any params, initialize URL from defaults
if (![incParam, fileParam, diffParam, params.get('ac'), params.get('bust'), params.get('bl'), params.get('win'), params.get('run')].some(Boolean)) {
syncURL({ setRunFlag: false });
}
});
})();