The script allows you to select text and use the context menu when reading a book.
当前为
// ==UserScript==
// @name AuthorTodayImprover
// @name:ru AuthorTodayImprover
// @namespace 90h.yy.zz
// @version 0.1.1
// @author Ox90
// @include https://author.today/reader/*
// @description The script allows you to select text and use the context menu when reading a book.
// @description:ru Скрипт возвращает возможность выделять текст и использовать контекстное меню при чтении на сайте.
// @run-at document-start
// @license MIT
// ==/UserScript==
(function start() {
"use strict";
/**
* Старт скрипта. Вызывается после загрузки DOM-дерева.
*
* @return void
*/
function start() {
[ "#reader.page-content", "div.text-wrapper" ].forEach(function(selector) {
const el = document.querySelector(selector);
el && processElement(el);
});
}
/**
* Обрабатывает переданный DOM-элемент.
* Если у элемента нет класса loading, то он обрабатывается немедленно,
* в ином случае на него вешается наблюдатель, а обратотка откладывается.
*
* @param Element el Обрабатываемый DOM-элемент
*
* @return void
*/
function processElement(el) {
if (!el.classList.contains("loading")) {
fixStyles(removeEvents(el));
return;
}
(new MutationObserver(function(mutations, observer) {
observer.disconnect();
processElement(el);
})).observe(el, { attributes: true, attributeFilter: [ "class" ] });
}
/**
* Подменяет переданный элемент новым, который создается через перенос его детей переднанного элемента в новый
* и копирование атрибутов из старого элемента в новый за исключением некоторых атрибутов, которые
* блокируют привычную функциональность как выделение текста и контекстное меню.
* el.cloneNode(true) здесь не используется, чтобы избежать потери обработчиков у вложенных элементов.
*
* @param Element el DOM-элемент для замены
*
* @return Element Вновь созданный элемент
*/
function removeEvents(el) {
const evil_attributes = [
"oncontextmenu", "onmousedown", "onselectstart", "unselectable"
];
const new_el = document.createElement(el.tagName);
for (let attr of el.attributes) {
if (!evil_attributes.includes(attr.name)) {
new_el.setAttribute(attr.name, attr.value);
}
}
while (el.childNodes[0]) {
new_el.appendChild(el.childNodes[0]);
}
el.replaceWith(new_el);
return new_el;
}
/**
* Исправляет стили элемента таким образом, чтобы они не блокировали привычную для пользователя функциональность.
*
* @param Element el DOM-элемент для исравления
*
* @return void
*/
function fixStyles(el) {
el.classList.remove("noselect");
el.style.userSelect = "text";
}
//----------
if (document.readyState === "loading")
window.addEventListener("DOMContentLoaded", start);
else
start();
}());