pbl_scense_ext/wwwroot/overlay/pbl_overlay_loader.js
2026-09-21 12:20:49 +08:00

115 lines
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ============================================================================
* pbl_overlay_loader.js —— 覆盖层脚本/CSS 按需注入器(M9)
*
* 为什么需要它:bricks .ui 页面禁止硬写 <script>/<link> 标签(会绕过 RBAC 或
* 触发 403)。本 loader 由页面壳(wwwroot/pbl_overlay/*.ui)的 bricks
* `script` actiontype 在用户点击「启用 PBL 覆盖层」时调用,动态注入同模块
* wwwroot/overlay 下的 JS/CSS,注入完成后调用 PblOverlayPlay.boot() 完成装配。
*
* RBAC 事实(QC #1 已补齐,与 scripts/load_path.py PATHS 表一一对应,禁止只改一边):
* - /pbl_scense_ext/overlay/pbl_overlay_loader.js → any (预鉴权页壳引导)
* - /pbl_scense_ext/overlay/pbl_overlay.css → any (页壳首屏样式)
* - /pbl_scense_ext/overlay/pbl_overlay_core.js → logined (业务内核)
* - /pbl_scense_ext/overlay/pbl_overlay_transport.js→ logined (广播/意图通道)
* - /pbl_scense_ext/overlay/pbl_overlay_play.js → logined (覆盖层装配)
* - /pbl_scense_ext/pbl_overlay/<page>.ui × 12 → logined (页面壳)
*
* 铁律:只注入本模块静态资源,不改写宿主(scense/scense_game)渲染层任何脚本。
* ==========================================================================*/
(function (global) {
'use strict';
var MODULE = 'pbl_scense_ext';
function apiBase() {
// 宿主显式注入优先;否则从当前 pathname 去掉模块路由尾部推应用根
var inj = String(global.__PBL_API_BASE__ || '');
if (inj) { return inj.replace(/\/$/, ''); }
return String(global.location && global.location.pathname || '')
.replace(/\/(pbl_overlay|overlay|game|scense_game|scense)\/.*$/, '')
.replace(/\/$/, '');
}
var DIR = apiBase() + '/' + MODULE + '/overlay/';
// 依赖顺序:core → transport → play(play 内部硬依赖前两者)
var FILES = ['pbl_overlay_core.js', 'pbl_overlay_transport.js', 'pbl_overlay_play.js'];
var CSS = 'pbl_overlay.css';
var injected = {};
var loading = null;
/* 注入失败时的最小可见降级(不依赖 core,避免「白屏无提示」)。
* 页面壳的 script 里也引用本函数:window.__pblOverlayFail(code, message) */
function degrade(code, message) {
try {
var host = document.querySelector('[data-pbl-overlay-root]') || document.body;
var box = document.getElementById('pbl-loader-degrade');
if (!box) {
box = document.createElement('div');
box.id = 'pbl-loader-degrade';
box.setAttribute('class', 'pbl-banner pbl-banner--error');
host.appendChild(box);
}
box.textContent = '[' + (code || 'PBL_E_NETWORK') + '] ' + (message || '覆盖层加载失败');
box.setAttribute('data-pbl-degrade', '1');
} catch (e) { /* DOM 不可用时静默,不抛出打断宿主 */ }
return { code: code || 'PBL_E_NETWORK', message: message || 'overlay load failed' };
}
function loadOne(src) {
return new Promise(function (resolve, reject) {
if (injected[src]) { return resolve(true); }
var s = document.createElement('script');
s.src = src;
s.async = false;
s.onload = function () { injected[src] = true; resolve(true); };
s.onerror = function () { reject({ code: 'PBL_E_NETWORK', message: '资源加载失败(403/404?) ' + src }); };
document.head.appendChild(s);
});
}
function ensureCss() {
var id = 'pbl-overlay-css';
if (document.getElementById(id)) { return; }
var l = document.createElement('link');
l.id = id;
l.rel = 'stylesheet';
l.href = DIR + CSS;
document.head.appendChild(l);
}
function load() {
ensureCss();
if (loading) { return loading; }
loading = FILES.reduce(function (p, f) {
return p.then(function () { return loadOne(DIR + f); });
}, Promise.resolve());
return loading;
}
// 幂等启动:已存在实例则复用(切换页面上下文时可 destroy 后重建)
function start(opts) {
return load().then(function () {
if (!global.PblOverlayPlay) { throw { code: 'PBL_E_NOT_FOUND', message: 'PblOverlayPlay 未注册' }; }
if (global.__PBL_OVERLAY__ && global.__PBL_OVERLAY__.mounted) { return global.__PBL_OVERLAY__; }
return global.PblOverlayPlay.boot(opts || {}) || global.__PBL_OVERLAY__;
}).catch(function (err) {
degrade((err && err.code) || 'PBL_E_LOAD_FAIL', (err && err.message) || String(err));
throw err;
});
}
global.PblOverlayLoader = {
MODULE: MODULE,
DIR: DIR,
FILES: FILES,
CSS: CSS,
apiBase: apiBase,
load: load,
start: start,
degrade: degrade
};
global.__pblOverlayFail = degrade;
})(window);