Samlib Reader

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

2021-01-16 기준 버전입니다. 최신 버전을 확인하세요.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name Samlib Reader
// @description Делает самиздатовские текста более читабельными: смена фона на тёмный, смена шрифта на verdana, смена цвета текста на светлый, текст выровнен по ширине строки, добавлен автоматический перенос слов. Дополнительно добавлено окно настроек, позволяющее изменить ширину текста, тип и размер шрифта, цвет общего фона страницы, а также принудительно сменить цвет текста, в случае, когда автор вручную установил цвет части текста. Работает также на zhurnal.lib.ru, budclub.ru
// @copyright 2019, Angens (https://openuserjs.org/users/angens)
// @license MIT
// @version 2.1.2
// @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;");
  
  /*
   * Блок работы с описанием
   */
  
  // Ищем комментарии на первом уровне тела
  let numOfComments = 0;
  let lastComment;
  let bodyChildren = document.body.childNodes;
  for (let i = 0; i < bodyChildren.length; i++){
    let node = bodyChildren[i];
    if (node.nodeType === 8){
      numOfComments++;
      lastComment = node;
    }
  }
  
  // Создаём блок описания
  let description = document.createElement('div');
  description.id = "SamLibReaderDescription"; 
  
  // Если комментарии начала и конца произведения дети body — сразу записываем всё под нижним комментарием в блок описания
  if(numOfComments === 2){
    while(lastComment.nextSibling){
      description.append(lastComment.nextSibling);
    }
  }
  
  // Ищем комментарий описания и находим главный блок. Все последующие отправляем в блок описания
  if(numOfComments === 1){
    let treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_ALL, null, false);
    
    while (treeWalker.nextNode()){
      if (treeWalker.currentNode.nodeType === 8){
        if (treeWalker.currentNode.nodeValue == "-- Блок описания произведения (слева внизу) ---------------------"){
          let node = treeWalker.currentNode.parentElement.parentElement.parentElement;
          while(node.nextSibling){
            description.append(node.nextSibling);
          }
          description.insertBefore(node, description.firstChild);
        }
      }
    }
  }
    
  
  /*
   *  Блок работы с текстом
   */
  // Блок работы с произведением
  let nodes = document.querySelectorAll("dd, p");
 
  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";
  
  if(nodes[0])
    nodes[0].parentElement.insertBefore(new_element, nodes[0]);
 
  for (let i = 0; i < nodes.length; i++){
    new_element.append(nodes[i]);
  }
  
  document.body.append(description);
  
  /*
   *  Блок работы с тегом <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";
    pre[i].style.padding = '5%';
  }
  
  addControls();
}

/*
 *  Функция работы с панелью управления
 */
function addControls(){
  
  /*
   *  Добавление блоков
   */
  // Добавление общего блока панели
  let showHide = document.createElement('div');
  showHide.style.position = 'fixed';
  //showHide.style.backgroundColor = 'rgba(0, 0, 0, 0.3)';
  showHide.style.width = 'min-content';
  showHide.style.height = 'min-content';
  showHide.style.right = '0px';
  showHide.style.top = '50%';  
  document.body.append(showHide);
  
  // Кнопка скрытия/раскрытия панели
  let showBtn = document.createElement('button');
  showBtn.style.width = '100px';
  showBtn.style.height = '40px';
  showBtn.textContent = 'Настройки';
  showBtn.addEventListener('mouseenter', (event) => event.target.style.borderColor = 'green');
  showBtn.addEventListener('mouseleave', (event) => event.target.style.borderColor = '#778beb');
  showBtn.style.fontWeight = 'bold';
  showHide.append(showBtn);
  
  // Внутренний блок, который скрывается/раскрывается
  let controlPanel = document.createElement('div');
  controlPanel.style.display = 'none';
  //controlPanel.style.height = '100px';
  
  // Кнопки ширины текста
  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');
  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');
  let bgCheckBox = document.createElement('input');
  let bgLabel = document.createElement('label');
  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');
  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';
    }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';     
    }
  });
  
  // Принудительная смена цвета шрифта
  let fonts = document.getElementById('SamLibReader').getElementsByTagName('font');
  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');
    }else{
      for (let i = 0; i < fonts.length; i++)
        fonts[i].setAttribute('color', fontsColors[i]);
    }
  });
}
 
 
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;
console.log(mRef.test(currRef));

if (mRef.test(currRef) && !exRef.test(currRef))
  document.addEventListener("DOMContentLoaded", changer());