zsbqb-static 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/js/auth-shell-sync.js +45 -45
- package/js/burst-preview-host.js +350 -212
- package/js/catalog-search.js +311 -311
- package/js/query-app.js +331 -331
- package/note-preview/assets/atlas/manifest.json +1 -1
- package/note-preview/note-preview.min.js +1 -1
- package/package.json +18 -18
- package/catalog/bubble-4f53cda18c2b.json +0 -1
- package/catalog/crescent-4f53cda18c2b.json +0 -1
- package/catalog/dianfeng-4f53cda18c2b.json +0 -1
- package/catalog/manifest.json +0 -1
- package/catalog/pinball-4f53cda18c2b.json +0 -1
- package/catalog/pinyin-4f53cda18c2b.json +0 -1
- package/catalog/xingdong-4f53cda18c2b.json +0 -1
package/js/burst-preview-host.js
CHANGED
|
@@ -3,7 +3,35 @@
|
|
|
3
3
|
|
|
4
4
|
const HOST_SELECTOR = "[data-burst-preview-host]";
|
|
5
5
|
const instances = new Map();
|
|
6
|
-
|
|
6
|
+
const previewPayloadCache = new Map();
|
|
7
|
+
|
|
8
|
+
function jsonScriptText(value) {
|
|
9
|
+
return JSON.stringify(value)
|
|
10
|
+
.replace(/</g, "\\u003C")
|
|
11
|
+
.replace(/>/g, "\\u003E")
|
|
12
|
+
.replace(/&/g, "\\u0026");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function loadSongPreviewPayload(apiUrl) {
|
|
16
|
+
const cached = previewPayloadCache.get(apiUrl);
|
|
17
|
+
if (cached) return cached;
|
|
18
|
+
const boot = window.__NOTE_PREVIEW_PAYLOAD__;
|
|
19
|
+
const bootUrl = window.__NOTE_PREVIEW_API_URL__ || "";
|
|
20
|
+
const useBoot = Boolean(boot) && (!bootUrl || bootUrl === apiUrl);
|
|
21
|
+
if (useBoot) window.__NOTE_PREVIEW_PAYLOAD__ = null;
|
|
22
|
+
const pending = (useBoot
|
|
23
|
+
? Promise.resolve(boot)
|
|
24
|
+
: fetch(apiUrl, { credentials: "same-origin" }).then(function (res) {
|
|
25
|
+
if (!res.ok) throw new Error("fetch_failed");
|
|
26
|
+
return res.json();
|
|
27
|
+
}))
|
|
28
|
+
.catch(function (error) {
|
|
29
|
+
previewPayloadCache.delete(apiUrl);
|
|
30
|
+
throw error;
|
|
31
|
+
});
|
|
32
|
+
previewPayloadCache.set(apiUrl, pending);
|
|
33
|
+
return pending;
|
|
34
|
+
}
|
|
7
35
|
|
|
8
36
|
function setStatus(host, message, state) {
|
|
9
37
|
const status = host.querySelector("[data-burst-preview-status]");
|
|
@@ -21,70 +49,99 @@
|
|
|
21
49
|
return !item || item.classList.contains("is-open");
|
|
22
50
|
}
|
|
23
51
|
|
|
24
|
-
function
|
|
25
|
-
if (window.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
52
|
+
function previewApiUrl() {
|
|
53
|
+
if (window.__NOTE_PREVIEW_API_URL__) return window.__NOTE_PREVIEW_API_URL__;
|
|
54
|
+
const pathParts = window.location.pathname.split("/").filter(Boolean);
|
|
55
|
+
if (pathParts.length < 4 || pathParts[0] !== "query") return "";
|
|
56
|
+
const family = pathParts[1];
|
|
57
|
+
const star = pathParts[2];
|
|
58
|
+
const title = decodeURIComponent(pathParts.slice(3).join("/"));
|
|
59
|
+
const mode = new URLSearchParams(window.location.search).get("mode") || "single";
|
|
60
|
+
const params = new URLSearchParams();
|
|
61
|
+
params.set("mode", mode);
|
|
62
|
+
return (
|
|
63
|
+
"/api/query/songs/" +
|
|
64
|
+
encodeURIComponent(family) +
|
|
65
|
+
"/" +
|
|
66
|
+
encodeURIComponent(star) +
|
|
67
|
+
"/" +
|
|
68
|
+
encodeURIComponent(title) +
|
|
69
|
+
"/?" +
|
|
70
|
+
params.toString()
|
|
71
|
+
);
|
|
38
72
|
}
|
|
39
73
|
|
|
40
|
-
function
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
74
|
+
function blobFromPayload(host, result) {
|
|
75
|
+
const mode = new URLSearchParams(window.location.search).get("mode") || "single";
|
|
76
|
+
const kind = host.getAttribute("data-burst-clip") === "2" ? "2" : "1";
|
|
77
|
+
const side = mode === "double" ? "doubles" : "singles";
|
|
78
|
+
const style = host.getAttribute("data-style") || "";
|
|
79
|
+
const rank = host.getAttribute("data-rank") || "";
|
|
80
|
+
const slots = (result && result.song && result.song[side]) || [];
|
|
81
|
+
const targetSlot = slots.find(function (s) {
|
|
82
|
+
return s.style === style && s.rank === rank;
|
|
83
|
+
}) || slots[0];
|
|
84
|
+
if (!targetSlot) return "";
|
|
85
|
+
return kind === "2" ? targetSlot.preview2 || targetSlot.preview || "" : targetSlot.preview || "";
|
|
45
86
|
}
|
|
46
87
|
|
|
47
|
-
function
|
|
48
|
-
if (!
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
88
|
+
function injectBlob(host, blob) {
|
|
89
|
+
if (!blob) return false;
|
|
90
|
+
if (host.querySelector('script[type="application/json"]')) return true;
|
|
91
|
+
const player = host.querySelector(".burst-preview__player");
|
|
92
|
+
if (!player) return false;
|
|
93
|
+
const scriptEl = document.createElement("script");
|
|
94
|
+
scriptEl.type = "application/json";
|
|
95
|
+
scriptEl.textContent = jsonScriptText(blob);
|
|
96
|
+
player.appendChild(scriptEl);
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function applyPayloadToHosts(result) {
|
|
101
|
+
if (!result || !result.ok || !result.song) return false;
|
|
102
|
+
let injected = 0;
|
|
103
|
+
document.querySelectorAll(HOST_SELECTOR).forEach(function (host) {
|
|
104
|
+
const blob = blobFromPayload(host, result);
|
|
105
|
+
if (blob && injectBlob(host, blob)) injected += 1;
|
|
106
|
+
});
|
|
107
|
+
return injected > 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function prefetchAllPreviewPayloads() {
|
|
111
|
+
const apiUrl = previewApiUrl();
|
|
112
|
+
if (!apiUrl) return;
|
|
113
|
+
loadSongPreviewPayload(apiUrl)
|
|
114
|
+
.then(function (result) {
|
|
115
|
+
applyPayloadToHosts(result);
|
|
116
|
+
mountExpanded(document);
|
|
117
|
+
})
|
|
118
|
+
.catch(function () {
|
|
119
|
+
// 进页预取失败不打断爆点展示;展开时再报错。
|
|
120
|
+
});
|
|
71
121
|
}
|
|
72
122
|
|
|
73
123
|
function waitForPlayer(done) {
|
|
74
|
-
if (
|
|
124
|
+
if (window.CkHua && typeof window.CkHua.gua_shang === "function") {
|
|
75
125
|
done(true);
|
|
76
126
|
return;
|
|
77
127
|
}
|
|
128
|
+
var finished = false;
|
|
129
|
+
var finish = function (ok) {
|
|
130
|
+
if (finished) return;
|
|
131
|
+
finished = true;
|
|
132
|
+
window.removeEventListener("ckhua:ok", onReady);
|
|
133
|
+
window.clearInterval(timer);
|
|
134
|
+
done(ok);
|
|
135
|
+
};
|
|
136
|
+
var onReady = function () {
|
|
137
|
+
if (window.CkHua && typeof window.CkHua.gua_shang === "function") finish(true);
|
|
138
|
+
};
|
|
139
|
+
window.addEventListener("ckhua:ok", onReady);
|
|
78
140
|
var tries = 0;
|
|
79
141
|
var timer = window.setInterval(function () {
|
|
80
142
|
tries += 1;
|
|
81
|
-
if (
|
|
82
|
-
|
|
83
|
-
done(true);
|
|
84
|
-
} else if (tries >= 400) {
|
|
85
|
-
window.clearInterval(timer);
|
|
86
|
-
done(false);
|
|
87
|
-
}
|
|
143
|
+
if (window.CkHua && typeof window.CkHua.gua_shang === "function") finish(true);
|
|
144
|
+
else if (tries >= 80) finish(false);
|
|
88
145
|
}, 50);
|
|
89
146
|
}
|
|
90
147
|
|
|
@@ -167,28 +224,31 @@
|
|
|
167
224
|
function applyAudioPrefs(prefs) {
|
|
168
225
|
if (!prefs) return;
|
|
169
226
|
const muted = effectiveMuted(prefs);
|
|
227
|
+
const burstColor = burstColorOf(prefs);
|
|
170
228
|
instances.forEach(function (instance) {
|
|
171
|
-
if (prefs.apply !== false && instance && typeof instance.
|
|
172
|
-
instance.
|
|
229
|
+
if (prefs.apply !== false && instance && typeof instance.xieYinLiang === "function") {
|
|
230
|
+
instance.xieYinLiang(prefs.volume, muted);
|
|
173
231
|
}
|
|
174
|
-
if (instance && typeof instance.
|
|
175
|
-
instance.
|
|
232
|
+
if (instance && typeof instance.xieAnJianYin === "function") {
|
|
233
|
+
instance.xieAnJianYin(Boolean(prefs.hitsounds));
|
|
234
|
+
}
|
|
235
|
+
if (instance && typeof instance.xieBaoSe === "function") {
|
|
236
|
+
instance.xieBaoSe(burstColor);
|
|
176
237
|
}
|
|
177
238
|
});
|
|
178
|
-
// 爆气色只影响之后挂载的播放器,避免切换偏好时立刻下载另一套贴图。
|
|
179
239
|
}
|
|
180
240
|
|
|
181
241
|
function bindInstancePersist(host, instance) {
|
|
182
|
-
if (instance && typeof instance.
|
|
242
|
+
if (instance && typeof instance.ting_zhe === "function") {
|
|
183
243
|
try {
|
|
184
|
-
instance.
|
|
244
|
+
instance.ting_zhe("volume", persistAudioPrefs);
|
|
185
245
|
} catch (_error) {
|
|
186
246
|
// Older players only support on("zone").
|
|
187
247
|
}
|
|
188
248
|
}
|
|
189
249
|
const persistFromPlayer = function () {
|
|
190
|
-
if (instance && typeof instance.
|
|
191
|
-
persistAudioPrefs(instance.
|
|
250
|
+
if (instance && typeof instance.duYinLiang === "function") {
|
|
251
|
+
persistAudioPrefs(instance.duYinLiang());
|
|
192
252
|
}
|
|
193
253
|
};
|
|
194
254
|
host.addEventListener("click", function (event) {
|
|
@@ -256,6 +316,68 @@
|
|
|
256
316
|
return url + (url.indexOf("?") >= 0 ? "&" : "?") + "v=" + encodeURIComponent(ver);
|
|
257
317
|
}
|
|
258
318
|
|
|
319
|
+
const warmedBgm = new Map();
|
|
320
|
+
|
|
321
|
+
function atlasPackName() {
|
|
322
|
+
const raw = String((window.__NOTE_PREVIEW_ATLAS_PACK__ || "")).trim().toLowerCase();
|
|
323
|
+
if (raw === "pinball" || raw === "bubble" || raw === "crescent") return raw;
|
|
324
|
+
return "idol";
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function warmAudioUrl(url) {
|
|
328
|
+
url = (url || "").trim();
|
|
329
|
+
if (!url || warmedBgm.has(url) || typeof Audio === "undefined") return;
|
|
330
|
+
const audio = new Audio();
|
|
331
|
+
audio.autoplay = false;
|
|
332
|
+
audio.preload = "auto";
|
|
333
|
+
if (/^https?:/i.test(url)) audio.crossOrigin = "anonymous";
|
|
334
|
+
try {
|
|
335
|
+
audio.src = url;
|
|
336
|
+
} catch (_error) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
try {
|
|
340
|
+
audio.load();
|
|
341
|
+
} catch (_error) {
|
|
342
|
+
// ignore
|
|
343
|
+
}
|
|
344
|
+
warmedBgm.set(url, audio);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function warmExpandedBgm() {
|
|
348
|
+
document.querySelectorAll(".burst-item.is-open " + HOST_SELECTOR).forEach(function (host) {
|
|
349
|
+
const urls = audioUrlsFor(host);
|
|
350
|
+
if (urls.audioUrl) warmAudioUrl(urls.audioUrl);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function preloadPlayerAssets() {
|
|
355
|
+
waitForPlayer(function (ready) {
|
|
356
|
+
if (!ready || !window.CkHua || typeof window.CkHua.yuJiaZai !== "function") {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const prefs = readAudioPrefs();
|
|
360
|
+
window.CkHua.yuJiaZai({
|
|
361
|
+
assetBase: assetBaseFromPlayer(),
|
|
362
|
+
mode: atlasPackName(),
|
|
363
|
+
burstColor: burstColorOf(prefs),
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function releaseWarmedBgm() {
|
|
369
|
+
warmedBgm.forEach(function (audio) {
|
|
370
|
+
try {
|
|
371
|
+
audio.pause();
|
|
372
|
+
audio.removeAttribute("src");
|
|
373
|
+
audio.load();
|
|
374
|
+
} catch (_error) {
|
|
375
|
+
// ignore
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
warmedBgm.clear();
|
|
379
|
+
}
|
|
380
|
+
|
|
259
381
|
function hookPreviewAssetSrc(proto) {
|
|
260
382
|
if (!proto) return;
|
|
261
383
|
const desc = Object.getOwnPropertyDescriptor(proto, "src");
|
|
@@ -277,39 +399,34 @@
|
|
|
277
399
|
hookPreviewAssetSrc(window.HTMLMediaElement && window.HTMLMediaElement.prototype);
|
|
278
400
|
}
|
|
279
401
|
|
|
280
|
-
|
|
281
|
-
if (!payload) return null;
|
|
282
|
-
return payload.song || payload;
|
|
283
|
-
}
|
|
402
|
+
let mountQueue = Promise.resolve();
|
|
284
403
|
|
|
285
|
-
function
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const slot = slots[i] || {};
|
|
294
|
-
if (String(slot.style || "") !== style) continue;
|
|
295
|
-
if (String(slot.rank || "") !== rank) continue;
|
|
296
|
-
const blob = clip === "2" ? slot.preview2 : slot.preview;
|
|
297
|
-
if (typeof blob === "string" && blob) return blob;
|
|
298
|
-
}
|
|
299
|
-
return "";
|
|
404
|
+
function anyInstancePlaying() {
|
|
405
|
+
let playing = false;
|
|
406
|
+
instances.forEach(function (instance) {
|
|
407
|
+
if (instance && typeof instance.zaiBoMe === "function" && instance.zaiBoMe()) {
|
|
408
|
+
playing = true;
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
return playing;
|
|
300
412
|
}
|
|
301
413
|
|
|
302
|
-
function
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
script = document.createElement("script");
|
|
307
|
-
script.type = "application/json";
|
|
308
|
-
const status = host.querySelector("[data-burst-preview-status]");
|
|
309
|
-
player.insertBefore(script, status || null);
|
|
414
|
+
function runWhenPlayerIdle(fn) {
|
|
415
|
+
if (!anyInstancePlaying()) {
|
|
416
|
+
fn();
|
|
417
|
+
return;
|
|
310
418
|
}
|
|
311
|
-
|
|
312
|
-
|
|
419
|
+
mountQueue = mountQueue.then(function () {
|
|
420
|
+
return new Promise(function (resolve) {
|
|
421
|
+
window.requestAnimationFrame(function () {
|
|
422
|
+
try {
|
|
423
|
+
fn();
|
|
424
|
+
} finally {
|
|
425
|
+
resolve();
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
});
|
|
313
430
|
}
|
|
314
431
|
|
|
315
432
|
function mount(host) {
|
|
@@ -318,153 +435,182 @@
|
|
|
318
435
|
}
|
|
319
436
|
|
|
320
437
|
const canvas = host.querySelector("[data-burst-preview-canvas]");
|
|
321
|
-
if (!canvas) {
|
|
322
|
-
setStatus(host, "谱面预览数据缺失。", "error");
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
438
|
let data = host.querySelector('script[type="application/json"]');
|
|
326
|
-
if (!
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
mount(host);
|
|
338
|
-
})
|
|
339
|
-
.catch(function (error) {
|
|
340
|
-
delete host.dataset.previewMounting;
|
|
341
|
-
if (typeof console !== "undefined" && console.error) {
|
|
342
|
-
console.error("谱面预览加载失败", error);
|
|
343
|
-
}
|
|
344
|
-
setStatus(host, "谱面预览数据缺失。", "error");
|
|
345
|
-
});
|
|
439
|
+
if (!canvas) return;
|
|
440
|
+
|
|
441
|
+
if (!data) {
|
|
442
|
+
if (host.dataset.previewFetching === "1") return;
|
|
443
|
+
host.dataset.previewFetching = "1";
|
|
444
|
+
setStatus(host, "正在加载谱面…", "loading");
|
|
445
|
+
|
|
446
|
+
const apiUrl = previewApiUrl();
|
|
447
|
+
if (!apiUrl) {
|
|
448
|
+
host.dataset.previewFetching = "";
|
|
449
|
+
setStatus(host, "谱面预览数据缺失。", "error");
|
|
346
450
|
return;
|
|
347
451
|
}
|
|
348
|
-
|
|
452
|
+
|
|
453
|
+
loadSongPreviewPayload(apiUrl)
|
|
454
|
+
.then(function (result) {
|
|
455
|
+
host.dataset.previewFetching = "";
|
|
456
|
+
if (!result || !result.ok || !result.song) {
|
|
457
|
+
setStatus(host, "谱面数据加载失败。", "error");
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
applyPayloadToHosts(result);
|
|
461
|
+
if (!blobFromPayload(host, result)) {
|
|
462
|
+
setStatus(host, "该档位无谱面预览。", "ready");
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
if (isExpanded(host)) mount(host);
|
|
466
|
+
})
|
|
467
|
+
.catch(function () {
|
|
468
|
+
host.dataset.previewFetching = "";
|
|
469
|
+
setStatus(host, "谱面获取失败,请重试。", "error");
|
|
470
|
+
});
|
|
349
471
|
return;
|
|
350
472
|
}
|
|
351
473
|
|
|
352
474
|
host.dataset.previewMounting = "1";
|
|
353
475
|
setStatus(host, "谱面预览加载中…", "loading");
|
|
354
476
|
waitForPlayer(function (ready) {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
477
|
+
runWhenPlayerIdle(function () {
|
|
478
|
+
try {
|
|
479
|
+
if (!host.isConnected || instances.has(host) || host.dataset.previewMounting !== "1") {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (!isExpanded(host)) {
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
if (!ready) throw new Error("preview player unavailable");
|
|
486
|
+
const snippet = JSON.parse(data.textContent);
|
|
487
|
+
if (typeof snippet !== "string" || !snippet) {
|
|
488
|
+
throw new Error("invalid preview snippet");
|
|
489
|
+
}
|
|
490
|
+
const startComboRaw = host.getAttribute("data-start-combo");
|
|
491
|
+
const startCombo = startComboRaw ? Number(startComboRaw) : NaN;
|
|
492
|
+
const prefs = readAudioPrefs();
|
|
493
|
+
const muted = effectiveMuted(prefs);
|
|
494
|
+
const audio = audioUrlsFor(host);
|
|
495
|
+
const leadRaw = Number(host.getAttribute("data-audio-lead-ms") || 0);
|
|
496
|
+
const instance = window.CkHua.gua_shang(canvas, {
|
|
497
|
+
snippet: snippet,
|
|
498
|
+
assetBase: assetBaseFromPlayer(),
|
|
499
|
+
audioUrl: audio.audioUrl || undefined,
|
|
500
|
+
audioFallbackUrl: audio.audioFallbackUrl || undefined,
|
|
501
|
+
audioLeadMs: Number.isFinite(leadRaw) ? leadRaw : 0,
|
|
502
|
+
startCombo: Number.isFinite(startCombo) ? startCombo : undefined,
|
|
503
|
+
hitsounds: Boolean(prefs.hitsounds),
|
|
504
|
+
burstColor: burstColorOf(prefs),
|
|
505
|
+
burstSfx: true,
|
|
506
|
+
volume: prefs.volume,
|
|
507
|
+
muted: muted,
|
|
508
|
+
});
|
|
509
|
+
if (instance && typeof instance.xieYinLiang === "function") {
|
|
510
|
+
instance.xieYinLiang(prefs.volume, muted);
|
|
511
|
+
}
|
|
512
|
+
if (instance && typeof instance.xieAnJianYin === "function") {
|
|
513
|
+
instance.xieAnJianYin(Boolean(prefs.hitsounds));
|
|
514
|
+
}
|
|
515
|
+
if (instance && typeof instance.xieBaoSe === "function") {
|
|
516
|
+
instance.xieBaoSe(burstColorOf(prefs));
|
|
517
|
+
}
|
|
518
|
+
bindInstancePersist(host, instance);
|
|
519
|
+
instances.set(host, instance || null);
|
|
520
|
+
host.__notePreview = instance || null;
|
|
521
|
+
window.__notePreview = instance || null;
|
|
522
|
+
setStatus(host, "", "ready");
|
|
523
|
+
} catch (error) {
|
|
524
|
+
if (typeof console !== "undefined" && console.error) {
|
|
525
|
+
console.error("谱面预览加载失败", error);
|
|
526
|
+
}
|
|
527
|
+
setStatus(host, "谱面预览加载失败,请稍后重试。", "error");
|
|
528
|
+
} finally {
|
|
529
|
+
delete host.dataset.previewMounting;
|
|
396
530
|
}
|
|
397
|
-
|
|
398
|
-
} finally {
|
|
399
|
-
delete host.dataset.previewMounting;
|
|
400
|
-
}
|
|
531
|
+
});
|
|
401
532
|
});
|
|
402
533
|
}
|
|
403
534
|
|
|
535
|
+
function unmountHost(host) {
|
|
536
|
+
if (!host) return;
|
|
537
|
+
const instance = instances.get(host);
|
|
538
|
+
if (instance) {
|
|
539
|
+
try {
|
|
540
|
+
if (typeof instance.zanTingg === "function") instance.zanTingg();
|
|
541
|
+
} catch (_error) {
|
|
542
|
+
// ignore
|
|
543
|
+
}
|
|
544
|
+
try {
|
|
545
|
+
if (typeof instance.chai_diao === "function") instance.chai_diao();
|
|
546
|
+
} catch (_error) {
|
|
547
|
+
// ignore
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
instances.delete(host);
|
|
551
|
+
if (host.__notePreview) host.__notePreview = null;
|
|
552
|
+
if (window.__notePreview === instance) window.__notePreview = null;
|
|
553
|
+
delete host.dataset.previewMounting;
|
|
554
|
+
setStatus(host, "谱面预览加载中…", "loading");
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function pauseHost(host) {
|
|
558
|
+
const instance = instances.get(host);
|
|
559
|
+
if (!instance || typeof instance.zanTingg !== "function") return;
|
|
560
|
+
if (typeof instance.zaiBoMe === "function" && !instance.zaiBoMe()) return;
|
|
561
|
+
try {
|
|
562
|
+
instance.zanTingg();
|
|
563
|
+
} catch (_error) {
|
|
564
|
+
// ignore
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function pauseItem(item) {
|
|
569
|
+
if (!item) return;
|
|
570
|
+
item.querySelectorAll(HOST_SELECTOR).forEach(pauseHost);
|
|
571
|
+
}
|
|
572
|
+
|
|
404
573
|
function mountExpanded(root) {
|
|
405
574
|
const scope = root && root.querySelectorAll ? root : document;
|
|
406
|
-
scope.querySelectorAll(
|
|
407
|
-
if (scope.
|
|
575
|
+
scope.querySelectorAll(".burst-item").forEach(syncItem);
|
|
576
|
+
if (scope.classList && scope.classList.contains("burst-item")) syncItem(scope);
|
|
408
577
|
}
|
|
409
578
|
|
|
410
|
-
function
|
|
411
|
-
if (!item
|
|
579
|
+
function syncItem(item) {
|
|
580
|
+
if (!item) return;
|
|
581
|
+
if (!item.classList.contains("is-open")) {
|
|
582
|
+
// 收起只是 CSS 隐藏卡片:暂停即可,不要 destroy/重新挂载。
|
|
583
|
+
pauseItem(item);
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
412
586
|
item.querySelectorAll(HOST_SELECTOR).forEach(mount);
|
|
587
|
+
warmExpandedBgm();
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
function mountItem(item) {
|
|
591
|
+
syncItem(item);
|
|
413
592
|
}
|
|
414
593
|
|
|
415
594
|
function onAccordionChange(event) {
|
|
416
595
|
const item = event.target.closest ? event.target.closest(".burst-item") : null;
|
|
417
|
-
|
|
596
|
+
syncItem(item);
|
|
418
597
|
}
|
|
419
598
|
|
|
420
599
|
function destroyAll() {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
observer = null;
|
|
424
|
-
}
|
|
425
|
-
instances.forEach(function (instance) {
|
|
426
|
-
if (instance && typeof instance.destroy === "function") {
|
|
427
|
-
try {
|
|
428
|
-
instance.destroy();
|
|
429
|
-
} catch (_error) {
|
|
430
|
-
// The page is leaving; one failed teardown must not block the others.
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
});
|
|
434
|
-
instances.clear();
|
|
600
|
+
Array.from(instances.keys()).forEach(unmountHost);
|
|
601
|
+
releaseWarmedBgm();
|
|
435
602
|
}
|
|
436
603
|
|
|
437
604
|
function init() {
|
|
438
605
|
installPreviewAssetVersioning();
|
|
606
|
+
prefetchAllPreviewPayloads();
|
|
607
|
+
preloadPlayerAssets();
|
|
608
|
+
warmExpandedBgm();
|
|
439
609
|
mountExpanded(document);
|
|
440
610
|
|
|
441
|
-
document.addEventListener("click", function (event) {
|
|
442
|
-
const toggle = event.target.closest ? event.target.closest("[data-burst-toggle]") : null;
|
|
443
|
-
if (!toggle) return;
|
|
444
|
-
window.setTimeout(function () {
|
|
445
|
-
mountItem(toggle.closest(".burst-item"));
|
|
446
|
-
}, 0);
|
|
447
|
-
});
|
|
448
611
|
document.addEventListener("burst:accordion-change", onAccordionChange);
|
|
449
612
|
document.addEventListener("burst-accordion:change", onAccordionChange);
|
|
450
613
|
|
|
451
|
-
observer = new MutationObserver(function (mutations) {
|
|
452
|
-
mutations.forEach(function (mutation) {
|
|
453
|
-
if (
|
|
454
|
-
mutation.type === "attributes" &&
|
|
455
|
-
mutation.attributeName === "class" &&
|
|
456
|
-
mutation.target.classList.contains("burst-item")
|
|
457
|
-
) {
|
|
458
|
-
mountItem(mutation.target);
|
|
459
|
-
}
|
|
460
|
-
});
|
|
461
|
-
});
|
|
462
|
-
observer.observe(document.body, {
|
|
463
|
-
subtree: true,
|
|
464
|
-
attributes: true,
|
|
465
|
-
attributeFilter: ["class"],
|
|
466
|
-
});
|
|
467
|
-
|
|
468
614
|
window.addEventListener(AUDIO_PREFS_EVENT, function (event) {
|
|
469
615
|
const prefs = (event && event.detail) || readAudioPrefs();
|
|
470
616
|
applyAudioPrefs(prefs);
|
|
@@ -472,15 +618,7 @@
|
|
|
472
618
|
|
|
473
619
|
window.addEventListener("load", function () {
|
|
474
620
|
mountExpanded(document);
|
|
475
|
-
|
|
476
|
-
Array.prototype.slice.call(document.querySelectorAll('script[src*="note-preview"]')).forEach(function (el) {
|
|
477
|
-
el.addEventListener(
|
|
478
|
-
"load",
|
|
479
|
-
function () {
|
|
480
|
-
mountExpanded(document);
|
|
481
|
-
},
|
|
482
|
-
{ once: true }
|
|
483
|
-
);
|
|
621
|
+
warmExpandedBgm();
|
|
484
622
|
});
|
|
485
623
|
window.addEventListener("pagehide", destroyAll, { once: true });
|
|
486
624
|
}
|