userscripts/Elements with ID lister.use...

228 lines
5.8 KiB
JavaScript

// ==UserScript==
// @name Elements with ID lister
// @namespace blankie-scripts
// @match *://*/*
// @grant GM_registerMenuCommand
// @grant GM_getResourceURL
// @require https://cdn.jsdelivr.net/npm/dialog-polyfill@0.5.6/dist/dialog-polyfill.min.js#sha256-cec1a2e320aab77e28bad4ad6bc5e532a6ef5757345c19bb5158aa880b7162a6
// @resource dialogPolyfillCSS https://cdn.jsdelivr.net/npm/dialog-polyfill@0.5.6/dist/dialog-polyfill.min.css#sha256-4dcb3ab62e545f30bf06a4824c253641ee889ca85ca28d5447590557922496ab
// @version 1.0.11
// @author blankie
// @description A userscript that adds a "Show elements popup" option to the Monkey Menu which lists all elements with an ID
// @inject-into content
// ==/UserScript==
"use strict";
const DIALOG_WRAPPER_ID = "element_lister-dialog_wrapper";
const ACCENT_COLOR = "#962AC3";
const BRIGHT_ACCENT_COLOR = "#DE6DE6";
const DIALOG_WRAPPER_CSS = `
all: unset;
position: fixed;
top: 0;
width: 100%;
height: 100%;
`;
const CSS = `
dialog {
position: fixed;
min-width: 25em;
max-height: 90%;
max-width: 90%;
font-size: 11pt;
text-align: left;
font-family: sans-serif;
color-scheme: dark;
overflow-y: auto;
overflow-wrap: break-word;
}
dialog, dialog.polyfilled button {
color: white;
background-color: black;
}
dialog.polyfilled {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
dialog::backdrop, dialog + .backdrop {
background-color: rgba(0, 0, 0, .5);
}
.align-right {
text-align: right;
}
button {
/* https://stackoverflow.com/a/27722954 */
display: inline;
}
a {
color: ${ACCENT_COLOR};
text-decoration: none;
}
a:hover {
color: ${BRIGHT_ACCENT_COLOR};
text-decoration: underline;
}
`;
function showElementList() {
if (document.getElementById(DIALOG_WRAPPER_ID)) {
return;
}
let dialogWrapper = document.createElement("div");
dialogWrapper.id = DIALOG_WRAPPER_ID;
dialogWrapper.style = DIALOG_WRAPPER_CSS;
let shadowRoot = dialogWrapper.attachShadow({mode: "closed"});
let link = document.createElement("link");
link.rel = "stylesheet";
link.href = GM_getResourceURL("dialogPolyfillCSS");
shadowRoot.append(link);
let style = document.createElement("style");
style.textContent = CSS;
shadowRoot.append(style);
let dialog = document.createElement("dialog");
if (!dialog.showModal) {
dialog.classList.add("polyfilled");
}
dialogPolyfill.registerDialog(dialog);
dialog.addEventListener("close", hideElementList, {once: true, passive: true});
dialogWrapper.addEventListener("click", function(e) {
let rect = dialog.getBoundingClientRect();
if (rect.top <= e.clientY && rect.left <= e.clientX && rect.bottom >= e.clientY && rect.right >= e.clientX) {
return;
}
hideElementList();
}, {passive: true});
let buttonWrapper = document.createElement("div");
buttonWrapper.classList.add("align-right");
let button = document.createElement("button");
button.textContent = "Close";
button.addEventListener("click", hideElementList, {once: true, passive: true});
buttonWrapper.append(button);
dialog.append(buttonWrapper);
let hr = document.createElement("hr");
dialog.append(hr);
let elements = getElementList();
if (elements.length) {
let ul = document.createElement("ul");
for (let i of getElementList()) {
ul.append(i);
}
dialog.append(ul);
} else {
dialog.append("There are no elements with an ID.");
}
shadowRoot.append(dialog);
document.body.append(dialogWrapper);
dialog.showModal();
}
function hideElementList() {
let dialogWrapper = document.getElementById(DIALOG_WRAPPER_ID);
if (!dialogWrapper) {
return;
}
dialogWrapper.remove();
}
function getElementList() {
let elements = [];
for (let element of document.body.querySelectorAll("[id]")) {
if (shouldIgnoreElement(element)) {
continue;
}
elements.push(getElementListItem(element));
}
return elements;
}
function shouldIgnoreElement(element) {
// Check if the element is not visible
let rect = element.getBoundingClientRect();
if (rect.height === 0 || rect.width === 0) {
return true;
}
// Check if the element is a svg or a part of one
// https://arstechnica.com/information-technology/2023/05/critics-say-googles-new-zip-and-mov-domains-will-be-a-boon-to-scammers/
while (element) {
if (element.localName === "svg") {
return true;
}
element = element.parentElement;
}
return false;
}
function getElementListItem(element) {
let newLocation = new URL(location.href);
let li = document.createElement("li");
let a = document.createElement("a");
newLocation.hash = a.innerText = "#" + element.id;
a.href = newLocation.href;
a.addEventListener("click", function(e) {
if (e.ctrlKey) {
return;
}
hideElementList();
}, {passive: true});
li.append(a);
let description = getElementDescription(element);
if (description) {
li.append(" (", description, ")");
}
return li;
}
function getElementDescription(element) {
let addEilipses = false;
let text = (element.innerText || "").trim();
let newlineIndex = text.indexOf("\n");
if (newlineIndex !== -1) {
text = text.substring(0, newlineIndex);
addEilipses = true;
}
if (text.length > 50) {
text = text.substring(0, 50);
addEilipses = true;
}
if (addEilipses) {
text += "...";
}
return text;
}
GM_registerMenuCommand("Show elements popup", function() {
if (!document.getElementById(DIALOG_WRAPPER_ID)) {
showElementList();
} else {
hideElementList();
}
})