// ==UserScript== // @name Prohardver Fórum – Power Tools // @namespace https://github.com/lkristof/userscripts // @version 2.1.1 // @description PH Fórum extra funkciók, fejlécbe épített beállításokkal. // @icon https://cdn.rios.hu/design/ph/logo-favicon.png // // @match https://prohardver.hu/* // @match https://mobilarena.hu/* // @match https://logout.hu/* // @match https://fototrend.hu/* // // @homepageURL https://github.com/lkristof/userscripts // @supportURL https://github.com/lkristof/userscripts/issues // @downloadURL https://raw.githubusercontent.com/lkristof/userscripts/main/ph-power-tools.user.js // @updateURL https://raw.githubusercontent.com/lkristof/userscripts/main/ph-power-tools.user.js // // @grant GM.getValue // @grant GM.setValue // @grant GM.deleteValue // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @grant GM_xmlhttpRequest // @connect kek.sh // @connect api.github.com // @run-at document-idle // ==/UserScript== (async function () { 'use strict'; /************ CONFIG ************/ const KEYS = { SETTINGS: 'ph_forum_settings', STATE: { HIDDEN_USERS: 'ph_hidden_users', HIDE_OFF: 'ph_hide_off', WIDE_VIEW: 'ph_wide_view', THREAD_VIEW: 'ph_thread_view', TOPIC_MAX_ID_MAP: 'ph_topic_max_id_map', }, KEK: { GALLERY: 'ph_kek_gallery', GALLERY_RESET_TS: 'ph_kek_gallery_reset_ts', GALLERY_DELETED: 'ph_kek_gallery_deleted', GALLERY_SEEN_RESET_TS: 'ph_kek_gallery_seen_reset_ts', }, SECRETS: 'ph_power_tools_secrets', }; const SYNC_KEYS = [ KEYS.SETTINGS, KEYS.STATE.HIDDEN_USERS, KEYS.STATE.TOPIC_MAX_ID_MAP, KEYS.KEK.GALLERY, KEYS.KEK.GALLERY_RESET_TS, KEYS.KEK.GALLERY_DELETED, ]; const STORAGE_KEY = KEYS.SETTINGS; const SECRETS_KEY = KEYS.SECRETS; const secrets = await loadSecrets(); const GIST_TOKEN = (secrets.gistToken || "").trim(); const GIST_ID = (secrets.gistId || "").trim(); const DEFAULT_GIST_FILENAME = "ph_forum_settings.json"; const GIST_FILENAME = ((secrets.gistFilename || DEFAULT_GIST_FILENAME)).trim(); const ENABLE_GIST_SYNC = !!(GIST_TOKEN && GIST_ID && GIST_FILENAME); const KEK_SH_API_KEY = (secrets.kekShApiKey || "").trim(); const DEFAULT_COLORIZE_PALETTE = { light: { own: "#C7D7E0", reply: "#CFE0C3", akcio: "#FFC0C0", focusAuthor: "#FFA966", focusReply: "#F6CEAF", chainBg: "#FFF6C8", chainBorder: "#FF9800", hashHighlight: "#FFF6C8", }, dark: { own: "#2F4A57", reply: "#344A3A", akcio: "#8B0000", focusAuthor: "#5B327A", focusReply: "#3A1F4F", chainBg: "#4A4015", chainBorder: "#FFB300", hashHighlight: "#4A4015", } }; const defaultSettings = { colorize: true, linkRedirect: true, msgAnchorHighlight: true, offHider: true, wideView: true, threadView: true, keyboardNavigation: true, hideUsers: true, markNewPosts: true, extraSmilies: true, kekShUploader: true, colorizePalette: DEFAULT_COLORIZE_PALETTE, }; const settingGroups = { appearance: { label: 'Megjelenés', keys: ['colorize', 'markNewPosts', 'wideView', 'threadView'], defaultOpen: true, }, filtering: { label: 'Szűrés', keys: ['offHider', 'hideUsers'], }, interaction: { label: 'Interakció', keys: ['kekShUploader', 'extraSmilies', 'linkRedirect', 'msgAnchorHighlight', 'keyboardNavigation'], } }; const tooltips = { colorize: 'Saját / válasz / #akció + avatar fókusz + hozzászólás-lánc kiemelés. Színek a 🎨 menüben.', linkRedirect: 'PH! lapcsalád linkjeit az aktuális oldalra irányítja.', msgAnchorHighlight: 'URL-ben lévő #msg hozzászólás kiemelése. Színek a 🎨 menüben.', offHider: 'OFF hozzászólások elrejtése/kibontása gombbal.', wideView: 'Szélesebb tartalom, kevesebb oldalsó margó.', threadView: 'Hozzászólás-láncok vizuális összekötése és strukturáltabb megjelenítése.', keyboardNavigation: 'Billentyű navigáció a hozzászólások között\n← első\n→ utolsó\n↑ előző\n↓ következő\nshift + ↑ sorban előző\nshift + ↓ sorban következő', hideUsers: 'Megadhatod, mely felhasználók hozzászólásai legyenek elrejtve.', markNewPosts: 'Az új hozzászólások fejléce kap egy kis jelölést.', extraSmilies: 'Extra emojik/smiliek listája a szerkesztőben.', kekShUploader: 'kek.sh-ra képfeltöltés, API kulcs szükséges.', }; function prettyName(key) { return { colorize: 'Hozzászólások színezése', linkRedirect: 'Link átirányítás', msgAnchorHighlight: 'Üzenet kiemelés', offHider: 'OFF hozzászólások elrejtése', wideView: 'Széles nézet', threadView: 'Thread nézet', keyboardNavigation: 'Billentyűzetes navigáció', hideUsers: 'Felhasználók elrejtése', markNewPosts: 'Új hozzászólás jelölése', extraSmilies: 'Extra smiley-k', kekShUploader: 'kek.sh képfeltöltő', }[key] || key; } const storage = createSyncedStorage(); await storage.init(); function getMergedSettings() { const local = safeJsonParse(storage.getItem(STORAGE_KEY) || '{}', {}); return { ...defaultSettings, ...local }; } const savedSettings = getMergedSettings(); let draftSettings = {...savedSettings}; function hasGM() { return (typeof GM_getValue === "function" && typeof GM_setValue === "function") || (typeof GM === "object" && typeof GM.getValue === "function" && typeof GM.setValue === "function"); } async function gmGet(key, def) { if (typeof GM_getValue === "function") return GM_getValue(key, def); if (typeof GM === "object" && typeof GM.getValue === "function") return await GM.getValue(key, def); return def; } async function gmSet(key, val) { if (typeof GM_setValue === "function") return GM_setValue(key, val); if (typeof GM === "object" && typeof GM.setValue === "function") return await GM.setValue(key, val); } async function gmDel(key) { if (typeof GM_deleteValue === "function") return GM_deleteValue(key); if (typeof GM === "object" && typeof GM.deleteValue === "function") return await GM.deleteValue(key); } function lsLoad() { try { return JSON.parse(localStorage.getItem(SECRETS_KEY) || "{}") || {}; } catch { return {}; } } function lsSave(obj) { localStorage.setItem(SECRETS_KEY, JSON.stringify(obj || {})); } function lsClear() { localStorage.removeItem(SECRETS_KEY); } async function loadSecrets() { if (hasGM()) { const s = await gmGet(SECRETS_KEY, null); if (s && typeof s === "object") return s; const legacy = lsLoad(); if (legacy && Object.keys(legacy).length) { await gmSet(SECRETS_KEY, legacy); return legacy; } return {}; } return lsLoad(); } async function saveSecrets(secrets) { if (hasGM()) return gmSet(SECRETS_KEY, secrets || {}); return lsSave(secrets); } async function clearSecrets() { if (hasGM()) return gmDel(SECRETS_KEY); return lsClear(); } function safeJsonParse(str, fallback) { try { return JSON.parse(str); } catch { return fallback; } } /** * Inject / update a