GMT Tags Helper

Improvements for working with groups of tags + increased efficiency of new requests creation

当前为 2021-07-06 提交的版本,查看 最新版本

// ==UserScript==
// @name         GMT Tags Helper
// @namespace    https://greasyfork.org/users/321857-anakunda
// @version      1.00.0
// @description  Improvements for working with groups of tags + increased efficiency of new requests creation
// @match        https://*/artist.php?id=*
// @match        https://*/artist.php?*&id=*
// @match        https://*/requests.php?action=new&tags=*
// @match        https://*/requests.php?action=view&id=*
// @match        https://*/requests.php?action=view&*&id=*
// @match        https://*/torrents.php?id=*
// @match        https://*/torrents.php?*&id=*
// @match        https://*/collages.php?id=14998
// @match        https://*/collages.php?*&id=14998
// @run-at       document-end
// @author       Anakunda
// @copyright    2021, Anakunda (https://greasyfork.org/users/321857-anakunda)
// @license      GPL-3.0-or-later
// @grant        GM_setClipboard
// @grant        GM_registerMenuCommand
// ==/UserScript==

(function() {
  'use strict';

	const hasStyleSheet = styleSheet => document.styleSheetSets && document.styleSheetSets.contains(styleSheet);
	const isLightTheme = ['postmod', 'shiro', 'layer_cake', 'proton', 'red_light'].some(hasStyleSheet);
	const isDarkTheme = ['kuro', 'minimal', 'red_dark'].some(hasStyleSheet);

	const getTagsFromIterable = iterable => Array.from(iterable).map(tag => tag.textContent.trim())
		.filter(tag => !/^(?:\d{4}s)$/i.test(tag));
	switch (document.location.pathname) {
		case '/artist.php': {
			function tagsClickHandler(evt) {
				if (!evt.altKey) return;
				let tags = getTagsFromIterable(evt.currentTarget.getElementsByTagName('A'));
				if (tags.length > 0) if (evt.ctrlKey)
					document.location.assign('/requests.php?action=new&tags=' + encodeURIComponent(JSON.stringify(tags)));
				else {
					GM_setClipboard(tags.join(', '), 'text');
					evt.currentTarget.style.backgroundColor = isDarkTheme ? 'darkgreen' : 'lightgreen';
					setTimeout(elem => { elem.style.backgroundColor = null }, 1000, evt.currentTarget);
				}
				return false;
			}

			const contextId = 'context-cae67c72-9aa7-4b96-855e-73cb23f5c7f8';
			let menu = document.createElement('menu');
			menu.type = 'context';
			menu.id = contextId;
			const contextUpdater = evt => { menu = evt.currentTarget };

			function addMenuItem(label, callback) {
				if (label) {
					let menuItem = document.createElement('MENUITEM');
					menuItem.label = label;
					if (typeof callback == 'function') menuItem.onclick = callback;
					menu.append(menuItem);
				}
				return menu.children.length;
			}

			addMenuItem('Copy tags to clipboard', function(evt) {
				console.assert(menu instanceof HTMLAnchorElement, 'menu instanceof HTMLAnchorElement')
				if (!(menu instanceof HTMLDivElement)) throw 'Invalid invoker';
				const tags = getTagsFromIterable(menu.getElementsByTagName('A'));
				if (tags.length > 0) GM_setClipboard(tags.join(', '), 'text');
			});
			addMenuItem('Make new request using these tags', function(evt) {
				console.assert(menu instanceof HTMLAnchorElement, 'menu instanceof HTMLAnchorElement')
				if (!(menu instanceof HTMLDivElement)) throw 'Invalid invoker';
				const tags = getTagsFromIterable(menu.getElementsByTagName('A'));
				if (tags.length > 0) document.location.assign('/requests.php?' + new URLSearchParams({
					action: 'new',
					tags: JSON.stringify(tags),
				}).toString());
			});
			document.body.append(menu);

			const elem = document.getElementById('discog_table');
			if (elem != null) elem.ondragover = elem.ondrop = evt => false;
			function dropUrlHandler(evt) {
				if (evt.dataTransfer.items.length <= 0) return false;
				let links = evt.dataTransfer.getData('text/uri-list');
				if (links) links = links.split(/\r?\n/); else {
					links = evt.dataTransfer.getData('text/x-moz-url');
					if (links) links = links.split(/\r?\n/).filter((item, ndx) => ndx % 2 == 0);
						else if (links = evt.dataTransfer.getData('text/plain')) links = links.split(/\r?\n/);
				}
				if (!Array.isArray(links) || links.length <= 0) return false;
				let tags = getTagsFromIterable(evt.currentTarget.getElementsByTagName('A'));
				if (tags.length > 0) document.location.assign('/requests.php?' + new URLSearchParams({
					action: 'new',
					tags: JSON.stringify(tags),
					url: links[0],
				}).toString());
				return false;
			}

			const isFirefox = /\b(?:Firefox)\b/.test(navigator.userAgent) || Boolean(window.InstallTrigger);
			for (let div of document.body.querySelectorAll('div#discog_table > table > tbody > tr.group div.tags')) {
				div.title = 'Alt + click => copy tags to clipboard\n' +
					'Ctrl+Alt + Click => make new request using these tags\n' +
					'----\n--or-- use context menu';
				div.addEventListener('click', tagsClickHandler);
				div.ondrop = dropUrlHandler;
				div.ondragover = evt => true;
				div.ondragenter = evt => { evt.currentTarget.style.backgroundColor = 'lawngreen' };
				div[isFirefox ? 'ondragexit' : 'ondragleave'] = evt => { evt.currentTarget.style.backgroundColor = null };
				div.oncontextmenu = contextUpdater;
				div.setAttribute('contextmenu', contextId);
			}
			break;
		}
		case '/requests.php': {
			const urlParams = new URLSearchParams(document.location.search);
			let tags = urlParams.get('tags');
			if (tags && (tags = JSON.parse(tags)).length > 0) {
				const input = document.getElementById('tags');
				if (input != null) input.value = tags.join(', ');
			}
			const url = urlParams.get('url');
			if (/^(https?:\/\/.+)$/i.test(url)) {
				let ua = document.getElementById('ua-data');
				function feedData() {
					ua.value = url;
					if ((ua = document.getElementById('autofill-form-2')) == null) return; // assertion failed
					if (typeof ua.onclick == 'function') ua.onclick(); else ua.click();
				}
				if (ua != null) feedData(); else {
					const container = document.querySelector('form#request_form > table > tbody');
					if (container != null) {
						let counters = [0, 0], timeStamp = Date.now();
						const mo = new MutationObserver(function(mutationsList) {
							++counters[0];
							for (let mutation of mutationsList) for (let node of mutation.addedNodes) {
								++counters[1];
								if (node.nodeName != 'TR' || (ua = node.querySelector('textarea#ua-data')) == null) continue;
								console.log('Found UA data by trigger:', counters, (Date.now() - timeStamp) / 1000);
								clearTimeout(timer);
								return feedData();
							}
						}), timer = setTimeout(mo => { mo.disconnect() }, 10000, mo);
						mo.observe(container, { childList: true });
					}
				}
			}
			break;
		}
	}

	let tagsBox = document.body.querySelector('div.box_tags');
	if (tagsBox == null) return;
	let head = tagsBox.querySelector('div.head');
	if (head == null) return;
	let span = document.createElement('SPAN'), a = document.createElement('A');
	span.style.float = 'right';
	a.className = 'brackets';
	a.textContent = 'Copy';
	a.href = '#';
	a.onclick = function(evt) {
		let tags = getTagsFromIterable(tagsBox.querySelectorAll('ul > li > a, ol > li > a'));
		if (tags.length <= 0) return false;
		GM_setClipboard(tags.join(', '), 'text');
		evt.currentTarget.style.color = isDarkTheme ? 'darkgreen' : 'lightgreen';
		setTimeout(elem => {elem.style.color = null }, 1000, evt.currentTarget);
		return false;
	};
	span.append(a);
	head.append(span);
})();