Samlib Reader

Делает самиздатовские текста более читабельными: смена фона на тёмный, смена шрифта на verdana, смена цвета текста на светлый, текст выровнен по ширине строки, добавлен автоматический перенос слов. Дополнительно добавлено окно настроек, позволяющее изменить ширину текста, тип и размер шрифта, цвет общего фона страницы, а также принудительно сменить цвет текста, в случае, когда автор вручную установил цвет части текста. Работает также на zhurnal.lib.ru, budclub.ru

目前為 2021-05-17 提交的版本,檢視 最新版本

// ==UserScript==
// @name Samlib Reader
// @description Делает самиздатовские текста более читабельными: смена фона на тёмный, смена шрифта на verdana, смена цвета текста на светлый, текст выровнен по ширине строки, добавлен автоматический перенос слов. Дополнительно добавлено окно настроек, позволяющее изменить ширину текста, тип и размер шрифта, цвет общего фона страницы, а также принудительно сменить цвет текста, в случае, когда автор вручную установил цвет части текста. Работает также на zhurnal.lib.ru, budclub.ru
// @copyright 2019, Angens (https://openuserjs.org/users/angens)
// @license MIT
// @version 2.3.0
// @match http://samlib.ru/*
// @match http://zhurnal.lib.ru/*
// @match http://budclub.ru/*
// @grant none
// @namespace https://greasyfork.org/users/386214
// ==/UserScript==
//
// ==OpenUserJS==
// @author angens
// ==/OpenUserJS==



/*
 *  Функция для работы с форматом
 */
function changer(){
  
  // Прописываем в тело возможность переноса слов
  document.querySelector("body").setAttribute("lang", "ru");
  document.querySelector("body").setAttribute("style", "-moz-hyphens: auto; -webkit-hyphens: auto; -ms-hyphens: auto; white-space: unset;");
  
  /*
   * Парсинг текста с помощью поиска <hr>
   */
  let lines = document.querySelectorAll('hr')
  let tail = document.createElement('div')
  while (lines[6].nextSibling){
    tail.append(lines[6].nextSibling)
  }
  let descLines = tail.querySelectorAll('hr')
  

  /*
   *  Создание новых блоков
   */
  // Блок работы с произведением
  
  // Создаём блок описания
  let description = document.createElement('div');
  description.id = "SamLibReaderDescription";
  while (descLines[0].nextSibling){
    description.append(descLines[0].nextSibling)
  }
  description.insertBefore(descLines[0], description.firstChild)
 
  // Создаём блок текста
  let new_element = document.createElement('div');
  new_element.id = "SamLibReader";
  new_element.style.color = "wheat";
  new_element.style.fontSize = "18px";
  new_element.style.fontFamily = "verdana";
  new_element.style.backgroundColor = "#212127";
  new_element.style.width = '40%';
  new_element.style.marginLeft = 'auto';
  new_element.style.marginRight = 'auto';  
  new_element.style.padding = "5%";
  new_element.style.textAlign = "justify";
  new_element.setAttribute('link', 'gray');
  while (tail.firstChild){
    new_element.append(tail.firstChild)
  }
  tail = null
  
  // Создаём контейнер, в который помещаются блоки настройки, текста, комментариев. Затем контейнер помещается в body
  let container_block = document.createElement('div');
  container_block.id = "SamLibContainer";
  container_block.append(new_element);
  container_block.append(description);
  document.body.append(container_block);
  

  // Заменяем цвет ссылок в документе
  let links = new_element.querySelectorAll('a');
   for (let i = 0; i < links.length; i++){
     if (links[i].hasAttribute('href'))
      links[i].style.color = 'gray';
  }
  
  /*
   *  Блок работы с тегом <pre>
   */
  let pre = document.querySelectorAll("pre");
  for (let i = 0; i < pre.length; i++){
    pre[i].style.marginLeft = 'auto';
    pre[i].style.marginRight = 'auto';
    pre[i].style.width = 'min-content';
    pre[i].style.color = "wheat";
    pre[i].style.fontSize = "18px";
    pre[i].style.fontFamily = "roboto condensed, verdana";
    pre[i].style.backgroundColor = "#212127";
  }
  
  addControls();
}
 
/*
 *  Функция работы с панелью управления
 */
function addControls(){
  
  /*
   *  Добавление блоков
   */
  // Добавление общего блока панели
  let showHide = document.createElement('div');
  showHide.setAttribute("style", "position: sticky; position: -webkit-sticky;")
  showHide.style.width = 'min-content';
  showHide.style.height = 'min-content';
  showHide.style.top = '15%';
  document.getElementById('SamLibContainer').insertBefore(showHide, document.getElementById('SamLibContainer').firstChild);
  
  // Кнопка скрытия/раскрытия панели
  let showBtn = document.createElement('button');
  showBtn.style.width = '100px';
  showBtn.style.height = '40px';
  showBtn.textContent = 'Настройки';
  showBtn.style.fontWeight = 'bold';
  showHide.append(showBtn);
  
  // Внутренний блок, который скрывается/раскрывается
  let controlPanel = document.createElement('div');
  controlPanel.style.display = 'none';
  
  // Кнопки ширины текста
  let sizeButtons = document.createElement('div');
  let buttons = [];
  for (let i = 0; i < 3; i++){
    let tmpBtn = document.createElement('button');
    tmpBtn.style.float = 'left';
    tmpBtn.style.width = 100/3 + "%";
    tmpBtn.textContent = i * 10 + 30 + "%";
    buttons.push(tmpBtn);
    sizeButtons.append(buttons[i]);
  }
  
  // Слайдер ширины текста
  let sliderBlock = document.createElement('div');
  let slider = document.createElement('input');
  slider.addEventListener('change', () => {
    document.getElementById('SamLibReader').style.width = slider.value + "%";
  });
  slider.type = 'range';
  slider.min = '1';
  slider.max = '100';
  slider.value = 40;
  slider.id = 'textWidth';
  slider.style.width = '350px';
  sliderBlock.append(slider);
  
  // Комбобокс с выбором шрифта
  let choiceBlock = document.createElement('div');
  choiceBlock.style.width = 'min-content';
  choiceBlock.style.margin = 'auto';
  let comboBox = document.createElement('select');
  let fontChoice = ['verdana', 'roboto', 'roboto condensed'];
  let options = [];
  for (let i = 0; i < fontChoice.length; i++){
    let opt = document.createElement('option');
    opt.text = fontChoice[i];
    options.push(opt);
    comboBox.add(options[i], comboBox[i]);
  }
  choiceBlock.append(comboBox);
  
  // Размер шрифта
  let fontSize = document.createElement('div');
  let btnMinus = document.createElement('button');
  let sizeBox = document.createElement('a');
  sizeBox.style.textShadow = "-1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff";
  let btnPlus = document.createElement('button');
  fontSize.style.width = '86px';
  fontSize.style.margin = 'auto';
  
  btnMinus.style.float = 'left';
  btnMinus.textContent = "-";
  btnMinus.style.width = '30px';
  btnMinus.style.textAlign = 'center';
  
  btnPlus.style.flost = 'left';
  btnPlus.textContent = "+";
  btnPlus.style.width = '30px';
  btnPlus.style.textAlign = 'center';
  
  sizeBox.style.float = 'left';
  sizeBox.style.display = 'block';
  sizeBox.style.width = '24px';
  sizeBox.style.height = '22px';
  sizeBox.textContent = '18';
  sizeBox.style.textAlign = 'center';
  
  fontSize.append(btnMinus);
  fontSize.append(sizeBox);
  fontSize.append(btnPlus);
  
  // Сделать весь фон тёмным
  let bgBlock = document.createElement('div');
  bgBlock.style.width = 'max-content';
  let bgCheckBox = document.createElement('input');
  let bgLabel = document.createElement('label');  
  bgLabel.style.textShadow = "-1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff";
  bgLabel.style.userSelect = "none";
  bgCheckBox.type = 'checkbox';
  bgCheckBox.name = 'bgCheckbox';
  bgCheckBox.borderRadius = '5px';
  bgLabel.for = 'bgCheckbox';
  bgLabel.textContent = "Сделать весь фон тёмным";
  bgBlock.append(bgCheckBox);
  bgBlock.append(bgLabel);
  
  // Принудительная смена текста шрифта
  let fntBlock = document.createElement('div');
  fntBlock.style.width = '350px';
  let fntCheckBox = document.createElement('input');
  let fntLabel = document.createElement('label');
  fntLabel.style.textShadow = "-1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff";
  fntLabel.style.userSelect = "none";
  fntCheckBox.type = 'checkbox';
  fntCheckBox.name = 'fntCheckbox';
  fntLabel.for = 'fntCheckbox';
  fntLabel.textContent = "Принудительно сменить цвет шрифта в тексте";
  fntBlock.append(fntCheckBox);
  fntBlock.append(fntLabel);
  
  // Добавление блоков опций в панель
  if (document.getElementById('SamLibReader')){
    controlPanel.append(sizeButtons);
    controlPanel.append(sliderBlock);
  }
  controlPanel.append(choiceBlock);
  controlPanel.append(fontSize);
  controlPanel.append(bgBlock);
  if (document.getElementById('SamLibReader'))
    controlPanel.append(fntBlock);
  showHide.append(controlPanel);
  
  /*
   *  Функционал
   */
  // Управление отображением панели
  function isElementHidden (element) {
    return window.getComputedStyle(element, null).getPropertyValue('display') === 'none';
  }  
  showBtn.addEventListener('click', () => {
    if (isElementHidden(controlPanel)){
      controlPanel.style.display = 'block';
      showHide.style.backgroundColor = 'rgba(0, 0, 0, 0.3)';
      showHide.style.borderRadius = "7px"
      showBtn.textContent = 'Скрыть';
    } else {
      controlPanel.style.display = 'none';
      showHide.style.backgroundColor = 'rgba(0, 0, 0, 0)';
      showBtn.textContent = 'Настройки';
    }
  });
  
  // Управление шириной текста
  for (let i = 0; i < 3; i++){
    buttons[i].addEventListener('click', () => {
      document.getElementById('SamLibReader').style.width = buttons[i].textContent;
      slider.value = 30 + i * 10;
    });    
  }
  
  // Смена шрифта
  comboBox.addEventListener('change', (event) => {
    if(document.getElementById('SamLibReader')){
      document.getElementById('SamLibReader').style.fontFamily = event.target.value;
    }
    if(document.querySelector('pre')){
      document.querySelector('pre').style.fontFamily = event.target.value;
    }
  });
  
  // Смена размера
  btnMinus.addEventListener('click', () => {
    if(sizeBox.textContent != 6){
      sizeBox.textContent = Number(sizeBox.textContent) - 2;
      if (document.getElementById('SamLibReader'))
        document.getElementById('SamLibReader').style.fontSize = sizeBox.textContent;
      if (document.querySelector('pre'))
        document.querySelector('pre').style.fontSize = sizeBox.textContent;
    }
  });
  btnPlus.addEventListener('click', () => {
    if(sizeBox.textContent != 32){
      sizeBox.textContent = Number(sizeBox.textContent) + 2;
      if (document.getElementById('SamLibReader'))
        document.getElementById('SamLibReader').style.fontSize = sizeBox.textContent;
      if (document.querySelector('pre'))
        document.querySelector('pre').style.fontSize = sizeBox.textContent;
    }
  });
  
  // Смена общего фона
  bgCheckBox.addEventListener('change', () => {
    if(bgCheckBox.checked){
      document.querySelector("body").style.backgroundColor = "#212127";
      document.querySelector("body").setAttribute("link", "gray");
      document.querySelector("body").setAttribute("vlink", "#C6B2D4");
      document.querySelector("body").style.color = 'white';
      fntLabel.style.textShadow = "-1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000";
      bgLabel.style.textShadow = "-1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000";
      sizeBox.style.textShadow = "-1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000";
    }else{
      document.querySelector("body").style.backgroundColor = "#E9E9E9";
      document.querySelector("body").setAttribute("link", "0000EE");
      document.querySelector("body").setAttribute("vlink", "#551A7E");
      document.querySelector("body").style.color = 'black';
      fntLabel.style.textShadow = "-1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff";
      bgLabel.style.textShadow = "-1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff";
      sizeBox.style.textShadow = "-1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff";
    }
  });
  bgCheckBox.click();  // Включаем тёмный фон по умолчанию
  
  // Принудительная смена цвета шрифта
  if(document.getElementById('SamLibReader')){
    let fonts = document.getElementById('SamLibReader').querySelectorAll('font');
    let st_font = document.getElementById('SamLibReader').querySelectorAll('table');
    let fontsColors = [];
    
    for (let i = 0; i < fonts.length; i++)
      fontsColors.push(fonts[i].getAttribute('color'));
    fntCheckBox.addEventListener('change', () => {
      if(fntCheckBox.checked){
        for (let i = 0; i < fonts.length; i++)
          fonts[i].setAttribute('color', 'wheat');
        for (let i = 0; i < st_font.length; i++)
          st_font[i].style.color = 'wheat';
      }else{
        for (let i = 0; i < fonts.length; i++)
          fonts[i].setAttribute('color', fontsColors[i]);
        for (let i = 0; i < st_font.length; i++)
          st_font[i].style.color = 'initial';
    }
  });
  }
  // Переключение чекбоксов по клику на лейблы
  bgLabel.addEventListener('mouseover', () => {
    bgLabel.style.textDecoration = "underline";
  })
  fntLabel.addEventListener('mouseover', () => {
    fntLabel.style.textDecoration = "underline";
  })
  bgLabel.addEventListener('mouseleave', () => {
    bgLabel.style.textDecoration = "none";
  })
  fntLabel.addEventListener('mouseleave', () => {
    fntLabel.style.textDecoration = "none";
  })
  bgLabel.addEventListener('click', () => {
    bgCheckBox.click();
  })
  fntLabel.addEventListener('click', () => {
    fntCheckBox.click();
  })
}
 
 
let mRef = /http:\/\/(zhurnal\.lib\.ru|samlib\.ru|budclub\.ru)\/.\/.+\/.+\.shtml/;
let exRef = /http:\/\/(zhurnal\.lib\.ru|samlib\.ru|budclub\.ru)\/.\/.+\/(index|stat).+\.shtml/;
let currRef = window.location.href;
 
if (mRef.test(currRef) && !exRef.test(currRef))
  document.addEventListener("DOMContentLoaded", changer());