From 3d8ac64a09fb26c9e2db02385b44c63b4b1bc838 Mon Sep 17 00:00:00 2001 From: "novice.li" Date: Sat, 20 Jan 2024 13:52:45 +0800 Subject: [PATCH] refactor tampermonkey script --- jetbra.js | 203 +++++------------------------------------------------- 1 file changed, 16 insertions(+), 187 deletions(-) diff --git a/jetbra.js b/jetbra.js index a1bca40..45e2628 100644 --- a/jetbra.js +++ b/jetbra.js @@ -3,7 +3,7 @@ // @namespace https://github.com/novice88/jetbra // @version 1.1 // @license MIT -// @description 添加一个按钮,点击获取插件的激活码 +// @description Add a button on the plugin homepage and click to get the plugin activation code // @author novice.li // @match https://plugins.jetbrains.com/plugin/* // @grant GM_setClipboard @@ -14,189 +14,19 @@ // @connect localhost // ==/UserScript== -var elmGetter = function() { - const win = window.unsafeWindow || document.defaultView || window; - const doc = win.document; - const listeners = new WeakMap(); - let mode = 'css'; - let $; - const elProto = win.Element.prototype; - const matches = elProto.matches || - elProto.matchesSelector || - elProto.webkitMatchesSelector || - elProto.mozMatchesSelector || - elProto.oMatchesSelector; - const MutationObs = win.MutationObserver || - win.WebkitMutationObserver || - win.MozMutationObserver; - function addObserver(target, callback) { - const observer = new MutationObs(mutations => { - for (const mutation of mutations) { - if (mutation.type === 'attributes') { - callback(mutation.target); - if (observer.canceled) return; - } - for (const node of mutation.addedNodes) { - if (node instanceof Element) callback(node); - if (observer.canceled) return; - } - } - }); - observer.canceled = false; - observer.observe(target, {childList: true, subtree: true, attributes: true}); - return () => { - observer.canceled = true; - observer.disconnect(); - }; - } - function addFilter(target, filter) { - let listener = listeners.get(target); - if (!listener) { - listener = { - filters: new Set(), - remove: addObserver(target, el => listener.filters.forEach(f => f(el))) - }; - listeners.set(target, listener); + +async function findElementWithRetry(cssSelector) { + const maxAttempts = 50; + for (let attempts = 0; attempts < maxAttempts; attempts++) { + const element = document.querySelector(cssSelector); + if (element) { + return element; } - listener.filters.add(filter); + await new Promise(resolve => setTimeout(resolve, 100)); } - function removeFilter(target, filter) { - const listener = listeners.get(target); - if (!listener) return; - listener.filters.delete(filter); - if (!listener.filters.size) { - listener.remove(); - listeners.delete(target); - } - } - function query(all, selector, parent, includeParent, curMode) { - switch (curMode) { - case 'css': - const checkParent = includeParent && matches.call(parent, selector); - if (all) { - const queryAll = parent.querySelectorAll(selector); - return checkParent ? [parent, ...queryAll] : [...queryAll]; - } - return checkParent ? parent : parent.querySelector(selector); - case 'jquery': - let jNodes = $(includeParent ? parent : []); - jNodes = jNodes.add([...parent.querySelectorAll('*')]).filter(selector); - if (all) return $.map(jNodes, el => $(el)); - return jNodes.length ? $(jNodes.get(0)) : null; - case 'xpath': - const ownerDoc = parent.ownerDocument || parent; - selector += '/self::*'; - if (all) { - const xPathResult = ownerDoc.evaluate(selector, parent, null, 7, null); - const result = []; - for (let i = 0; i < xPathResult.snapshotLength; i++) { - result.push(xPathResult.snapshotItem(i)); - } - return result; - } - return ownerDoc.evaluate(selector, parent, null, 9, null).singleNodeValue; - } - } - function isJquery(jq) { - return jq && jq.fn && typeof jq.fn.jquery === 'string'; - } - function getOne(selector, parent, timeout) { - const curMode = mode; - return new Promise(resolve => { - const node = query(false, selector, parent, false, curMode); - if (node) return resolve(node); - let timer; - const filter = el => { - const node = query(false, selector, el, true, curMode); - if (node) { - removeFilter(parent, filter); - timer && clearTimeout(timer); - resolve(node); - } - }; - addFilter(parent, filter); - if (timeout > 0) { - timer = setTimeout(() => { - removeFilter(parent, filter); - resolve(null); - }, timeout); - } - }); - } - return { - get currentSelector() { - return mode; - }, - get(selector, ...args) { - let parent = typeof args[0] !== 'number' && args.shift() || doc; - if (mode === 'jquery' && parent instanceof $) parent = parent.get(0); - const timeout = args[0] || 0; - if (Array.isArray(selector)) { - return Promise.all(selector.map(s => getOne(s, parent, timeout))); - } - return getOne(selector, parent, timeout); - }, - each(selector, ...args) { - let parent = typeof args[0] !== 'function' && args.shift() || doc; - if (mode === 'jquery' && parent instanceof $) parent = parent.get(0); - const callback = args[0]; - const curMode = mode; - const refs = new WeakSet(); - for (const node of query(true, selector, parent, false, curMode)) { - refs.add(curMode === 'jquery' ? node.get(0) : node); - if (callback(node, false) === false) return; - } - const filter = el => { - for (const node of query(true, selector, el, true, curMode)) { - const _el = curMode === 'jquery' ? node.get(0) : node; - if (refs.has(_el)) break; - refs.add(_el); - if (callback(node, true) === false) { - return removeFilter(parent, filter); - } - } - }; - addFilter(parent, filter); - }, - create(domString, ...args) { - const returnList = typeof args[0] === 'boolean' && args.shift(); - const parent = args[0]; - const template = doc.createElement('template'); - template.innerHTML = domString; - const node = template.content.firstElementChild; - if (!node) return null; - parent ? parent.appendChild(node) : node.remove(); - if (returnList) { - const list = {}; - node.querySelectorAll('[id]').forEach(el => list[el.id] = el); - list[0] = node; - return list; - } - return node; - }, - selector(desc) { - switch (true) { - case isJquery(desc): - $ = desc; - return mode = 'jquery'; - case !desc || typeof desc.toLowerCase !== 'function': - return mode = 'css'; - case desc.toLowerCase() === 'jquery': - for (const jq of [window.jQuery, window.$, win.jQuery, win.$]) { - if (isJquery(jq)) { - $ = jq; - break; - }; - } - return mode = $ ? 'jquery' : 'css'; - case desc.toLowerCase() === 'xpath': - return mode = 'xpath'; - default: - return mode = 'css'; - } - } - }; -}(); + throw new Error(`Element with selector '${cssSelector}' not found after ${maxAttempts} attempts.`); +} + (async function () { 'use strict'; GM_addStyle(` @@ -227,17 +57,16 @@ var elmGetter = function() { let pluginDetail = await fetch('https://plugins.jetbrains.com/api/plugins/' + pluginId).then(r => r.json()); - const parentElement = await elmGetter.get('.plugin-header__controls-panel > div:first-child'); + const parentElement = await findElementWithRetry('.plugin-header__controls-panel > div:first-child'); let newElement = document.createElement('div'); newElement.classList.toggle('wt-col-inline'); - newElement.innerHTML = ``; + newElement.innerHTML = ``; parentElement.appendChild(newElement) - newElement.addEventListener('click', async () => { if (pluginDetail.purchaseInfo === undefined) { - window.alert('此插件不是付费插件'); + window.alert('This plugin is not a paid plugin in the market'); return; } let data = { @@ -268,7 +97,7 @@ var elmGetter = function() { onload: function (response) { let license = JSON.parse(response.responseText).license GM_setClipboard(license, 'text'); - window.alert('激活码已复制到剪切版'); + window.alert('The activation code has been copied to your clipboard'); } }); })