您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Menu module for KameSame Open Framework
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://updategreasyfork.deno.dev/scripts/451522/1111222/KameSame%20Open%20Framework%20-%20Menu%20module.js
"use strict"; // ==UserScript== // @name KameSame Open Framework - Menu module // @namespace timberpile // @description Menu module for KameSame Open Framework // @version 0.1.0.1 // @copyright 2022+, Robin Findley, Timberpile // @license MIT; http://opensource.org/licenses/MIT // ==/UserScript== var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; (async (global) => { var _MenuUi_instances, _MenuUi_install_style, _MenuUi_install_menu; const ksof = global.ksof; await ksof.ready('document'); class MenuUi { constructor() { _MenuUi_instances.add(this); // if (this.style) { // this.style.remove() // } this.style = __classPrivateFieldGet(this, _MenuUi_instances, "m", _MenuUi_install_style).call(this); // if (this.menu) { // this.menu.remove() // } try { this.menu = __classPrivateFieldGet(this, _MenuUi_instances, "m", _MenuUi_install_menu).call(this); } catch (error) { throw new Error(`Can't install ksof menu: ${error}`); } // this.style = undefined as unknown as HTMLStyleElement // this.menu = undefined as unknown as HTMLDivElement // // this.submenus = new Map() this.configs = []; // const setup = () => { // if (this.style) { // this.style.remove() // } // this.style = this.#install_style() // if (this.menu) { // this.menu.remove() // } // try { // this.menu = this.#install_menu() // } catch (error) { // throw new Error(`Can't install ksof menu: ${error}`) // } // } ksof.on('ksof.page_changed', () => { // setup() for (const config of this.configs) { insert_script_link(config); } }); // setup() } get header() { return this.dropdown_menu?.querySelector(':scope > li.scripts-header'); } get scripts_icon() { if (ksof.pageInfo.on == 'review') { return this.menu.querySelector(':scope > a.scripts-icon'); } else { // TODO use fitting selector for top menu bar return this.menu.querySelector(':scope > a.scripts-icon'); } } get dropdown_menu() { return this.menu.querySelector('ul.dropdown-menu'); } get_submenu(name) { const safe_name = escape_attr(name); // return this.submenus.get(safe_name) return document.querySelector('.scripts-submenu[name="' + safe_name + '"]'); } //------------------------------ // Install Submenu, if not present. //------------------------------ install_scripts_submenu(name) { // Abort if already installed. const sub = this.get_submenu(name); if (sub) { return sub; } const safe_name = escape_attr(name); const safe_text = escape_text(name); this.header.insertAdjacentHTML('afterend', `<li class="scripts-submenu" name="${safe_name}"> <a href="#">${safe_text}</a> <ul class="dropdown-menu"></ul> </li>`); const submenu = document.querySelector(`.scripts-submenu[name="${safe_name}"]`); // const link_element = document.createElement('a') // link_element.href = '#' // link_element.innerText = safe_text // const dropdown_menu = document.createElement('ul') // dropdown_menu.className = 'dropdown-menu' // const submenu = document.createElement('li') // submenu.setAttribute('name', safe_name) // submenu.appendChild(link_element) // submenu.appendChild(dropdown_menu) // this.dropdown_menu.appendChild(submenu) if (!submenu) { return undefined; } const menu_contents = this.dropdown_menu.querySelectorAll(':scope > .scripts-submenu, :scope > .script-link'); if (!menu_contents) return undefined; for (const node of Array.from(menu_contents).sort(sort_name)) { // TODO why append again without removing first? node.parentNode?.append(node); } return submenu; } } _MenuUi_instances = new WeakSet(), _MenuUi_install_style = function _MenuUi_install_style() { const style = document.head.querySelector('style[name="scripts_submenu"]'); if (style) { return style; } document.head.insertAdjacentHTML('beforeend', `<style name="scripts_submenu"> #scripts-menu {text-shadow:none;} #scripts-menu.scripts-menu-icon {display:inline-block;} #scripts-menu .scripts-icon {display:inline-block; cursor: pointer; font-size: 1.2em; margin-right: auto; opacity: .65; position: relative; top: 3px;} #scripts-menu:not(.open) > .dropdown-menu {display:none;} #scripts-menu .scripts-submenu:not(.open) > .dropdown-menu {display:none;} #scripts-menu ul.dropdown-menu {position:absolute; background-color:#eee; margin:0; padding:5px 0; list-style-type:none; border:1px solid #333; display:block;} #scripts-menu ul.dropdown-menu > li {text-align:left; color:#333; white-space:nowrap; line-height:20px; padding:3px 0; display:list-item;} #scripts-menu ul.dropdown-menu > li.scripts-header {text-transform:uppercase; font-size:.8rem; font-weight:bold; padding:3px 12px; display:list-item;} #scripts-menu ul.dropdown-menu > li:hover:not(.scripts-header) {background-color:rgba(0,0,0,0.15)} #scripts-menu ul.dropdown-menu a {padding:3px 20px; color:#333; opacity:1;} #scripts-menu .scripts-submenu {position:relative; font-size: 1rem;} #scripts-menu .scripts-submenu > a:after {content:">"; font-family:"FontAwesome"; position:absolute; top:0; right:0; padding:3px 4px 3px 0;} #scripts-menu .scripts-submenu .dropdown-menu {left:100%; top:-6px;} #app.kamesame nav li #scripts-menu { display: flex; flex-direction: column; height: 100%; width: 100%; color: var(--gray); } </style>`); return document.head.querySelector('style[name="scripts_submenu"]'); }, _MenuUi_install_menu = function _MenuUi_install_menu() { // Throws Error let menu = document.querySelector('#scripts-menu'); if (menu) { return menu; } const page = ksof.pageInfo.on; // Abort if on unsupported page // if (!page) throw new Error('Unsupported page') // Install html. if (page == 'review') { const exit_button = document.querySelector('.header a.exit'); if (!exit_button) throw new Error('Exit button not found'); exit_button.insertAdjacentHTML('afterend', ` <div id="scripts-menu" class="scripts-menu-icon"> <a class="scripts-icon state" href="#"><i title="Script Menu">⚙️</i></a> <ul class="dropdown-menu"> <li class="scripts-header">Script Menu</li> </ul> </div>`); } else { const search_icon = find_search_icon(); if (!search_icon) throw new Error('Search icon not found'); search_icon.parentElement?.insertAdjacentHTML('afterend', ` <li> <div id="scripts-menu" class="scripts-menu-icon"> <a class="scripts-icon" href="#"> <div class="icon"> <div>⚙</div> </div> <div class="label"> <span>Scripts Menu</span> </div> </a> <ul class="dropdown-menu"> <li class="scripts-header label">Scripts Menu</li> </ul> </div> </li>`); } menu = document.querySelector('#scripts-menu'); if (!menu) { throw new Error('Menu not found after insertion'); } this.menu = menu; this.scripts_icon.addEventListener('click', (e) => { this.menu.classList.toggle('open'); if (this.menu.classList.contains('open')) document.body.addEventListener('click', body_click); e.stopPropagation(); }); // Click to open/close sub-menu. this.menu.addEventListener('click', submenu_click); function submenu_click(e) { const target = e.target; if (!target.matches('.scripts-submenu>a')) return false; const link = target.parentElement; if (!link) return false; if (!link.parentElement) return false; for (const submenu of link.parentElement.querySelectorAll('.scripts-submenu.open')) { if (submenu !== link) submenu.classList.remove('open'); } if (ksof.pageInfo.on === null) { const menu = document.querySelector('#sitemap__account,[id="#sitemap__account"]'); const submenu = link.querySelector('.dropdown-menu'); if (menu && submenu) { submenu.style.fontSize = '12px'; submenu.style.maxHeight = ''; let top = Math.max(0, link.offsetTop); link.classList.toggle('open'); if (link.classList.contains('open')) { submenu.style.top = top + 'px'; if (menu.offsetHeight - top < submenu.offsetHeight) { top = Math.max(0, menu.offsetHeight - submenu.offsetHeight); submenu.style.top = top + 'px'; submenu.style.maxHeight = String(menu.offsetHeight - top); } } } } else { link.classList.toggle('open'); } // If we opened the menu, listen for off-menu clicks. if (link.classList.contains('open')) { document.body.addEventListener('click', body_click); } else { document.body.removeEventListener('click', body_click); } e.stopPropagation(); } return menu; }; ksof.Menu = { insert_script_link: insert_script_link, ui: new MenuUi() }; const ui = ksof.Menu.ui; ui.menu.setAttribute('display', 'none'); function escape_attr(attr) { return attr.replace(/"/g, '\''); } function escape_text(text) { return text.replace(/[<&>]/g, (ch) => { switch (ch) { case '<': return '<'; case '>': return '>'; case '&': return '&'; default: return ch; } }); } function find_search_icon() { const text_div = Array.from(document.querySelectorAll('#nav_container .real ul li a div.icon div')) .find(el => el.textContent === '🔍'); if (!text_div) { return undefined; } return text_div.parentElement?.parentElement; } //------------------------------ // Handler that closes menus when clicking outside of menu. //------------------------------ function body_click() { ui.menu.classList.remove('open'); for (const submenu of document.querySelectorAll('.scripts-submenu.open')) { submenu.classList.remove('open'); } document.body.removeEventListener('click', body_click); } //------------------------------ // Sort menu items //------------------------------ function sort_name(a, b) { const a1 = a.querySelector('a'); if (!a1) return -1; const b1 = b.querySelector('a'); if (!b1) return -1; return a1.innerText.localeCompare(b1.innerText); } //------------------------------ // Inserts script link into Kamesame menu. //------------------------------ function insert_script_link(config) { // Abort if the script already exists const link_id = config.name + '_script_link'; const link_text = escape_text(config.title); if (document.querySelector('#' + link_id)) return; if (ui.menu.hasAttribute('display')) { ui.menu.removeAttribute('display'); } let classes; const scripts_header = ui.header; if (!scripts_header) return; const link = document.createElement('li'); link.id = link_id; link.setAttribute('name', config.name); link.innerHTML = '<a href="#">' + link_text + '</a>'; if (config.submenu) { const submenu = ui.install_scripts_submenu(config.submenu); if (!submenu) { return; } // Append the script, and sort the menu. const menu = submenu.querySelector('.dropdown-menu'); if (!menu) { return; } classes = ['sitemap__page']; if (config.class) classes.push(config.class_html || ''); link.setAttribute('class', classes.join(' ')); link.innerHTML = '<a href="#">' + link_text + '</a>'; menu.append(link); } else { classes = ['sitemap__page', 'script-link']; if (config.class) classes.push(config.class_html || ''); link.setAttribute('class', classes.join(' ')); if (ksof.pageInfo.on == 'review') { scripts_header.after(link); } else { scripts_header.append(link); } } const menu_contents = scripts_header.parentElement?.querySelectorAll(':scope > .scripts-submenu, :scope > .script-link'); if (menu_contents) { for (const node of Array.from(menu_contents).sort(sort_name)) { node.parentNode?.append(node); } } // Add a callback for when the link is clicked. document.querySelector('#' + link_id)?.addEventListener('click', function (e) { document.body.removeEventListener('click', body_click); document.querySelector('#scripts-menu')?.classList.remove('open'); for (const submenu of document.querySelectorAll('.scripts-submenu')) { submenu.classList.remove('open'); } const temp = document.querySelector('#sitemap__account,[id="#sitemap__account"]'); if (temp) { const temp2 = temp.parentElement?.querySelector('[data-expandable-navigation-target],[data-navigation-section-toggle]'); temp2.click(); const nav_toggle = document.querySelector('.navigation__toggle'); if (nav_toggle.offsetWidth > 0 || nav_toggle.offsetWidth > 0) nav_toggle.click(); } config.on_click(e); return false; }); } // Delay guarantees include() callbacks are called before ready() callbacks. setTimeout(() => { ksof.set_state('ksof.Menu', 'ready'); }, 0); })(window);