Greasy Fork is available in English.

Sketchful Scripts Menu

Adds a menu with every Sketchful script from Greasy Fork

// ==UserScript==
// @name        Sketchful Scripts Menu
// @namespace   https://greasyfork.org/users/281093
// @match       *://sketchful.io/
// @grant       GM.xmlHttpRequest
// @version     1.1.2
// @author      Bell
// @license     MIT
// @copyright   2020, Bell
// @connect     greasyfork.org
// @run-at      document-body
// @description Adds a menu with every Sketchful script from Greasy Fork
// ==/UserScript==
/* jshint esversion: 8 */

// style by High
const css = `
	@import url(https://fonts.googleapis.com/css2?family=Londrina+Solid&display=swap);
	.content-wrapper {
		height: 490px
	}

	.dark .content-wrapper * {
		scrollbar-width: thin;
		scrollbar-color: rgba(200, 200, 200, .4) transparent;
	}

	.content-wrapper * {
		scrollbar-width: thin;
		scrollbar-color: rgba(10, 10, 10, .4) transparent;
	}

	.plugin-button-container {
		width: 100%;
		padding: 15px 30px;
		height: inherit;
		overflow-x: hidden;
		overflow-y: scroll
	}

	.dark .plugin-button {
		background-color: rgb(0 0 0 / .15)
	}

	.plugin-button {
		width: 100%;
		margin-bottom: 14px;
		height: 80px;
		background-color: rgb(152 191 216 / 24%);
		overflow: hidden;
		border-radius: 5px
	}

	.script-title-author {
		height: 100%;
		width: 230px;
		float: left
	}

	.script-title {
		width: 100%;
		height: 40px;
		text-align: center;
		float: left;
		line-height: 56px;
		font-size: 22px;
		font-family: 'Londrina Solid', cursive;
		overflow: hidden
	}

	.script-author {
		width: 100%;
		height: 40px;
		text-align: center;
		float: left;
		line-height: 30px
	}

	.script-info {
		padding: 10px 0;
		height: calc(100% - 10px);
		width: calc(100% - 340px);
		float: left;
		overflow: scroll;
		overflow-x: hidden;
		margin-top: 5px
	}

	.script-version {
		height: 100%;
		width: 100px;
		float: left;
		text-align: center;
		line-height: 80px;
		font-weight: 800
	}

	.content-wrapper ::-webkit-scrollbar-thumb {
		background-color: rgba(10, 10, 10, .4);
		border-radius: 4px;
	}

	.content-wrapper ::-webkit-scrollbar {
		width: 6px
	}

	.dark .content-wrapper ::-webkit-scrollbar-thumb {
		background-color: rgba(200, 200, 200, .4);
		border-radius: 4px
	}
`;

const scriptContainer = document.createElement('div');
const greasyForkURL = 'https://greasyfork.org/en/scripts/by-site/sketchful.io';

(function init() {
	addCSS(css);
	addMenuButton();
	addContainer();
	addScripts();
})();

async function addScripts() {
	let page = 1;
	do {
		await getScripts(`${greasyForkURL}?page=${page}`);
	} while (scriptContainer.childElementCount === 50 * page++);
}

function addMenuButton() {
	const menu = document.querySelector('#menu > div.menuNav > ul');
	const button = menu.lastChild.cloneNode(true);
	const linkTag = button.querySelector('a');
	const linkSpan = linkTag.lastChild;

	linkTag.href = '#menuScripts';
	linkSpan.textContent = 'Scripts';
	linkTag.firstChild.remove();

	const linkImage = document.createElement('img');
	linkImage.style.marginRight = '7px';
	linkImage.src = 'data:image/gif;base64,R0lGODlhQABAAJEDAGWAkVBXZAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkRCMjA3NDI4RTlFMjExRUFBREIwQzY3MEQyOUI1Njg0IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkRCMjA3NDI5RTlFMjExRUFBREIwQzY3MEQyOUI1Njg0Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6REIyMDc0MjZFOUUyMTFFQUFEQjBDNjcwRDI5QjU2ODQiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6REIyMDc0MjdFOUUyMTFFQUFEQjBDNjcwRDI5QjU2ODQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQJGQADACwAAAAAQABAAAAC/5yPqcvtD6OctNqLs968+w+GokCWYlem6mmpZRC4Ahu58I2vdKMC/u/DxUy7RQqIDN6IxYTsCMzNms4nVDmkUkq/5VQ7IWFJ4K1g/C1DxFL1gVkFtN2DlOI1d7veeC+9fgZABkgilEUnJmhjOKgXOFRoCAOnxSUHKTnZCGaZKUnZZHnp6ZVGJTpKCqmGmuoJunOVFCVlWtQzS1taZiUrtMnZG5gKi3hV+2ckipx8l0gM3EzYtSuNsFxtbYCtadvMfai9/Zwtzl2cfB4tre79Td4tzjdcrt0uP06viZ9PjZ4O798fbHbwEVzHLqC7hPQQWrMk8F1BftcmUryIMaPGjQP8CgAAIfkECRkAAwAsAAAAAEAAQAAAAv+cj6nL7Q+jnLTai7PevDsBhqLXieZJYmfAsqeQUidAA+09xpBZ9/YdCOkeL4GvBgQNP8XQsSVcRoo9qFJaESGt2Ato64J1Ld9feEw2moNX9CGacCbb7kEOEQKe6wZUH6S3x/dilxdIh+YE8BIo6FZmY9LomKi2NklZCYYZJlYHyTnnWWkZioPYBWn2pAc3pnjkczj6qloD24oq1cRDk0uLxavlK8rXMHzJBmz8B1rMvAB76Qr9pvpcjYd7ugyNPJcd7dwZrrBNXq5datVdfa6crr5JXf5OH26vm53f7j3OFk/elnv4rsEL2GzdnXjfDiIspLDfPlX60sGqaLEMxowPBB8ufAgypMiRJEuafFAAADs=';
	linkTag.insertBefore(linkImage, linkSpan);
	menu.appendChild(button);
}

function addContainer() {
	const contentWrapper = document.createElement('div');
	const menuScripts = document.createElement('div');

	menuScripts.id = 'menuScripts';
	contentWrapper.classList.add('content-wrapper');
	scriptContainer.classList.add('plugin-button-container');

	contentWrapper.appendChild(scriptContainer);
	menuScripts.appendChild(contentWrapper);
	document.querySelector('.menuTabs').appendChild(menuScripts);
}

async function getScripts(url) {
	const html = await getHTML(url);
	const scriptList = html.querySelector('.script-list');
	scriptList.childNodes.forEach(addScript);
}

function addScript(script) {
	const scriptObj = getScriptData(script);
	if (!scriptObj) return;

	const {
		scriptName,
		scriptDescription,
		scriptVersion,
		scriptLink,
		authorLink,
		authorNames
	} = scriptObj;

	const scriptDiv = document.createElement('div');
	scriptDiv.classList.add('plugin-button');
	scriptDiv.innerHTML = `
		<div class='script-title-author'>
			<a class='script-title' href="${scriptLink}" target="_blank">${scriptName}</a>
			<div class='script-author'>By: <a href="${authorLink}" target="_blank"><b>${authorNames}</b></a></div>
		</div>
		<div class='script-info'>${scriptDescription}</div>
		<div class='script-version'>v${scriptVersion}</div>
	`;
	scriptContainer.appendChild(scriptDiv);
}

function getScriptData(script) {
	const scriptData = script.dataset;
	if (!scriptData || !scriptData.hasOwnProperty('scriptId')) {return null;}

	const descriptionSpan = script.querySelector('.description');
	const authorsObject = JSON.parse(scriptData.scriptAuthors);

	scriptData.scriptDescription = descriptionSpan.textContent.trim();
	scriptData.scriptLink = `https://greasyfork.org/en/scripts/${scriptData.scriptId}`;
	scriptData.authorLink = `https://greasyfork.org/en/users/${Object.keys(authorsObject)[0]}`;
	scriptData.authorNames = Object.values(authorsObject).join(', ');

	return scriptData;
}

function getHTML(url) {
	return new Promise((resolve, reject) => {
		GM.xmlHttpRequest({
			method: 'GET',
			url: url,
			onload: (res) => {
				const html = document.createElement('html');
				html.innerHTML = res.responseText;
				resolve(html);
			},
			onerror: reject
		});
	});
}

function addCSS(style) {
	const stylesheet = document.createElement('style');
	stylesheet.type = 'text/css';
	stylesheet.innerText = style;
	document.head.appendChild(stylesheet);
}