Greasy Fork

Greasy Fork is available in English.

快捷便笺+

快捷便笺,快速记录所需信息

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        快捷便笺+
// @namespace   kjnote Scripts
// @author      Takitooru
// @match       *://*/*
// @version     1.1.0.5
// @description 快捷便笺,快速记录所需信息
// @license     GPL License
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_addValueChangeListener
// @grant       GM_addStyle
// @grant       GM_addElement
// @noframes
// ==/UserScript==
(function() {
	window.addEventListener('load', function() {
		const RndToken = ({
			__TOKEN_LIST__:['ContainerID', 'ContainerCSSOpen'],
			init() {
				this.__TOKEN_LIST__.forEach((name) => (this[name] = randstr(16, false, true)));
				return this;
			}
		}).init();
		const CONST = {
			CSS: {
				outer: replaceText(
					`#{ContainerID}{width:20px;border:1px solid #70ae2d;position:fixed;top:2px;right:0px;z-index:999999999999999999999;background-color:#8bc34a;color:#fff;border-radius:3px;font:initial;opacity:0.5;transition-duration: 0.3s;}
					#{ContainerID}:hover, #{ContainerID}.{ContainerCSSOpen}{opacity:1;}`,
					{'{ContainerID}': RndToken.ContainerID, '{ContainerCSSOpen}': RndToken.ContainerCSSOpen}
				),
				inner: `.kjbtn{display: inline-block;text-align: center;cursor:pointer;user-select:none;}
				.boxhide{display: none;position:fixed;z-index:998;padding:3px;border-radius:3px;}
				.jstoolbar{color:#000;display:block;border-bottom:1px solid #ddd;background-color:#eeeeee;padding:10px 0 10px 0;}
				.slider{display:inline-block;font-size:13px}.sliderinput{vertical-align:middle;}
				.sclose{margin-right: 10px;font-size: 14px;text-align: center;font-weight:bold;color:black;width:20px;float:left;cursor:pointer;}
				.savetxt{width:80px;float:right;background-color:#8bc34a;border-radius:3px;color:#fff;padding:2px;font-size: 14px;text-align: center;cursor:pointer;margin-right:1em;}
				.stextarea{background-color: #eeeeee;margin: 0;padding: 5px 0 0 0;height: 200px;width: 350px;color: #000;font-size: 15px;resize: vertical;outline:none;border: none;scrollbar-width: thin;}.stextarea:focus{border: none;box-shadow: none;}
				.stextarea::-webkit-scrollbar {width: 5px;height: 5px;}.stextarea::-webkit-scrollbar-thumb {border-radius: 3px;-webkit-border-radius: 3px;background-color: #8bc34a;}.stextarea::-webkit-scrollbar-track {background-color: transparent;}`
			},
			HTML: {
				all: `<span class="kjbtn">快捷便笺+</span>
				<div class="boxhide"><div class="jstoolbar"><div class="sclose">X</div>
				<div class="slider">透明度:<input class="sliderinput" type="range" min="30" max="100" step="1" value="100"></div>
				<div class="savetxt">保存为txt</div>
				</div><textarea class="stextarea"></textarea></div>`
			}
		};

		const Newdiv = document.createElement("div");
		Newdiv.id = RndToken.ContainerID;
		stopPropagation(Newdiv, ['click', 'dblclick', 'input', 'change', 'focus', 'blur', 'copy', 'paste', 'contextmenu', 'drag', 'mouseenter', 'mousemove', 'mouseover', 'mouseleave', 'mousedown', 'keydown', 'keyup', 'keypress']);
		const shadow = Newdiv.attachShadow({mode: 'closed'});
		shadow.innerHTML = CONST.HTML.all;
		document.body.appendChild(Newdiv);

		GM_addStyle(CONST.CSS.outer);
		GM_addElement(shadow, 'style', {textContent: CONST.CSS.inner});

		const boxname = $(shadow, ".boxhide");
		const kjbtn = $(shadow, ".kjbtn");
		const close = $(shadow, ".sclose");
		const input = $(shadow, ".stextarea");
		const savetxt = $(shadow, ".savetxt");
		const opacity2t = $(shadow, ".sliderinput");
		kjbtn.addEventListener('click', e => e.preventDefault() || e.stopImmediatePropagation() || [...Newdiv.classList].includes(RndToken.ContainerCSSOpen) ? hide() : show(), {capture: true});
		close.onclick = hide;
		input.oninput = save;
		input.value = GM_getValue('text', '');
		if (typeof GM_addValueChangeListener === 'function') {
			GM_addValueChangeListener('text', function(name, old_value, new_value, remote) {
				remote && (input.value = new_value);
			});
		} else {
			setInterval(function() {
				input.value = GM_getValue('text', '');
			}, 500);
		}
		savetxt.addEventListener("click",function(e) {
			Val2txt();
		});
		opacity2t.addEventListener("input",function(e) {
			o2t(this);
		});

		function show() {
			Newdiv.classList.add(RndToken.ContainerCSSOpen);
			boxname.style.display = "block";
			boxname.style.top = "2px";
			boxname.style.right = (Newdiv.clientWidth + 2) + "px";
		}

		function hide() {
			Newdiv.classList.remove(RndToken.ContainerCSSOpen);
			boxname.style = null;
		}

		function save() {
			GM_getValue('text', '') !== input.value && GM_setValue('text', input.value);
		}

		function Val2txt() {
			const Filename = new Date().getTime();
			const TextContent = input.value;
			const Addele = document.createElement('a');
			Addele.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(TextContent));
			Addele.setAttribute('download', Filename);
			const jset = document.createEvent('MouseEvents');
			jset.initEvent('click', true, true);
			Addele.dispatchEvent(jset);

		}

		function o2t(vv) {
			input.style.opacity = vv.value / 100;
		}

		function stopPropagation(elm, evtName) {
			Array.isArray(evtName) ? evtName.forEach(name => stopPropagation(elm, name)) : elm.addEventListener(evtName, e => e.stopPropagation());
		}
	});

	// Basic functions
	// querySelector
	function $() {
		switch(arguments.length) {
			case 2:
				return arguments[0].querySelector(arguments[1]);
				break;
			default:
				return document.querySelector(arguments[0]);
		}
	}
	// querySelectorAll
	function $All() {
		switch(arguments.length) {
			case 2:
				return arguments[0].querySelectorAll(arguments[1]);
				break;
			default:
				return document.querySelectorAll(arguments[0]);
		}
	}
	// createElement
	function $CrE() {
		switch(arguments.length) {
			case 2:
				return arguments[0].createElement(arguments[1]);
				break;
			default:
				return document.createElement(arguments[0]);
		}
	}

	// Replace model text with no mismatching of replacing replaced text
	// e.g. replaceText('aaaabbbbccccdddd', {'a': 'b', 'b': 'c', 'c': 'd', 'd': 'e'}) === 'bbbbccccddddeeee'
	//      replaceText('abcdAABBAA', {'BB': 'AA', 'AAAAAA': 'This is a trap!'}) === 'abcdAAAAAA'
	//      replaceText('abcd{AAAA}BB}', {'{AAAA}': '{BB', '{BBBB}': 'This is a trap!'}) === 'abcd{BBBB}'
	//      replaceText('abcd', {}) === 'abcd'
	/* Note:
	    replaceText will replace in sort of replacer's iterating sort
	    e.g. currently replaceText('abcdAABBAA', {'BBAA': 'TEXT', 'AABB': 'TEXT'}) === 'abcdAATEXT'
	    but remember: (As MDN Web Doc said,) Although the keys of an ordinary Object are ordered now, this was
	    not always the case, and the order is complex. As a result, it's best not to rely on property order.
	    So, don't expect replaceText will treat replacer key-values in any specific sort. Use replaceText to
	    replace irrelevance replacer keys only.
	*/
	function replaceText(text, replacer) {
		if (Object.entries(replacer).length === 0) {return text;}
		const [models, targets] = Object.entries(replacer);
		const len = models.length;
		let text_arr = [{text: text, replacable: true}];
		for (const [model, target] of Object.entries(replacer)) {
			text_arr = replace(text_arr, model, target);
		}
		return text_arr.map((text_obj) => (text_obj.text)).join('');

		function replace(text_arr, model, target) {
			const result_arr = [];
			for (const text_obj of text_arr) {
				if (text_obj.replacable) {
					const splited = text_obj.text.split(model);
					for (const part of splited) {
						result_arr.push({text: part, replacable: true});
						result_arr.push({text: target, replacable: false});
					}
					result_arr.pop();
				} else {
					result_arr.push(text_obj);
				}
			}
			return result_arr;
		}
	}

	// Append a style text to document(<head>) with a <style> element
    function addStyle(css, id, parent=document.head) {
		const style = document.createElement("style");
		id && (style.id = id);
		style.textContent = css;
		for (const elm of document.querySelectorAll('#'+id)) {
			elm.parentElement && elm.parentElement.removeChild(elm);
		}
        parent.appendChild(style);
    }

	// Returns a random string
	function randstr(length=16, nums=true, cases=true) {
		const all = 'abcdefghijklmnopqrstuvwxyz' + (nums ? '0123456789' : '') + (cases ? 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' : '');
		return Array(length).fill(0).reduce(pre => (pre += all.charAt(randint(0, all.length-1))), '');
	}

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