zsbqb-static 0.1.0
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 -0
- package/catalog/bubble-4f53cda18c2b.json +1 -0
- package/catalog/crescent-4f53cda18c2b.json +1 -0
- package/catalog/dianfeng-4f53cda18c2b.json +1 -0
- package/catalog/manifest.json +1 -0
- package/catalog/pinball-4f53cda18c2b.json +1 -0
- package/catalog/pinyin-4f53cda18c2b.json +1 -0
- package/catalog/xingdong-4f53cda18c2b.json +1 -0
- package/css/app.css +2157 -0
- package/img/apple-touch-icon.png +0 -0
- package/img/brand-logo.png +0 -0
- package/img/favicon-192.png +0 -0
- package/img/favicon-32.png +0 -0
- package/img/favicon.ico +0 -0
- package/img/modes/bubble.png +0 -0
- package/img/modes/crescent.png +0 -0
- package/img/modes/dianfeng-master.png +0 -0
- package/img/modes/dianfeng.png +0 -0
- package/img/modes/master.png +0 -0
- package/img/modes/pinball.png +0 -0
- package/img/modes/xingdong.png +0 -0
- package/img/skills/fire.png +0 -0
- package/img/skills/limit.png +0 -0
- package/js/app.js +2124 -0
- package/js/auth-shell-sync.js +45 -0
- package/js/burst-preview-host.js +493 -0
- package/js/catalog-search.js +311 -0
- package/js/query-app.js +331 -0
- package/note-preview/assets/atlas/bubble.webp +0 -0
- package/note-preview/assets/atlas/crescent.webp +0 -0
- package/note-preview/assets/atlas/idol.webp +0 -0
- package/note-preview/assets/atlas/manifest.json +1 -0
- package/note-preview/assets/atlas/pinball.webp +0 -0
- package/note-preview/assets/atlas/shared.webp +0 -0
- package/note-preview/assets/sfx/burst-end.m4a +0 -0
- package/note-preview/assets/sfx/burst-start.m4a +0 -0
- package/note-preview/assets/sfx/key-tone-bubble-slide.m4a +0 -0
- package/note-preview/assets/sfx/key-tone-bubble.m4a +0 -0
- package/note-preview/assets/sfx/key-tone-classic.m4a +0 -0
- package/note-preview/assets/sfx/key-tone-crescent-slide.m4a +0 -0
- package/note-preview/assets/sfx/key-tone-crescent.m4a +0 -0
- package/note-preview/assets/sfx/key-tone-long.m4a +0 -0
- package/note-preview/assets/sfx/key-tone-pinball-slide.m4a +0 -0
- package/note-preview/assets/sfx/key-tone-pinball.m4a +0 -0
- package/note-preview/assets/sfx/key-tone-rhythm.m4a +0 -0
- package/note-preview/assets/sfx/key-tone-slide.m4a +0 -0
- package/note-preview/assets/sfx/key-tone-xingdong-slide.m4a +0 -0
- package/note-preview/assets/sfx/key-tone-xingdong.m4a +0 -0
- package/note-preview/assets/sfx/key-tone.m4a +0 -0
- package/note-preview/note-preview.min.js +1 -0
- package/package.json +18 -0
- package/public/season.json +1 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 游客静态页与已登录动态页共用同一 URL。
|
|
3
|
+
* 浏览器 HTTP 缓存 / bfcache 可能在登录后仍展示登录前的游客壳(或退出后仍展示用户壳)。
|
|
4
|
+
* sessionid 为 HttpOnly,不可读;用非敏感 cookie zsbqb_auth 与 data-auth-shell 比对。
|
|
5
|
+
* 不一致时强制重新拉取文档(每路径只重试一次,避免死循环)。
|
|
6
|
+
*/
|
|
7
|
+
(function () {
|
|
8
|
+
var AUTH_MARKER = "zsbqb_auth";
|
|
9
|
+
|
|
10
|
+
function hasAuthMarker() {
|
|
11
|
+
return document.cookie.split(";").some(function (part) {
|
|
12
|
+
return part.trim().indexOf(AUTH_MARKER + "=") === 0;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function shellMismatch() {
|
|
17
|
+
var shell = document.documentElement.getAttribute("data-auth-shell");
|
|
18
|
+
if (shell === "guest" && hasAuthMarker()) return true;
|
|
19
|
+
if (shell === "user" && !hasAuthMarker()) return true;
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function syncAuthShell() {
|
|
24
|
+
if (!shellMismatch()) return;
|
|
25
|
+
var key = "auth-shell-reload:" + location.pathname;
|
|
26
|
+
try {
|
|
27
|
+
if (sessionStorage.getItem(key)) {
|
|
28
|
+
sessionStorage.removeItem(key);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
sessionStorage.setItem(key, "1");
|
|
32
|
+
} catch (err) {
|
|
33
|
+
/* private mode 等:仍尝试一次 reload */
|
|
34
|
+
}
|
|
35
|
+
location.reload();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
window.addEventListener("pageshow", function (event) {
|
|
39
|
+
if (event.persisted || shellMismatch()) {
|
|
40
|
+
syncAuthShell();
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
syncAuthShell();
|
|
45
|
+
})();
|
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const HOST_SELECTOR = "[data-burst-preview-host]";
|
|
5
|
+
const instances = new Map();
|
|
6
|
+
let observer = null;
|
|
7
|
+
|
|
8
|
+
function setStatus(host, message, state) {
|
|
9
|
+
const status = host.querySelector("[data-burst-preview-status]");
|
|
10
|
+
host.dataset.previewState = state;
|
|
11
|
+
if (!status) return;
|
|
12
|
+
status.textContent = message;
|
|
13
|
+
const ready = state === "ready";
|
|
14
|
+
status.hidden = ready;
|
|
15
|
+
if (ready) status.setAttribute("hidden", "");
|
|
16
|
+
else status.removeAttribute("hidden");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isExpanded(host) {
|
|
20
|
+
const item = host.closest(".burst-item");
|
|
21
|
+
return !item || item.classList.contains("is-open");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function previewLibrary() {
|
|
25
|
+
if (window.NotePreview && typeof window.NotePreview.mount === "function") {
|
|
26
|
+
return window.NotePreview;
|
|
27
|
+
}
|
|
28
|
+
const ck = window.CkHua;
|
|
29
|
+
if (ck && typeof ck.gua_shang === "function") {
|
|
30
|
+
return {
|
|
31
|
+
mount: function (el, opts) {
|
|
32
|
+
return englishPlayer(ck.gua_shang(el, opts));
|
|
33
|
+
},
|
|
34
|
+
preloadAssets: ck.yuJiaZai,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function bindFn(raw, english, chinese) {
|
|
41
|
+
if (!raw) return undefined;
|
|
42
|
+
if (typeof raw[english] === "function") return raw[english].bind(raw);
|
|
43
|
+
if (typeof raw[chinese] === "function") return raw[chinese].bind(raw);
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function englishPlayer(raw) {
|
|
48
|
+
if (!raw) return raw;
|
|
49
|
+
if (typeof raw.getTime === "function" && typeof raw.play === "function") return raw;
|
|
50
|
+
return {
|
|
51
|
+
seek: bindFn(raw, "seek", "laJinTiao"),
|
|
52
|
+
play: bindFn(raw, "play", "kai_bo"),
|
|
53
|
+
pause: bindFn(raw, "pause", "zanTingg"),
|
|
54
|
+
isPlaying: bindFn(raw, "isPlaying", "zaiBoMe"),
|
|
55
|
+
getTime: bindFn(raw, "getTime", "kanShiJian"),
|
|
56
|
+
getOutput: bindFn(raw, "getOutput", "duYinLiang"),
|
|
57
|
+
setOutput: bindFn(raw, "setOutput", "xieYinLiang"),
|
|
58
|
+
getHitsounds: bindFn(raw, "getHitsounds", "duAnJianYin"),
|
|
59
|
+
setHitsounds: bindFn(raw, "setHitsounds", "xieAnJianYin"),
|
|
60
|
+
getRate: bindFn(raw, "getRate", "duBeiSu"),
|
|
61
|
+
setRate: bindFn(raw, "setRate", "xieBeiSu"),
|
|
62
|
+
getNoteSpeed: bindFn(raw, "getNoteSpeed", "duLiuSu"),
|
|
63
|
+
setNoteSpeed: bindFn(raw, "setNoteSpeed", "xieLiuSu"),
|
|
64
|
+
getBurstColor: bindFn(raw, "getBurstColor", "duBaoSe"),
|
|
65
|
+
setBurstColor: bindFn(raw, "setBurstColor", "xieBaoSe"),
|
|
66
|
+
on: bindFn(raw, "on", "ting_zhe"),
|
|
67
|
+
destroy: bindFn(raw, "destroy", "chai_diao"),
|
|
68
|
+
getSize: bindFn(raw, "getSize", "kanDaXiao"),
|
|
69
|
+
debugIdol: bindFn(raw, "debugIdol", "kanTiaoShi"),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function waitForPlayer(done) {
|
|
74
|
+
if (previewLibrary()) {
|
|
75
|
+
done(true);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
var tries = 0;
|
|
79
|
+
var timer = window.setInterval(function () {
|
|
80
|
+
tries += 1;
|
|
81
|
+
if (previewLibrary()) {
|
|
82
|
+
window.clearInterval(timer);
|
|
83
|
+
done(true);
|
|
84
|
+
} else if (tries >= 400) {
|
|
85
|
+
window.clearInterval(timer);
|
|
86
|
+
done(false);
|
|
87
|
+
}
|
|
88
|
+
}, 50);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function audioUrlsFor(host) {
|
|
92
|
+
const cdn = (host.getAttribute("data-audio-url") || "").trim();
|
|
93
|
+
const origin = (host.getAttribute("data-audio-fallback") || "").trim();
|
|
94
|
+
return {
|
|
95
|
+
audioUrl: cdn || origin,
|
|
96
|
+
audioFallbackUrl: cdn && origin && origin !== cdn ? origin : "",
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const AUDIO_PREFS_KEY = "zsbqb.preview.audio";
|
|
101
|
+
const AUDIO_PREFS_EVENT = "zsbqb:preview-audio";
|
|
102
|
+
|
|
103
|
+
function fallbackAudioPrefs() {
|
|
104
|
+
try {
|
|
105
|
+
const raw = window.localStorage.getItem(AUDIO_PREFS_KEY);
|
|
106
|
+
if (!raw) return { volume: 1, muted: false, alwaysMute: false, hitsounds: false, burstColor: "pink" };
|
|
107
|
+
const parsed = JSON.parse(raw);
|
|
108
|
+
const volume = Number(parsed && parsed.volume);
|
|
109
|
+
const audible = Number.isFinite(volume) ? Math.min(1, Math.max(0, volume)) : 1;
|
|
110
|
+
return {
|
|
111
|
+
volume: audible > 0 ? audible : 1,
|
|
112
|
+
muted: Boolean(parsed && parsed.muted) || audible <= 0,
|
|
113
|
+
alwaysMute: Boolean(parsed && parsed.alwaysMute),
|
|
114
|
+
hitsounds: Boolean(parsed && parsed.hitsounds),
|
|
115
|
+
burstColor: parsed && parsed.burstColor === "red" ? "red" : "pink",
|
|
116
|
+
};
|
|
117
|
+
} catch (_error) {
|
|
118
|
+
return { volume: 1, muted: false, alwaysMute: false, hitsounds: false, burstColor: "pink" };
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function readAudioPrefs() {
|
|
123
|
+
if (window.ZsbqbPreviewAudio && typeof window.ZsbqbPreviewAudio.read === "function") {
|
|
124
|
+
return window.ZsbqbPreviewAudio.read();
|
|
125
|
+
}
|
|
126
|
+
return fallbackAudioPrefs();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function effectiveMuted(prefs) {
|
|
130
|
+
return Boolean(prefs && (prefs.alwaysMute || prefs.muted));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function persistAudioPrefs(state) {
|
|
134
|
+
const current = readAudioPrefs();
|
|
135
|
+
const next = {
|
|
136
|
+
volume: state && state.volume,
|
|
137
|
+
muted: current.alwaysMute ? current.muted : Boolean(state && state.muted),
|
|
138
|
+
alwaysMute: current.alwaysMute,
|
|
139
|
+
hitsounds: current.hitsounds,
|
|
140
|
+
burstColor: current.burstColor === "red" ? "red" : "pink",
|
|
141
|
+
};
|
|
142
|
+
if (window.ZsbqbPreviewAudio && typeof window.ZsbqbPreviewAudio.write === "function") {
|
|
143
|
+
window.ZsbqbPreviewAudio.write(next, { apply: !current.alwaysMute });
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const prefs = {
|
|
147
|
+
volume: Math.min(1, Math.max(0, Number(next.volume) || 1)),
|
|
148
|
+
muted: Boolean(next.muted),
|
|
149
|
+
alwaysMute: Boolean(next.alwaysMute),
|
|
150
|
+
hitsounds: Boolean(next.hitsounds),
|
|
151
|
+
burstColor: next.burstColor === "red" ? "red" : "pink",
|
|
152
|
+
};
|
|
153
|
+
try {
|
|
154
|
+
window.localStorage.setItem(AUDIO_PREFS_KEY, JSON.stringify(prefs));
|
|
155
|
+
} catch (_error) {
|
|
156
|
+
// Private mode can reject localStorage writes.
|
|
157
|
+
}
|
|
158
|
+
if (!current.alwaysMute) {
|
|
159
|
+
window.dispatchEvent(new CustomEvent(AUDIO_PREFS_EVENT, { detail: prefs }));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function burstColorOf(prefs) {
|
|
164
|
+
return prefs && prefs.burstColor === "red" ? "red" : "pink";
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function applyAudioPrefs(prefs) {
|
|
168
|
+
if (!prefs) return;
|
|
169
|
+
const muted = effectiveMuted(prefs);
|
|
170
|
+
instances.forEach(function (instance) {
|
|
171
|
+
if (prefs.apply !== false && instance && typeof instance.setOutput === "function") {
|
|
172
|
+
instance.setOutput(prefs.volume, muted);
|
|
173
|
+
}
|
|
174
|
+
if (instance && typeof instance.setHitsounds === "function") {
|
|
175
|
+
instance.setHitsounds(Boolean(prefs.hitsounds));
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
// 爆气色只影响之后挂载的播放器,避免切换偏好时立刻下载另一套贴图。
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function bindInstancePersist(host, instance) {
|
|
182
|
+
if (instance && typeof instance.on === "function") {
|
|
183
|
+
try {
|
|
184
|
+
instance.on("volume", persistAudioPrefs);
|
|
185
|
+
} catch (_error) {
|
|
186
|
+
// Older players only support on("zone").
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const persistFromPlayer = function () {
|
|
190
|
+
if (instance && typeof instance.getOutput === "function") {
|
|
191
|
+
persistAudioPrefs(instance.getOutput());
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
host.addEventListener("click", function (event) {
|
|
195
|
+
const target = event.target;
|
|
196
|
+
if (!target || !target.closest || !target.closest(".note-preview-speaker")) return;
|
|
197
|
+
window.setTimeout(persistFromPlayer, 0);
|
|
198
|
+
});
|
|
199
|
+
host.addEventListener("input", function (event) {
|
|
200
|
+
const target = event.target;
|
|
201
|
+
if (!target || !target.classList || !target.classList.contains("note-preview-volume__slider")) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
persistFromPlayer();
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function assetBaseFromPlayer() {
|
|
209
|
+
const configured = (
|
|
210
|
+
(typeof window !== "undefined" && window.__NOTE_PREVIEW_ASSET_BASE__) ||
|
|
211
|
+
""
|
|
212
|
+
).trim();
|
|
213
|
+
if (configured) {
|
|
214
|
+
return configured.charAt(configured.length - 1) === "/" ? configured : configured + "/";
|
|
215
|
+
}
|
|
216
|
+
const scripts = Array.prototype.slice.call(document.getElementsByTagName("script"));
|
|
217
|
+
const withSrc = scripts.filter(function (script) {
|
|
218
|
+
return Boolean(script.src);
|
|
219
|
+
});
|
|
220
|
+
const named = withSrc.filter(function (script) {
|
|
221
|
+
return script.src.indexOf("note-preview") !== -1;
|
|
222
|
+
});
|
|
223
|
+
const fallback = withSrc.filter(function (script) {
|
|
224
|
+
return script.src.indexOf("burst-preview-host") === -1;
|
|
225
|
+
});
|
|
226
|
+
const chosen = named[named.length - 1] || fallback[fallback.length - 1];
|
|
227
|
+
const src = chosen && chosen.src ? chosen.src : "";
|
|
228
|
+
const clean = src.split(/[?#]/)[0];
|
|
229
|
+
const slash = clean.lastIndexOf("/");
|
|
230
|
+
const dir = slash >= 0 ? clean.slice(0, slash + 1) : "./";
|
|
231
|
+
return dir + "assets/";
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function stampPreviewAssetUrl(url) {
|
|
235
|
+
const ver = (
|
|
236
|
+
(typeof window !== "undefined" && window.__NOTE_PREVIEW_ASSET_VER__) ||
|
|
237
|
+
""
|
|
238
|
+
).trim();
|
|
239
|
+
const base = (
|
|
240
|
+
(typeof window !== "undefined" && window.__NOTE_PREVIEW_ASSET_BASE__) ||
|
|
241
|
+
""
|
|
242
|
+
).trim();
|
|
243
|
+
if (!ver || !url || typeof url !== "string") return url;
|
|
244
|
+
if (/[?&]v=/.test(url)) return url;
|
|
245
|
+
let absUrl = url;
|
|
246
|
+
let absBase = base;
|
|
247
|
+
try {
|
|
248
|
+
absUrl = new URL(url, window.location.href).href;
|
|
249
|
+
if (base) {
|
|
250
|
+
absBase = new URL(base.charAt(base.length - 1) === "/" ? base : base + "/", window.location.href).href;
|
|
251
|
+
}
|
|
252
|
+
} catch (_error) {
|
|
253
|
+
return url;
|
|
254
|
+
}
|
|
255
|
+
if (absBase && absUrl.indexOf(absBase) !== 0) return url;
|
|
256
|
+
return url + (url.indexOf("?") >= 0 ? "&" : "?") + "v=" + encodeURIComponent(ver);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function hookPreviewAssetSrc(proto) {
|
|
260
|
+
if (!proto) return;
|
|
261
|
+
const desc = Object.getOwnPropertyDescriptor(proto, "src");
|
|
262
|
+
if (!desc || typeof desc.set !== "function") return;
|
|
263
|
+
Object.defineProperty(proto, "src", {
|
|
264
|
+
configurable: true,
|
|
265
|
+
enumerable: desc.enumerable,
|
|
266
|
+
get: desc.get,
|
|
267
|
+
set: function (value) {
|
|
268
|
+
desc.set.call(this, stampPreviewAssetUrl(value));
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function installPreviewAssetVersioning() {
|
|
274
|
+
if (typeof window === "undefined" || window.__NOTE_PREVIEW_ASSET_VER_HOOK__) return;
|
|
275
|
+
window.__NOTE_PREVIEW_ASSET_VER_HOOK__ = true;
|
|
276
|
+
hookPreviewAssetSrc(window.HTMLImageElement && window.HTMLImageElement.prototype);
|
|
277
|
+
hookPreviewAssetSrc(window.HTMLMediaElement && window.HTMLMediaElement.prototype);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function songFromPayload(payload) {
|
|
281
|
+
if (!payload) return null;
|
|
282
|
+
return payload.song || payload;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function blobFromSong(song, host) {
|
|
286
|
+
const item = host.closest(".burst-item");
|
|
287
|
+
const kind = item && item.getAttribute("data-burst-kind");
|
|
288
|
+
const slots = kind === "double" ? (song && song.doubles) || [] : (song && song.singles) || [];
|
|
289
|
+
const style = host.getAttribute("data-style") || "";
|
|
290
|
+
const rank = host.getAttribute("data-rank") || "";
|
|
291
|
+
const clip = host.getAttribute("data-burst-clip") || "1";
|
|
292
|
+
for (let i = 0; i < slots.length; i += 1) {
|
|
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 "";
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function writeSnippet(host, blob) {
|
|
303
|
+
const player = host.querySelector(".burst-preview__player") || host;
|
|
304
|
+
let script = host.querySelector('script[type="application/json"]');
|
|
305
|
+
if (!script) {
|
|
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);
|
|
310
|
+
}
|
|
311
|
+
script.textContent = JSON.stringify(blob);
|
|
312
|
+
return script;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function mount(host) {
|
|
316
|
+
if (instances.has(host) || host.dataset.previewMounting === "1" || !isExpanded(host)) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const canvas = host.querySelector("[data-burst-preview-canvas]");
|
|
321
|
+
if (!canvas) {
|
|
322
|
+
setStatus(host, "谱面预览数据缺失。", "error");
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
let data = host.querySelector('script[type="application/json"]');
|
|
326
|
+
if (!data || !String(data.textContent || "").trim()) {
|
|
327
|
+
const pending = window.__NOTE_PREVIEW_PAYLOAD__;
|
|
328
|
+
if (pending && typeof pending.then === "function") {
|
|
329
|
+
host.dataset.previewMounting = "1";
|
|
330
|
+
setStatus(host, "谱面预览加载中…", "loading");
|
|
331
|
+
Promise.resolve(pending)
|
|
332
|
+
.then(function (payload) {
|
|
333
|
+
const blob = blobFromSong(songFromPayload(payload), host);
|
|
334
|
+
if (!blob) throw new Error("preview blob missing");
|
|
335
|
+
writeSnippet(host, blob);
|
|
336
|
+
delete host.dataset.previewMounting;
|
|
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
|
+
});
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
setStatus(host, "谱面预览数据缺失。", "error");
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
host.dataset.previewMounting = "1";
|
|
353
|
+
setStatus(host, "谱面预览加载中…", "loading");
|
|
354
|
+
waitForPlayer(function (ready) {
|
|
355
|
+
try {
|
|
356
|
+
const lib = previewLibrary();
|
|
357
|
+
if (!ready || !lib) throw new Error("preview player unavailable");
|
|
358
|
+
const snippet = JSON.parse(data.textContent);
|
|
359
|
+
if (typeof snippet !== "string" || !snippet) {
|
|
360
|
+
throw new Error("invalid preview snippet");
|
|
361
|
+
}
|
|
362
|
+
const startComboRaw = host.getAttribute("data-start-combo");
|
|
363
|
+
const startCombo = startComboRaw ? Number(startComboRaw) : NaN;
|
|
364
|
+
const prefs = readAudioPrefs();
|
|
365
|
+
const muted = effectiveMuted(prefs);
|
|
366
|
+
const audio = audioUrlsFor(host);
|
|
367
|
+
const instance = lib.mount(canvas, {
|
|
368
|
+
snippet: snippet,
|
|
369
|
+
assetBase: assetBaseFromPlayer(),
|
|
370
|
+
audioUrl: audio.audioUrl || undefined,
|
|
371
|
+
audioFallbackUrl: audio.audioFallbackUrl || undefined,
|
|
372
|
+
startCombo: Number.isFinite(startCombo) ? startCombo : undefined,
|
|
373
|
+
hitsounds: Boolean(prefs.hitsounds),
|
|
374
|
+
burstColor: burstColorOf(prefs),
|
|
375
|
+
burstSfx: true,
|
|
376
|
+
volume: prefs.volume,
|
|
377
|
+
muted: muted,
|
|
378
|
+
});
|
|
379
|
+
if (instance && typeof instance.setOutput === "function") {
|
|
380
|
+
instance.setOutput(prefs.volume, muted);
|
|
381
|
+
}
|
|
382
|
+
if (instance && typeof instance.setHitsounds === "function") {
|
|
383
|
+
instance.setHitsounds(Boolean(prefs.hitsounds));
|
|
384
|
+
}
|
|
385
|
+
if (instance && typeof instance.setBurstColor === "function") {
|
|
386
|
+
instance.setBurstColor(burstColorOf(prefs));
|
|
387
|
+
}
|
|
388
|
+
bindInstancePersist(host, instance);
|
|
389
|
+
instances.set(host, instance || null);
|
|
390
|
+
host.__notePreview = instance || null;
|
|
391
|
+
window.__notePreview = instance || null;
|
|
392
|
+
setStatus(host, "", "ready");
|
|
393
|
+
} catch (error) {
|
|
394
|
+
if (typeof console !== "undefined" && console.error) {
|
|
395
|
+
console.error("谱面预览加载失败", error);
|
|
396
|
+
}
|
|
397
|
+
setStatus(host, "谱面预览加载失败,请稍后重试。", "error");
|
|
398
|
+
} finally {
|
|
399
|
+
delete host.dataset.previewMounting;
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function mountExpanded(root) {
|
|
405
|
+
const scope = root && root.querySelectorAll ? root : document;
|
|
406
|
+
scope.querySelectorAll(HOST_SELECTOR).forEach(mount);
|
|
407
|
+
if (scope.matches && scope.matches(HOST_SELECTOR)) mount(scope);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function mountItem(item) {
|
|
411
|
+
if (!item || !item.classList.contains("is-open")) return;
|
|
412
|
+
item.querySelectorAll(HOST_SELECTOR).forEach(mount);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function onAccordionChange(event) {
|
|
416
|
+
const item = event.target.closest ? event.target.closest(".burst-item") : null;
|
|
417
|
+
mountItem(item);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function destroyAll() {
|
|
421
|
+
if (observer) {
|
|
422
|
+
observer.disconnect();
|
|
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();
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function init() {
|
|
438
|
+
installPreviewAssetVersioning();
|
|
439
|
+
mountExpanded(document);
|
|
440
|
+
|
|
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
|
+
document.addEventListener("burst:accordion-change", onAccordionChange);
|
|
449
|
+
document.addEventListener("burst-accordion:change", onAccordionChange);
|
|
450
|
+
|
|
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
|
+
window.addEventListener(AUDIO_PREFS_EVENT, function (event) {
|
|
469
|
+
const prefs = (event && event.detail) || readAudioPrefs();
|
|
470
|
+
applyAudioPrefs(prefs);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
window.addEventListener("load", function () {
|
|
474
|
+
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
|
+
);
|
|
484
|
+
});
|
|
485
|
+
window.addEventListener("pagehide", destroyAll, { once: true });
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (document.readyState === "loading") {
|
|
489
|
+
document.addEventListener("DOMContentLoaded", init, { once: true });
|
|
490
|
+
} else {
|
|
491
|
+
init();
|
|
492
|
+
}
|
|
493
|
+
})();
|