87 lines
4.1 KiB
JavaScript
87 lines
4.1 KiB
JavaScript
// webAPP 顶栏:回退/前进/重载 + 标题 + 标签面板(切换/关闭)
|
||
// webapp topbar: back/forward/reload + title + tab panel (switch/close)
|
||
// 原生方法(webGoBack/webGoForward/webReload/switchTab/closeWebApp)Task 13 才实现,
|
||
// 此处仅作为调用方,点击会静默 reject(符合预期)。
|
||
// Native methods (webGoBack/webGoForward/webReload/switchTab/closeWebApp) land in Task 13;
|
||
// this topbar is only a caller, clicks silently reject (expected).
|
||
|
||
// 展示/刷新顶栏:tabs 为标签数组(含 active 标记)
|
||
// Show/refresh the topbar: tabs is the tab array (with active flag)
|
||
window.showTopbar = function (tabs) {
|
||
let bar = document.getElementById('web-topbar');
|
||
if (!bar) {
|
||
bar = document.createElement('div');
|
||
bar.id = 'web-topbar';
|
||
const back = document.createElement('button');
|
||
back.id = 'tb-back'; back.title = '回退'; back.textContent = '←';
|
||
const fwd = document.createElement('button');
|
||
fwd.id = 'tb-fwd'; fwd.title = '前进'; fwd.textContent = '→';
|
||
const reload = document.createElement('button');
|
||
reload.id = 'tb-reload'; reload.title = '重载'; reload.textContent = '⟳';
|
||
const title = document.createElement('span');
|
||
title.className = 'tb-title'; title.id = 'tb-title';
|
||
const tabsBtn = document.createElement('button');
|
||
tabsBtn.id = 'tb-tabs';
|
||
bar.append(back, fwd, reload, title, tabsBtn);
|
||
document.body.appendChild(bar);
|
||
back.onclick = () => bridge.call('webGoBack');
|
||
fwd.onclick = () => bridge.call('webGoForward');
|
||
reload.onclick = () => bridge.call('webReload');
|
||
}
|
||
// 标签按钮:调原生弹出标签面板(PopupWindow 在内容 WebView 之上)
|
||
// Tab button: show the native tab panel (PopupWindow above the content WebView)
|
||
document.getElementById('tb-tabs').onclick = () => bridge.call('showTabPanel');
|
||
document.getElementById('tb-tabs').textContent = `标签 (${tabs.length})`;
|
||
const active = tabs.find(t => t.active);
|
||
document.getElementById('tb-title').textContent = active ? active.name : '';
|
||
// 无标签时隐藏顶栏与标签面板(回到桌面)
|
||
// Hide the topbar and tab panel when no tabs remain (back to desktop)
|
||
bar.style.display = tabs.length ? 'flex' : 'none';
|
||
const panel = document.getElementById('tab-panel');
|
||
if (panel && !tabs.length) panel.style.display = 'none';
|
||
// 有 webapp 时隐藏 webapplist 页面内容(避免半透明顶栏透出列表控件),无 webapp 时恢复
|
||
// Hide the webapp list content while a webapp is open (so the translucent topbar
|
||
// doesn't show the list controls through it); restore when none are open
|
||
document.body.classList.toggle('webapp-active', tabs.length > 0);
|
||
};
|
||
|
||
// 标签面板:列出所有标签,点击切换,× 关闭
|
||
// Tab panel: list all tabs, click to switch, × to close
|
||
// 用 createElement + textContent/dataset 渲染,避免 innerHTML 拼接用户数据(XSS)
|
||
// Render with createElement + textContent/dataset to avoid innerHTML user data (XSS)
|
||
function showTabPanel(tabs) {
|
||
let panel = document.getElementById('tab-panel');
|
||
if (!panel) {
|
||
panel = document.createElement('div');
|
||
panel.id = 'tab-panel';
|
||
document.body.appendChild(panel);
|
||
}
|
||
panel.textContent = '';
|
||
tabs.forEach(t => {
|
||
const row = document.createElement('div');
|
||
row.className = 'tab-row' + (t.active ? ' active' : '');
|
||
row.dataset.id = t.id;
|
||
const name = document.createElement('span');
|
||
name.className = 'tab-name';
|
||
name.textContent = t.name;
|
||
const close = document.createElement('button');
|
||
close.className = 'tab-close';
|
||
close.title = '关闭';
|
||
close.textContent = '×';
|
||
name.onclick = () => { bridge.call('switchTab', t.id); closeTabPanel(); };
|
||
close.onclick = () => bridge.call('closeWebApp', t.id);
|
||
row.append(name, close);
|
||
panel.appendChild(row);
|
||
});
|
||
// 横向展开(CSS flex-direction: row)
|
||
// Expand horizontally (CSS flex-direction: row)
|
||
panel.style.display = 'flex';
|
||
}
|
||
|
||
// 收起标签面板
|
||
// Collapse the tab panel
|
||
function closeTabPanel() {
|
||
const panel = document.getElementById('tab-panel');
|
||
if (panel) panel.style.display = 'none';
|
||
}
|