mirror of
				https://github.com/chylex/Userscripts.git
				synced 2025-10-31 12:17:15 +01:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			master
			...
			vim-bindin
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 390626f99f | 
| @@ -1,20 +0,0 @@ | |||||||
| // ==UserScript== |  | ||||||
| // @name         Patreon - Click Anywhere to Close Image Dialog |  | ||||||
| // @description  Close modal image dialog by clicking anywhere on the page. |  | ||||||
| // @version      1 |  | ||||||
| // @license      MPL-2.0 |  | ||||||
| // @namespace    https://chylex.com |  | ||||||
| // @homepageURL  https://github.com/chylex/Userscripts |  | ||||||
| // @supportURL   https://github.com/chylex/Userscripts/issues |  | ||||||
| // @include      https://www.patreon.com/* |  | ||||||
| // @run-at       document-idle |  | ||||||
| // @grant        none |  | ||||||
| // ==/UserScript== |  | ||||||
|  |  | ||||||
| document.body.addEventListener("click", e => { |  | ||||||
| 	if (e.target.tagName === "IMG" && e.target.getAttribute("data-tag") === "lightboxImage") { |  | ||||||
| 		document.querySelector("[data-tag='close']")?.click(); |  | ||||||
| 		e.preventDefault(); |  | ||||||
| 		e.stopPropagation(); |  | ||||||
| 	} |  | ||||||
| }); |  | ||||||
							
								
								
									
										20
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								README.md
									
									
									
									
									
								
							| @@ -44,15 +44,6 @@ Before you [report an issue](https://github.com/chylex/Userscripts/issues/new) ( | |||||||
|         </tr> |         </tr> | ||||||
|     </tbody> |     </tbody> | ||||||
| </table> | </table> | ||||||
| <table> |  | ||||||
|     <tbody> |  | ||||||
|         <tr> |  | ||||||
|             <th width="110px" rowspan="1">Patreon</th> |  | ||||||
|             <td width="325px"><a href="#click-anywhere-to-close-image-dialog">Click Anywhere to Close Image Dialog</a></td> |  | ||||||
|             <td>Script</td> |  | ||||||
|         </tr> |  | ||||||
|     </tbody> |  | ||||||
| </table> |  | ||||||
| <table> | <table> | ||||||
|     <tbody> |     <tbody> | ||||||
|         <tr> |         <tr> | ||||||
| @@ -140,17 +131,6 @@ Adds a button to load all pages in the [Crashes](https://openeye.openmods.info/c | |||||||
| \- | \- | ||||||
| \[ [Source code](https://github.com/chylex/Userscripts/blob/master/OpenEye/LoadAllPages.user.js) \] | \[ [Source code](https://github.com/chylex/Userscripts/blob/master/OpenEye/LoadAllPages.user.js) \] | ||||||
|  |  | ||||||
| --- |  | ||||||
| ### Patreon |  | ||||||
|  |  | ||||||
| #### Click Anywhere to Close Image Dialog |  | ||||||
| Close modal image dialog by clicking anywhere on the page.   |  | ||||||
| \[ [Automatically updated](https://github.com/chylex/Userscripts/raw/master/Patreon/ClickAnywhereToCloseImageDialog.user.js) \] |  | ||||||
| \- |  | ||||||
| \[ [Manually updated](https://github.com/chylex/Userscripts/raw/6b2f3698dc7082a57afa82470422f5c3418f1938/Patreon/ClickAnywhereToCloseImageDialog.user.js) \] |  | ||||||
| \- |  | ||||||
| \[ [Source code](https://github.com/chylex/Userscripts/blob/master/Patreon/ClickAnywhereToCloseImageDialog.user.js) \] |  | ||||||
|  |  | ||||||
| --- | --- | ||||||
| ### Reddit | ### Reddit | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										279
									
								
								Vim/VimBindings.user.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										279
									
								
								Vim/VimBindings.user.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,279 @@ | |||||||
|  | // ==UserScript== | ||||||
|  | // @name         Vim Bindings | ||||||
|  | // @description  Reformats the Vim index help page and adds custom notes to each binding. | ||||||
|  | // @author       chylex | ||||||
|  | // @version      1 | ||||||
|  | // @license      MIT | ||||||
|  | // @namespace    https://chylex.com | ||||||
|  | // @homepageURL  https://github.com/chylex/Userscripts | ||||||
|  | // @supportURL   https://github.com/chylex/Userscripts/issues | ||||||
|  | // @include      https://vimhelp.org/index.txt.html | ||||||
|  | // @run-at       document-end | ||||||
|  | // @grant        none | ||||||
|  | // ==/UserScript== | ||||||
|  |  | ||||||
|  | const settings = loadSettings(); | ||||||
|  |  | ||||||
|  | function loadSettings() { | ||||||
|  | 	try { | ||||||
|  | 		const json = localStorage.getItem("vim_binding_settings"); | ||||||
|  | 		return json === null ? {} : JSON.parse(json); | ||||||
|  | 	} catch (e) { | ||||||
|  | 		console.error("Could not load binding settings", e); | ||||||
|  | 		return null; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function saveSettings() { | ||||||
|  | 	localStorage.setItem("vim_binding_settings", JSON.stringify(settings)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | appendElement(document.head, "style").textContent = ` | ||||||
|  | #vh-content { | ||||||
|  |   flex: 1 1 auto; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #vh-content pre { | ||||||
|  |   width: 100%; | ||||||
|  |   margin: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | table { | ||||||
|  |   border-collapse: collapse; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | tr.h { | ||||||
|  |   border-bottom: 2px dashed var(--aqua); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | tr.h td, tr:has(+ tr.subsection) td { | ||||||
|  |   padding-bottom: 12px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | tr.h + tr td, tr.subsection + tr td { | ||||||
|  |   padding-top: 12px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | td { | ||||||
|  |   padding: 2px 8px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | td:first-child { | ||||||
|  |   padding-left: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | td:last-child { | ||||||
|  |   padding-right: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | tr.subsection td { | ||||||
|  |   padding-top: 12px; | ||||||
|  |   padding-bottom: 12px; | ||||||
|  |   border-top: 1px dashed var(--aqua); | ||||||
|  |   border-bottom: 1px dashed var(--aqua); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | tr[data-binding-value="used"], | ||||||
|  | tr[data-binding-value="changed"], | ||||||
|  | tr[data-binding-value="rebound"], | ||||||
|  | tr[data-binding-value="not-used"] { | ||||||
|  |   opacity: 0.35; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | tr.missing { | ||||||
|  |   opacity: 0.35; | ||||||
|  |   text-decoration: line-through; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | select { | ||||||
|  |   border: 0; | ||||||
|  |   padding: 3px 5px; | ||||||
|  |   font-size: 0.82rem; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | select, select option { | ||||||
|  |   font-family: monospace; | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
|  | `; | ||||||
|  |  | ||||||
|  | let headingIndex = 0; | ||||||
|  |  | ||||||
|  | /** @var {Object<string, HTMLSelectElement[]>} */ | ||||||
|  | const selectsByBinding = {}; | ||||||
|  |  | ||||||
|  | for (const headingEle of document.querySelectorAll("span[class='h']:has(+ span[class='h'])")) { | ||||||
|  | 	const delimiterEle = headingEle.nextElementSibling; | ||||||
|  | 	 | ||||||
|  | 	const columnNames = headingEle.textContent.split(/(?<!^)(?:\t+\s*|(?<=note ))(?!$)/).map(name => name.trim()); | ||||||
|  | 	 | ||||||
|  | 	const tableEle = document.createElement("table"); | ||||||
|  | 	addHeadingRow(tableEle, columnNames); | ||||||
|  | 	 | ||||||
|  | 	let currentNode = delimiterEle.nextSibling; | ||||||
|  | 	let html = ""; | ||||||
|  | 	 | ||||||
|  | 	while (currentNode !== null && !isTagWithClass(currentNode, "SPAN", "h")) { | ||||||
|  | 		html += currentNode.nodeType === Node.ELEMENT_NODE ? currentNode.outerHTML : currentNode.textContent; | ||||||
|  | 		 | ||||||
|  | 		const nextNode = currentNode.nextSibling; | ||||||
|  | 		currentNode.remove(); | ||||||
|  | 		currentNode = nextNode; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	for (const line of html.split(/\n(?=<a|\t{1,3}[^\t]|\n)/)) { | ||||||
|  | 		addContentRow(tableEle, line, columnNames); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	headingEle.insertAdjacentElement("beforebegin", tableEle); | ||||||
|  | 	headingEle.remove(); | ||||||
|  | 	delimiterEle.remove(); | ||||||
|  | 	 | ||||||
|  | 	++headingIndex; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function isColumnRightAligned(columnName) { | ||||||
|  | 	return columnName === "note"; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function addHeadingRow(table, columnNames) { | ||||||
|  | 	const tr = appendElement(table, "tr"); | ||||||
|  | 	tr.classList.add("h"); | ||||||
|  | 	 | ||||||
|  | 	appendElement(tr, "td"); | ||||||
|  | 	 | ||||||
|  | 	for (const columnName of columnNames) { | ||||||
|  | 		const td = appendElement(tr, "td"); | ||||||
|  | 		td.innerText = columnName; | ||||||
|  | 		 | ||||||
|  | 		if (isColumnRightAligned(columnName)) { | ||||||
|  | 			td.style.textAlign = "right"; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function addContentRow(table, line, columnNames) { | ||||||
|  | 	if (line.includes("Meta characters (0x80 to 0xff, 128 to 255)")) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	// console.info(line); | ||||||
|  | 	 | ||||||
|  | 	const columnCount = columnNames.length; | ||||||
|  | 	const matches = line.match(/^(?:(<a .+?<\/a>)\s+|\t{2})(.*?)(?:(?:\t+?\s*| {2}\t*|(?<=] )|(?<=<span class="s">{.+?}<\/span> (?!(?:<a .+?class="s">.+?<\/a> )?<span class="s">{.+?}<\/span>)))(?:(\d+(?:,\d+)*)\s+)?(.*))?$/s); | ||||||
|  | 	 | ||||||
|  | 	if (matches === null) { | ||||||
|  | 		addSubsectionRow(table, line, columnCount); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	/** @var {HTMLTableRowElement} */ | ||||||
|  | 	const tr = appendElement(table, "tr"); | ||||||
|  | 	 | ||||||
|  | 	/** @var {HTMLTableCellElement[]} */ | ||||||
|  | 	const tds = [ appendElement(tr, "td") ]; | ||||||
|  | 	 | ||||||
|  | 	for (let matchIndex = 1; matchIndex < matches.length; matchIndex++) { | ||||||
|  | 		let td = tds[tds.length - 1]; | ||||||
|  | 		 | ||||||
|  | 		if (matchIndex <= columnCount) { | ||||||
|  | 			tds.push(td = appendElement(tr, "td")); | ||||||
|  | 			 | ||||||
|  | 			if (isColumnRightAligned(columnNames[matchIndex - 1])) { | ||||||
|  | 				td.style.textAlign = "right"; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		else if (td.innerHTML.length > 0) { | ||||||
|  | 			td.innerHTML += " "; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		const match = matches[matchIndex]; | ||||||
|  | 		if (match !== undefined) { | ||||||
|  | 			td.innerHTML += match | ||||||
|  | 				.replaceAll("\t", "") | ||||||
|  | 				.replace(/\n\s*/g, " ") | ||||||
|  | 				.replace("<MiddleMouse>", "<MiddleMouse>") | ||||||
|  | 				.trim(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	const bindingElements = tds[1].getElementsByClassName("l"); | ||||||
|  | 	 | ||||||
|  | 	if (bindingElements.length === 1) { | ||||||
|  | 		const binding = bindingElements[0].innerText; | ||||||
|  | 		const select = appendElement(tds[0], "select"); | ||||||
|  | 		 | ||||||
|  | 		addOption(select, "TBD", ""); | ||||||
|  | 		addOption(select, "Used", "used"); | ||||||
|  | 		addOption(select, "Changed", "changed"); | ||||||
|  | 		addOption(select, "Rebound", "rebound"); | ||||||
|  | 		addOption(select, "Interested", "interested"); | ||||||
|  | 		addOption(select, "Not Used", "not-used"); | ||||||
|  | 		 | ||||||
|  | 		const loadedValue = settings[binding]; | ||||||
|  | 		select.value = loadedValue ?? ""; | ||||||
|  | 		tr.setAttribute("data-binding-value", loadedValue); | ||||||
|  | 		 | ||||||
|  | 		select.addEventListener("change", _ => { | ||||||
|  | 			const newValue = select.value; | ||||||
|  | 			 | ||||||
|  | 			for (const otherSelect of selectsByBinding[binding]) { | ||||||
|  | 				if (otherSelect !== select) { | ||||||
|  | 					otherSelect.value = newValue; | ||||||
|  | 				} | ||||||
|  | 				 | ||||||
|  | 				otherSelect.closest("tr").setAttribute("data-binding-value", newValue); | ||||||
|  | 			} | ||||||
|  | 			 | ||||||
|  | 			if (!newValue) { | ||||||
|  | 				delete settings[binding]; | ||||||
|  | 			} | ||||||
|  | 			else { | ||||||
|  | 				settings[binding] = newValue; | ||||||
|  | 			} | ||||||
|  | 			 | ||||||
|  | 			saveSettings(); | ||||||
|  | 		}); | ||||||
|  | 		 | ||||||
|  | 		if (!(binding in selectsByBinding)) { | ||||||
|  | 			selectsByBinding[binding] = []; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		selectsByBinding[binding].push(select); | ||||||
|  | 	} | ||||||
|  | 	else { | ||||||
|  | 		tr.classList.add("missing"); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function addSubsectionRow(table, line, columnCount) { | ||||||
|  | 	const text = line.trim(); | ||||||
|  | 	if (text.length === 0) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	/** @var {HTMLTableRowElement} */ | ||||||
|  | 	const tr = appendElement(table, "tr"); | ||||||
|  | 	tr.classList.add("subsection"); | ||||||
|  | 	 | ||||||
|  | 	/** @var {HTMLTableCellElement} */ | ||||||
|  | 	const td = appendElement(tr, "td"); | ||||||
|  | 	td.innerHTML = text; | ||||||
|  | 	td.colSpan = columnCount; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function isTagWithClass(node, tagName, className) { | ||||||
|  | 	return node.nodeType === Node.ELEMENT_NODE && node.tagName === tagName && node.classList.contains(className); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function appendElement(target, tagName) { | ||||||
|  | 	const ele = document.createElement(tagName); | ||||||
|  | 	target.appendChild(ele); | ||||||
|  | 	return ele; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function addOption(select, name, value) { | ||||||
|  | 	const option = appendElement(select, "option"); | ||||||
|  | 	option.innerText = name; | ||||||
|  | 	option.value = value; | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user