Provides an API for other userscripts to add tabs to a site's settings menu.
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/533630/1575889/Settings%20Tab%20Manager%20%28STM%29.js
// ==UserScript==
// @name Settings Tab Manager (STM)
// @namespace shared-settings-manager
// @version 1.1.3
// @description Provides an API for other userscripts to add tabs to a site's settings menu.
// @author nipah, Gemini
// @license MIT
// @match https://8chan.moe/*
// @match https://8chan.se/*
// @grant GM_addStyle
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
// ... (Constants, State, Readiness Promise, Public API Definition, Styling remain the same) ...
const log = (...args) => console.log(`[${MANAGER_ID}]`, ...args);
const warn = (...args) => console.warn(`[${MANAGER_ID}]`, ...args);
const error = (...args) => console.error(`[${MANAGER_ID}]`, ...args);
const MANAGER_ID = 'SettingsTabManager';
const SELECTORS = {/* ... */};
const ACTIVE_CLASSES = {/* ... */};
const ATTRS = {/* ... */};
// ... (findSettingsElements, deactivateCurrentTab, activateTab, handleTabClick, attachTabClickListener remain the same) ...
function handleTabClick(event) { /* ... same code (no stopPropagation) ... */ }
function activateTab(scriptId) { /* ... same code ... */ }
// --- REMOVED createSeparator function ---
/** Creates and inserts the tab and panel elements for a given script config. (No Separators) */
function createTabAndPanel(config) {
if (!tabContainerEl || !panelContainerEl) {
error(`Cannot create tab/panel for ${config.scriptId}: Containers not found.`);
return;
}
if (tabContainerEl.querySelector(`span[${ATTRS.SCRIPT_ID}="${config.scriptId}"]`)) {
log(`Tab already exists for ${config.scriptId}, skipping creation.`);
return;
}
log(`Creating tab/panel for: ${config.scriptId} (No Separator)`);
// --- Create Tab ---
const newTab = document.createElement('span');
newTab.className = SELECTORS.SITE_TAB.substring(1); // Use site's class
newTab.textContent = config.tabTitle;
newTab.setAttribute(ATTRS.SCRIPT_ID, config.scriptId);
newTab.setAttribute(ATTRS.MANAGED, 'true');
newTab.setAttribute('title', `${config.tabTitle} (Settings by ${config.scriptId})`);
const desiredOrder = typeof config.order === 'number' ? config.order : Infinity;
newTab.setAttribute('data-stm-order', desiredOrder);
// --- Create Panel ---
const newPanel = document.createElement('div');
newPanel.className = SELECTORS.SITE_PANEL.substring(1); // Use site's class
newPanel.setAttribute(ATTRS.SCRIPT_ID, config.scriptId);
newPanel.setAttribute(ATTRS.MANAGED, 'true');
newPanel.id = `${MANAGER_ID}-${config.scriptId}-panel`;
// --- Insertion Logic (Simplified: No Separators) ---
let insertBeforeElement = null;
const existingStmTabs = Array.from(
tabContainerEl.querySelectorAll(`span[${ATTRS.MANAGED}][${ATTRS.SCRIPT_ID}]`)
).sort( // Sort based on order attribute
(a, b) => parseInt(a.getAttribute('data-stm-order') || Infinity, 10) - parseInt(b.getAttribute('data-stm-order') || Infinity, 10)
);
// Find the first existing STM tab with a higher order number
for (const existingTab of existingStmTabs) {
const existingOrder = parseInt(existingTab.getAttribute('data-stm-order') || Infinity, 10);
if (desiredOrder < existingOrder) {
insertBeforeElement = existingTab;
break;
}
}
// Insert the new tab
if (insertBeforeElement) {
// Insert before the found element
tabContainerEl.insertBefore(newTab, insertBeforeElement);
} else {
// Append at the end (after all other STM tabs, potentially before native 'Other' etc.)
// We could add logic here to find the last native tab if needed,
// but appending might be sufficient.
tabContainerEl.appendChild(newTab);
}
// Append Panel (Order doesn't matter visually)
panelContainerEl.appendChild(newPanel);
// --- Initialize Panel Content ---
try {
Promise.resolve(config.onInit(newPanel, newTab)).catch(e => {
error(`Error during async onInit for ${config.scriptId}:`, e);
newPanel.innerHTML = `<p style="color: red;">Error initializing settings panel for ${config.scriptId}. See console.</p>`;
});
} catch (e) {
error(`Error during sync onInit for ${config.scriptId}:`, e);
newPanel.innerHTML = `<p style="color: red;">Error initializing settings panel for ${config.scriptId}. See console.</p>`;
}
}
// ... (processPendingRegistrations, Initialization, Observer, API Implementation, Global Exposure remain the same) ...
function initializeManager() { /* ... same code ... */ }
const observer = new MutationObserver(/* ... */);
// ... start observer ...
// ... initializeManager() call ...
// ... API Implementation functions (registerTabImpl etc.) ...
// ... Global Exposure logic ...
})();