244 lines
6.5 KiB
JavaScript
244 lines
6.5 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.13
|
|
// @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 = `
|
|
/* https://lamplightdev.com/blog/2019/03/26/why-is-my-web-component-inheriting-styles/ */
|
|
all: initial;
|
|
|
|
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;
|
|
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], a[name]")) {
|
|
let id = element.id;
|
|
if (!id && element.localName === "a") {
|
|
id = element.name;
|
|
}
|
|
if (!id) {
|
|
continue;
|
|
}
|
|
if (shouldIgnoreElement(element)) {
|
|
continue;
|
|
}
|
|
elements.push(getElementListItem(element, id));
|
|
}
|
|
|
|
return elements;
|
|
}
|
|
|
|
function shouldIgnoreElement(element) {
|
|
// Check if the element is not visible
|
|
// https://wayland.freedesktop.org/docs/html/apb.html has <a name=...> elements with no size
|
|
if (element.localName !== "a") {
|
|
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, id) {
|
|
let newLocation = new URL(location.href);
|
|
|
|
let li = document.createElement("li");
|
|
|
|
let a = document.createElement("a");
|
|
newLocation.hash = a.innerText = "#" + 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;
|
|
// Attempt to get text by going up the DOM, as https://wayland.freedesktop.org/docs/html/apb.html has elements with no content
|
|
let text = "";
|
|
while (!text && element) {
|
|
text = (element.innerText || "").trim();
|
|
element = element.parentElement;
|
|
}
|
|
|
|
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();
|
|
}
|
|
}) |