Dimava / Ficscript(Ficbook.net)

// ==UserScript==
// @name        Ficscript(Ficbook.net)
// @author      Dimava
// @include     https://ficbook.net/*
// @include     http://ficbook.net/*
// @version     1
// @grant       none
// ==/UserScript==// ==UserScript==
// @name        Ficscript(Ficbook.net)
// @author      Dimava
// @include     https://ficbook.net/*
// @include     http://ficbook.net/*
// @version     1.4
// @grant       none
// ==/UserScript==
function setHiddenAuthors(authorsId) {
  localStorage.setItem('ficbookFilter.hiddenAuthors', JSON.stringify(authorsId)
  )
}
function setHiddenWritings(writingsId) {
  localStorage.setItem('ficbookFilter.hiddenWritings', JSON.stringify(writingsId)
  )
}
function getHiddenAuthors() {
  var str = localStorage.getItem('ficbookFilter.hiddenAuthors')
  return str ? JSON.parse(str)  : [
  ]
}
function getHiddenWritings() {
  var str = localStorage.getItem('ficbookFilter.hiddenWritings')
  return str ? JSON.parse(str)  : [
  ]
}
function createEIbuttons() {
  // Create export button
  var $exportBtn = $('<div class="exportBtn"></div>').attr('title', 'Экспортировать бан-список').html('E').appendTo(document.body)
  $exportBtn.click(function () {
    var data = JSON.stringify({
      authors: getHiddenAuthors(),
      writings: getHiddenWritings()
    })
    var $a = $('<a/>').attr('href', 'data:text/plain,' + data).attr('download', 'ficbook-filters.json').appendTo(document.body)
    $a[0].click()
    $a.remove()
  })
  // Create import button
  var $fileInput = $('<input type="file"/>').css({
    position: 'absolute',
    top: '-10000px',
    left: '-10000px'
  }).appendTo(document.body)
  var $importBtn = $exportBtn.clone().css({
    right: '110px'
  }).attr('title', 'Импортировать бан-список').html('I').appendTo(document.body)
  $importBtn.click(function () {
    $fileInput.click()
  })
  $fileInput.change(function () {
    try {
      var fileReader = new FileReader()
      fileReader.readAsText(this.files[0])
      fileReader.onloadend = function () {
        try {
          var json = JSON.parse(this.result)
        } 
        catch (e) {
          alert('Ошибка. Файл бан-списка поврежден.\n' +
          'По умолчанию это обычно "ficbook-filters.json".\n' +
          'Может быть, вы выбрали какой-то другой файл?')
          return
        }
        setHiddenWritings(json.writings)
        setHiddenAuthors(json.authors)
        alert('Импортировано ' + json.writings.length +
        ' произведений и ' + json.authors.length + ' авторов.\n' +
        'Обновите страницу чтобы применить фильтры немедленно.')
      }
    } 
    catch (e) {
      alert('Ошибка. Файл бан-списка поврежден.')
      console.error(e.toString())
    }
  })
  console.log('createEIbuttons applied');
}
// Hide writings

function hideFics() {
  // Load hidden authors and writings
  var hiddenAuthors = getHiddenAuthors()
  var hiddenWritings = getHiddenWritings()
  // Hide writings
  $('.fanfic_thumb').each(function () {
    var $title = $('a.title', this),
    title = $title.text(),
    writingId = parseInt($title.attr('href').match(/\/(\d+)$/) [1], 10),
    text = $('b:eq(1)', this).closest('small').text(),
    pageCount = parseInt(text.match(/(\d+)\s+страниц/) [1]),
    slash = text.indexOf('(яой)') >= 0,
    writingHidden = hiddenWritings.indexOf(writingId) >= 0,
    $author = $('>tbody>tr:eq(1)>td:eq(0) a:eq(0)', this),
    authorId = parseInt($author.attr('href').match(/\/(\d+)$/) [1], 10),
    authorHidden = hiddenAuthors.indexOf(authorId) >= 0,
    $thumb = $(this),
    $ficbox = $thumb.closest('.ficbox')
    function hideThisFic($hideReason) {
      $resurrectBtn =
      $ficbox.addClass('hiddenfic').prepend('<tr class="rsrbox"><td colspan=9></td></tr>').find('td:first').html('<span class="rsrBtn">' + $hideReason + '</span> <a href="' + $title.attr('href') + '">"'
      + title + '"</a> от <a href="' + $author.attr('href') + '">' + $author.text() + '</a>.').find('span').attr('title', 'Да ВОССТАНЬ!');
      $ficbox.children().css('background-color', 'rgba(255,255,255,0.5)').css('transition', 'background-color 1s').find('>tr:not(.rsrbox)').hide();
      $resurrectBtn.hover(function () {
        $(this).parents('.ficbox').find('>*>tr:not(.rsrbox)').show();
      }, function () {
        $(this).parents('.ficbox').find('>*>tr:not(.rsrbox)').hide()
      }).click(function () {
        if (authorHidden) {
          hiddenAuthors = getHiddenAuthors() // could be updated on the other tab
          hiddenAuthors.splice(hiddenAuthors.indexOf(authorId), 1)
          setHiddenAuthors(hiddenAuthors)
        }
        if (writingHidden) {
          hiddenWritings = getHiddenWritings() // could be updated on the other tab
          hiddenWritings.splice(hiddenWritings.indexOf(writingId), 1)
          setHiddenWritings(hiddenWritings)
        }
        $(this).parents('.ficbox').removeClass('hiddenfic').children().css('background-color', 'transparent').find('tr').show().end().find('.rsrbox').remove();
        return false
      })
    }
    if (writingHidden)
    hideThisFic('Отфильтрован фанф') 
    else if (authorHidden)
    hideThisFic('Отфильтрован по автору') 
    else if (slash)
    hideThisFic('Отфильтрован <i>слеш</i>') 
    else if (pageCount < 30)
    hideThisFic('Отфильтрован по длине (' + pageCount + ')')
    // Add removing writing button
    var $rmWritingBtn = $('<span class="removeFic"></span>').attr('title', 'Забанить это произведение').html('&#10006;')
    $title.parent().append($rmWritingBtn)
    $rmWritingBtn.click(function () {
      alert()
      hiddenWritings = getHiddenWritings() // could be updated on the other tab
      hiddenWritings.push(writingId)
      setHiddenWritings(hiddenWritings)
      hideThisFic('Отфильтрован фанф')
      return false
    })
    // Add removing author button
    var $rmAuthorBtn = $('<span class="removeFic removeAuthor"></span>').attr('title', 'Забанить ВСЕ произведения этого автора').html('&#10006;')
    $title.closest('.ficbox').find('small:first a:first').after($rmAuthorBtn);
    $rmAuthorBtn.click(function () {
      hiddenAuthors = getHiddenAuthors() // could be updated on the other tab
      hiddenAuthors.push(authorId)
      setHiddenAuthors(hiddenAuthors)
      hideThisFic('Отфильтрован по автору')
      return false
    })
  })
  console.log('hideFics applied');
}
//
//
//
// location detector v1

function getLoc() {
  var a = window.location.pathname.split('/');
  var b = window.location.href.split('=');
  var c = '';
  for (i = 1; i < a.length; i++) {
    c += (a[i].charAt(0) <= '9' && a[i].charAt(0) >= '0') ? '0' : a[i].charAt(0);
  }
  for (i = 1; i < b.length; i++) {
    c += (b[i].charAt(0) <= '9' && b[i].charAt(0) >= '0') ? '0' : b[i].charAt(0);
  }
  return c;
}
function testLoc(a) {
  var c = getLoc()
  for (var i = 0; i < arguments.length; i++) {
    if (arguments[i] == c) return true;
  }
  return false;
}
//
// ficboxes classifier  v1 : adds .ficbox class

function classFics() {
  if ($('table.fanfic_thumb').length > 0) {
    if (testLoc('hcu')) { // create ficboxes
      $('table:has(.fanfic_thumb):not(:has(h1))').addClass('ficbox');
    } else {
      $('table.fanfic_thumb').addClass('ficbox');
    }
    $('.ficbox:has(.new_parts_in_title)').addClass('newfic'); // select updated
    $('table[style="background-color: #ffff99"]').addClass('newfic').attr('style', '');
    return true;
  } else {
    return false;
  }
  console.log('classFics applied');
}
//
// everything compressor

function compressFics() {
  // chapter TRASHHIDER  v1
  if (testLoc('r00')) {
    if ($('.fanfic_reward_list').length > 15) { // hide 16+ rewards
      $('.fanfic_reward_list:eq(0)').html('x' + $('.fanfic_reward_list:gt(0)').length);
      $('.fanfic_reward_list:gt(0)').hide();
      $('.fanfic_reward_list').parent().find('br:gt(1)').remove();
    }
    $('.give_reward_fic').hide(); // hide reward giver
    $('.public_beta_info').hide(); // hide beta info
    $('hr:first').nextUntil('table').css('display', 'inline').css('padding-right', '0'); // collapce font setter
    $($('.choose_font').contents().filter(function () {
      return this.nodeType == 3;
    })).remove();
    $('hr:first').nextUntil('table').addClass('hoverVisible').find('br').hide();
  }
  // contents compressor

  if (testLoc('r0')) {
    $('<div class="partListContainer"><div></div></div>').insertAfter($('.part_list').prev().prev()).children().append($('.part_list').prev().text($('.main_cell h1:first').text()).css('margin', '5px')).append('<h6>' + $('.main_cell td:last span.urlize:first').html() + '</h6>').parent().append($('.part_list').prepend('<div class="partListTop">^ ^ ^</div>').append('<div class="partListBottom">^ ^ ^</div>'));
  }
  console.log('compressFics applied');
}
//
// checker : adds checkboxes to open multiple fics at once

function checkFics() {
  $('.ficbox>*>tr:has(.title)').append('<td align="right" style="width: 30px;"><input type="checkbox" class="openAll"></td>');
  $('.ficbox td[style]:not([width])').attr('colspan', '2')
  $('.openAll').click(function () {
    $('#openAll').show();
    $('.openAll').css('opacity', '1')
  });
  $('<button id="openAll">Открыть<br>выбранные</button>').appendTo($('body')).click(function () {
    $('input.openAll:checked').each(function () {
      window.open($(this).parent().parent().find('.title') [0].href, '_blank'
      );
    });
    $('.ficbox').has('input.openAll:checked').children().css('background-color', 'rgba(127,255,0,0.3)');
  });
  console.log('checkFics applied');
}
//
// load new collections v1 : shows new fics in updated collection (/home/collections?type=other)

function loadCols(callback) {
  collectionsNotLoaded = $('tr[style] .collection_thumb').length;
  if (collectionsNotLoaded == 0) {
    callback()
  }
  $('tr[style] .collection_thumb').each(function () {
    $(this).after('<div style="display:none">!div!</div>').next().load($(this).find('.title a') [0].href, function () {
      $(this).after($(this).find('.fic_and_collection[style]').each(function () {
        $(this).append($(this).children().eq(0))
      })
      );
      $(this).html('!!!div!!!');
      collectionsNotLoaded--;
      if (collectionsNotLoaded == 0) {
        callback()
      }
    }
    )
  })
  console.log('loadCols applied');
}
//
// keypress handler v2

function handleKeys() {
  $(document).keydown(function (e) {
    var tag = e.target.tagName.toLowerCase();
    var keyStr = (e.ctrlKey ? '[Ctrl] + ' : '') + (e.shiftKey ? '[Shift] + ' : '') + '[' + (typeof e.key == 'undefined' ? ('#' + e.which)  : e.key) + ']' + ' pressed' + ((tag != 'input' && tag != 'textarea') ? '' : ' inside input') + ((getSelectedText() == '') ? '' : ' with "' + getSelectedText() + '" selected');
    var keyCode = (e.ctrlKey ? 'c' : '') + (e.shiftKey ? 's' : '') + ('#' + e.which) + ((tag != 'input' && tag != 'textarea') ? '' : 'i') + ((getSelectedText() == '' || getSelectedText == '\n') ? '' : '{}');
    console.log(keyCode + ' : ' + keyStr);
    switch (keyCode) {
      case 's#37': // s#37 : [Shift] + [ArrowLeft] pressed {open prev or first chap}
        if (testLoc('r0') && $('.part_list a:first') [0]) window.location.href = $('.part_list a:first') [0].href;
         else if (testLoc('r00') && $('td[width="45%"]:first a') [0]) document.location.href = $('td[width="45%"]:first a') [0].href;
        break
      case 's#39': // s#39 : [Shift] + [ArrowRight] pressed {open next ol last chap}
        if (testLoc('r0') && $('.part_list a:last') [0]) window.location.href = $('.part_list a:last') [0].href;
         else if (testLoc('r00') && $('td[width="45%"]:last a') [0]) document.location.href = $('td[width="45%"]:last a') [0].href;
        break
      case 's#38': // s#38 : [Shift] + [ArrowUp] pressed {open contents}
        if (testLoc('r00') && $('td[align="center"] a') [0]) document.location.href = $('td[align="center"] a') [0].href;
        break
      case '#39': // #39 : [ArrowRight] pressed {open next chap if read}
        if (testLoc('r00') && $('td[width="45%"]:last a') [0] && ($('td[width="45%"]:last a') [0].getBoundingClientRect().top - window.innerHeight < 163)) document.location.href = $('td[width="45%"]:last a') [0].href;
        break
    }
  });
  console.log('handleKeys applied');
}
//
// scroll : scrolls page down to somewhere

function scrollFic() {
  if (testLoc('r0')) {
    $('.part_list').scrollTop(9999);
    $('.partListContainer') [0].scrollIntoView();
  }
}
//
// style

function styleFics() {
  var styleStr = '<style>'
  + '.ficbox{margin:15px 0}'
  + '.newfic{background-color:#ff9;}'
  + '.rsrbox {color:#888; transition: background-color 0.3s; background-color: rgba(255,255,255,0.3);}'
  + '.rsrBtn:hover {color:red;cursor:pointer}'
  + '.rsrbox:hover {background-color: white}'
  + '.hiddenfic{border-collapse:collapse;margin:0}'
  + '.hiddenfic>*>tr:not(.rsrbox) {opacity: 0.5; background-color: white}'
  + '.openAll{opacity:0.1; transition: opacity 0.3s;}'
  + '.openAll:hover {opacity: 1 !important}'
  + '#openAll{display: none; position: fixed; right: 270px; bottom: 0px}'
  + '.exportBtn{position: fixed; bottom: 20px; right: 80px; color: white; background-color: rgba(0, 0, 0, 0.52); border-radius: 3px; width: 25px; text-align: center; height: 24px; line-height: 24px; cursor: pointer; font-size: 18px;opacity:0.1;transition: opacity 0.3s;}'
  + '.exportBtn:hover{opacity:1}'
  + '.removeFic{cursor: pointer; color: white; font-weight: bold; display: inline-block; background: rgb(136, 1, 1); border-radius: 10px; height: 20px; line-height: 19px; width: 20px;text-align: center;opacity:0.1;transition: opacity 0.3s;}'
  + '.removeAuthor{height: 14px; width: 14px; line-height: 14px; font-size: 12px; margin-left: 3px}'
  + '.removeFic:hover{opacity:1}'
  + '.hiddenfic .removeFic{display: none;}'
  + '.partListContainer{max-height:98vh;display:flex;flex-direction:column;box-shadow:0px 0px 2px inset,0px 0px 2px;margin-right:10px;padding:0px 5px 5px 0}'
  + '.partListBottom{font-size: 15px; height: 10px; overflow: hidden; text-align: center;}'
  + '.partListTop{font-size: 15px; height: 11px; overflow: hidden; text-align: center;transform: rotate(180deg);}'
  + '.part_list{overflow-y: auto}'
  + 'h6{margin:0;padding: 0 15px 10px 15px}'
  /*!!!*/
  + 'table{border-collapse:collapse}'
  /*!!!*/
  + 'td{padding-top: 0 !important;padding-bottom: 0 !important;}'
  + '.hoverVisible{opacity:0.1;transition: opacity 0.3s;}'
  + '.hoverVisible:hover{opacity:1}'
  + '</style>'
  $('<style></style>').appendTo($('head')).html(styleStr);
  console.log('styleFics applied');
}
//
// calls

function callFicscript() {
  console.log('ficscript start');
  styleFics();
  loadCols(function () {
    classFics();
    if (testLoc('p', 'hc', 'hco', 'hcu', 'c0', 'c0a', 'c0a0', 'c0u')) {
      createEIbuttons();
      hideFics();
    }
    //removethis
/*
    if (testLoc('hf')) {
      $('.new_top').each(function () {
        window.open(this.href, '_blank');
      })
    }*/
    checkFics();
    compressFics();
    handleKeys();
    scrollFic();
  })
  //  unsafeWindow.callFicscript = callFicscript;
  console.log('ficscript end')
}
//

callFicscript();