Greasyfork 快捷编辑收藏

在GF脚本页添加快速打开收藏集编辑页面功能

Verzia zo dňa 02.11.2023. Pozri najnovšiu verziu.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

/* eslint-disable no-multi-spaces */

// ==UserScript==
// @name               Greasyfork 快捷编辑收藏
// @name:zh-CN         Greasyfork 快捷编辑收藏
// @name:zh-TW         Greasyfork 快捷編輯收藏
// @name:en            Greasyfork script-set-edit button
// @name:en-US         Greasyfork script-set-edit button
// @namespace          Greasyfork-Favorite
// @version            0.1.7
// @description        在GF脚本页添加快速打开收藏集编辑页面功能
// @description:zh-CN  在GF脚本页添加快速打开收藏集编辑页面功能
// @description:zh-TW  在GF腳本頁添加快速打開收藏集編輯頁面功能
// @description:en     Add open script-set-edit-page button in GF script page
// @description:en-US  Add open script-set-edit-page button in GF script page
// @author             PY-DNG
// @license            GPL-3
// @match              http*://*.greasyfork.org/*
// @match              http*://*.sleazyfork.org/*
// @require            https://greasyfork.org/scripts/456034-basic-functions-for-userscripts/code/script.js?version=1226884
// @icon               data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAbBJREFUOE+Vk7GKGlEUhr8pAiKKDlqpCDpLUCzWBxCENBa+hBsL9wHsLWxXG4tNtcGH0MIiWopY7JSGEUWsbESwUDMw4Z7siLsZDbnlPff/7n/+e67G38sA6sAXIPVWXgA/gCdgfinRPuhfCoXCw3Q65XA4eLBl6zvw1S2eAZqmvTqOc5/NZhkMBqRSKWzbvgYxgbwquoAX4MGyLHK5HIlEgtFo9C+IOFEAo1gsWsvlUmyPx2MymYxAhsMh6XT6lpM7BXjWdf1xNpuRz+fl8GQywTAMGo0G1WpVnJxOJ692vinADPgcDAaZz+cCOR6PmKZJPB4XUb/fp1wuewF+KoBCf1JVBVE5dDodms3mWdDtdqlUKl6AX+8ALmS9XgtM0/5kvNlspKX9fv8RIgBp4bISCoXo9XqsVitKpRK6rrPb7STQ7XZ7eVRaeAYerz14OBxGOfL7/eIgmUwKzHEcJZEQ1eha1wBqPxqNihufzyeQWCzmtiPPqJYM0jWIyiISibBYLAgEAtTrdVqt1nmQXN0rcH/LicqmVqvRbrdN27bfjbKru+nk7ZD3Z7q4+b++82/YPKIrXsKZ3AAAAABJRU5ErkJggg==
// @grant              GM_xmlhttpRequest
// @grant              GM_setValue
// @grant              GM_getValue
// ==/UserScript==

/* global LogLevel DoLog Err $ $All $CrE $AEL $$CrE addStyle detectDom destroyEvent copyProp copyProps parseArgs escJsStr replaceText getUrlArgv dl_browser dl_GM AsyncManager */

(function __MAIN__() {
    'use strict';

	const CONST = {
		Text: {
			'zh-CN': {
				FavEdit: '收藏集:',
				Add: '加入此集',
				Edit: '手动编辑',
				CopySID: '复制脚本ID',
				Working: ['正在添加...', '就快好了...'],
				Error: {
					Unknown: '未知错误'
				}
			},
			'zh-TW': {
				FavEdit: '收藏集:',
				Add: '加入此集',
				Edit: '手動編輯',
				CopySID: '複製腳本ID',
				Working: ['正在添加...', '就快好了...'],
				Error: {
					Unknown: '未知錯誤'
				}
			},
			'en': {
				FavEdit: 'Add to/Remove from favorite list: ',
				Add: 'Add',
				Edit: 'Edit Manually',
				CopySID: 'Copy-Script-ID',
				Working: ['Working...', 'Just a moment...'],
				Error: {
					Unknown: 'Unknown Error'
				}
			},
			'default': {
				FavEdit: 'Add to/Remove from favorite list: ',
				Add: 'Add',
				Edit: 'Edit Manually',
				CopySID: 'Copy-Script-ID',
				Working: ['Working...', 'Just a moment...'],
				Error: {
					Unknown: 'Unknown Error'
				}
			},
		}
	}

	// Get i18n code
	let i18n = navigator.language;
	if (!Object.keys(CONST.Text).includes(i18n)) {i18n = 'default';}

	main()
	function main() {
		const HOST = getHost();
		const API = getAPI();

		// Common actions
		commons();

		// API-based actions
		switch(API[1]) {
			case "scripts":
				API[2] && centerScript(API);
				break;
			default:
				DoLog('API is {}'.replace('{}', API));
		}
	}

	function centerScript(API) {
		switch(API[3]) {
			case undefined:
				pageScript();
				break;
			case 'code':
				pageCode();
				break;
			case 'feedback':
				pageFeedback();
				break;
		}
	}

	function commons() {
		// Your common actions here...
	}

	function pageScript() {
		addFavPanel();
	}

	function pageCode() {
		addFavPanel();
	}

	function pageFeedback() {
		addFavPanel();
	}

	function addFavPanel() {
		if (!getUserpage()) {return false;}
		GUI();

		function GUI() {
			// Get elements
			const script_after = $('#script-feedback-suggestion+*') || $('#new-script-discussion');
			const script_parent = script_after.parentElement;

			// My elements
			const script_favorite = $CrE('div');
			script_favorite.id = 'script-favorite';
			script_favorite.style.margin = '0.75em 0';
			script_favorite.innerHTML = CONST.Text[i18n].FavEdit;

			const favorite_groups = $CrE('select');
			favorite_groups.id = 'favorite-groups';

			const stored_sets = GM_getValue('script-sets', {sets: []}).sets;
			for (const set of stored_sets) {
				// Make <option>
				const option = $CrE('option');
				option.innerText = set.name;
				option.value = set.linkedit;
				$APD(favorite_groups, option);
			}
			adjustWidth();

			getScriptSets(function(sets) {
				clearChildnodes(favorite_groups);
				for (const set of sets) {
					// Make <option>
					const option = $CrE('option');
					option.innerText = set.name;
					option.value = set.linkedit;
					$APD(favorite_groups, option);
				}
				adjustWidth();

				// Set edit-button.href
				favorite_edit.href = favorite_groups.value;
			})
			favorite_groups.addEventListener('change', function(e) {
				favorite_edit.href = favorite_groups.value;
			});

			const favorite_add = $CrE('a');
			favorite_add.id = 'favorite-add';
			favorite_add.innerHTML = CONST.Text[i18n].Add;
			favorite_add.style.margin = favorite_add.style.margin = '0px 0.5em';
			favorite_add.href = 'javascript:void(0);'
			favorite_add.addEventListener('click', function(e) {
				addFav();
			});

			const favorite_edit = $CrE('a');
			favorite_edit.id = 'favorite-edit';
			favorite_edit.innerHTML = CONST.Text[i18n].Edit;
			favorite_edit.style.margin = favorite_edit.style.margin = '0px 0.5em';
			favorite_edit.target = '_blank';

			const favorite_copy = $CrE('a');
			favorite_copy.id = 'favorite-copy';
			favorite_copy.href = 'javascript: void(0);';
			favorite_copy.innerHTML = CONST.Text[i18n].CopySID;
			favorite_copy.addEventListener('click', function() {
				copyText(getStrSID());
			});

			// Append to document
			$APD(script_favorite, favorite_groups);
			script_parent.insertBefore(script_favorite, script_after);
			$APD(script_favorite, favorite_add);
			$APD(script_favorite, favorite_edit);
			$APD(script_favorite, favorite_copy);

			function adjustWidth() {
				favorite_groups.style.width = Math.max.apply(null, Array.from(favorite_groups.children).map((o) => (o.innerText.length))).toString() + 'em';
				favorite_groups.style.maxWidth = '40vw';
			}

			function addFav() {
				const iframe = $CrE('iframe');
				iframe.style.width = iframe.style.height = iframe.style.border = '0';
				iframe.addEventListener('load', edit_onload, {once: true});
				iframe.src = favorite_groups.value;
				$APD(document.body, iframe);
				displayNotice(CONST.Text[i18n].Working[0]);

				function edit_onload() {
					const oDom = iframe.contentDocument;
					const input = $CrE('input');
					input.value = getStrSID();
					input.name = 'scripts-included[]';
					input.type = 'hidden';
					$APD($(oDom, '#script-set-scripts'), input);
					$(oDom, 'button[name="save"]').click();
					iframe.addEventListener('load', finish_onload, {once: true});
					displayNotice(CONST.Text[i18n].Working[1]);
				}

				function finish_onload() {
					const status = $(iframe.contentDocument, 'p.notice');
					const status_text = status ? status.innerText : CONST.Text[i18n].Error.Unknown;
					displayNotice(status_text);
					iframe.parentElement.removeChild(iframe);
				}

				function displayNotice(text) {
					const notice = $CrE('p');
					notice.classList.add('notice');
					notice.id = 'fav-notice';
					notice.innerText = text;
					const old_notice = $('#fav-notice');
					old_notice && old_notice.parentElement.removeChild(old_notice);
					$('#script-content').insertAdjacentElement('afterbegin', notice);
				}
			}
		}
	}

	function getScriptSets(callback, args=[]) {
		const userpage = getUserpage();
		getDocument(userpage, function(oDom) {
			/*
			const user_script_sets = oDom.querySelector('#user-script-sets');
			const script_sets = [];

			for (const li of user_script_sets.querySelectorAll('li')) {
				// Get fav info
				const name = li.childNodes[0].nodeValue.trimRight();
				const link = li.children[0].href;
				const linkedit = li.children[1] ? li.children[1].href : 'https://greasyfork.org/' + $('#language-selector-locale').value + '/users/' + $('#nav-user-info>.user-profile-link>a').href.match(/[a-zA-Z\-]+\/users\/([^\/]*)/)[1] + '/sets/' + li.children[0].href.match(/[\?&]set=(\d+)/)[1] + '/edit';

				// Append to script_sets
				script_sets.push({
					name: name,
					link: link,
					linkedit: linkedit
				});
			}
			*/
			const script_sets = Array.from($(oDom, 'ul#user-script-sets').children).map(li => ({
				name: li.children[0].innerText,
				link: li.children[0].href,
				linkedit: li.children[1].href
			}));

			// Save to GM_storage
			GM_setValue('script-sets', {
				sets: script_sets,
				time: (new Date()).getTime(),
				version: '0.2'
			});

			// callback
			callback.apply(null, [script_sets].concat(args));
		});
	}

	function getUserpage() {
		const a = $('#nav-user-info>.user-profile-link>a');
		return a ? a.href : null;
	}

	function getStrSID(url=location.href) {
		const API = getAPI(url);
		const strSID = API[2].match(/\d+/);
		return strSID;
	}

	function getSID(url=location.href) {
		return Number(getStrSID(url));
	}
	// Basic functions
	function $APD(a,b) {return a.appendChild(b);}

	// Remove all childnodes from an element
	function clearChildnodes(element) {
		const cns = []
		for (const cn of element.childNodes) {
			cns.push(cn);
		}
		for (const cn of cns) {
			element.removeChild(cn);
		}
	}

	// Download and parse a url page into a html document(dom).
    // when xhr onload: callback.apply([dom, args])
    function getDocument(url, callback, args=[]) {
        GM_xmlhttpRequest({
            method       : 'GET',
            url          : url,
            responseType : 'blob',
			onloadstart  : function() {
				DoLog(LogLevel.Info, 'getting document, url=\'' + url + '\'');
			},
            onload       : function(response) {
                const htmlblob = response.response;
				parseDocument(htmlblob, callback, args);
            }
        })
    }

	function parseDocument(htmlblob, callback, args=[]) {
		const reader = new FileReader();
		reader.onload = function(e) {
			const htmlText = reader.result;
			const dom = new DOMParser().parseFromString(htmlText, 'text/html');
			args = [dom].concat(args);
			callback.apply(null, args);
			//callback(dom, htmlText);
		}
		reader.readAsText(htmlblob, document.characterSet);
	}

	// Copy text to clipboard (needs to be called in an user event)
    function copyText(text) {
        // Create a new textarea for copying
        const newInput = document.createElement('textarea');
        document.body.appendChild(newInput);
        newInput.value = text;
        newInput.select();
        document.execCommand('copy');
        document.body.removeChild(newInput);
    }

	// get '/' splited API array from a url
	function getAPI(url=location.href) {
		return url.replace(/https?:\/\/(.*?\.){1,2}.*?\//, '').replace(/\?.*/, '').match(/[^\/]+?(?=(\/|$))/g);
	}

	// get host part from a url(includes '^https://', '/$')
	function getHost(url=location.href) {
		const match = location.href.match(/https?:\/\/[^\/]+\//);
		return match ? match[0] : match;
	}

	function randint(min, max) {
		return Math.floor(Math.random() * (max - min + 1)) + min;
	}
})();