imShara / TJournal Unread Comments Navigator

// ==UserScript==
// @name        TJournal Unread Comments Navigator
// @description This script adds navigation buttons for unread comments
// @copyright   2016, Shara (https://tjournal.ru/users/6146)
// @license     MIT License
// @namespace   tj
// @include     https://tjournal.ru/*
// @version     0.2
// @grant       none
// ==/UserScript==
// ==OpenUserJS==
// @author imShara
// ==/OpenUserJS==

// ver 0.2 - Works after ajax comment refresh
// ver 0.1 - Initial version

let options = {
  animate: true,       // scrolling animation
  animationSpeed: 250, // scrolling animation speed in milliseconds
  circular: false,     // circular switching between comments
  position: 20,        // comment position in percentage from bottom of window
  scrollOver: true     // swith between comments with scrolling over navigation buttons
}


let css = `
#unread-navigation {
  position: fixed;
  right: 0;
  top: 50%;
  width: 40px;
  margin-top: 50px;
  z-index: 10001;
  background-color: #aaa;
  text-align: center;
  border-radius: 5px 0 0 5px;
  font-size: 10px;
  line-height: 10px;
  font-weight: 400;
  overflow: hidden;
}

#unread-navigation a {
  color: #eee;
  width: 40px;
  display: block;
  cursor: pointer;
}

#unread-navigation a.disabled {
  color: #bbb;
}

#unread-navigation a:hover {
  background-color: #999;
}

#unread-navigation #un-prev {
  padding: 6px 0 8px 0;
}

#unread-navigation #un-prev:after {
}

#unread-navigation #un-next {
  padding: 6px 0 8px 0;
}

.b-comment.unread-current {
  background-color: #e1e7eb;
}`;


let html = `
<div id="unread-navigation">
  <a id="un-prev" class="disabled">
    <i class="icon-up-open"></i>
  </a>
  <a id="un-next" class="disabled">
    <i class="icon-down-open"></i>
  </a>
</div>`;


function initUI() {
  // Init CSS
  $('style').html(css).appendTo('head');

  // Init HTML
  unEl = $(html).appendTo('body');
  prevEl = $('#un-prev');
  nextEl = $('#un-next');

  // Init Events
  nextEl.on('click', next);
  prevEl.on('click', prev);

  if (options.scrollOver) {
    unEl.on('wheel', scrollOver);
  }
}


function updateUI() {
  if (position <= 0 && !options.circular) {
    prevEl.addClass('disabled');
    nextEl.removeClass('disabled');
  } else if (position == unreadEl.length - 1 && !options.circular) {
    nextEl.addClass('disabled');
    prevEl.removeClass('disabled');
  } else {
    prevEl.removeClass('disabled');
    nextEl.removeClass('disabled');
  }
}


function replaceRefreshCommentsFunction() {
  if (!$('.b-comments-refresh a').length) return;

  $('body').off('click', '.b-comments-refresh a');

  $('body').on('click', '.b-comments-refresh a', function(e) {

    e.preventDefault();
    e.stopImmediatePropagation();

    var commentsSort = $('.commentsSort .active > a').attr('data-type');
    loadComments(paperId, commentsSort, function(){
      if (supportsHtml5Storage()) {
        if (typeof localStorage["commentText"+paperId] != 'undefined' && localStorage["commentText"+paperId].length > 0) {
          $('#commentMessage').val(localStorage["commentText"+paperId]);
        }

        // REPLACED CODE
        unreadEl = $('.unread');

        if (unreadEl.length) {
          position = 0;
          
          if (!unEl) {
            initUI();
          }
        
          updateUI();
          scrollToUnread(position);
        }              
        // REPLACED CODE
      }
    });

  });

  // Popping up event in stack
  let stack = $._data($('body')[0], 'events')['click'];
  let event = stack.pop();
  stack.splice(0, 0, event);
}


function prev() {
  if (prevEl.hasClass('disabled')) return;

  if (position > 0) {
    position--;
  } else {
    if (options.circular) {
      position = unreadEl.length - 1;
    }
  }

  updateUI();
  scrollToUnread(position);
}


function next() {
  if (nextEl.hasClass('disabled')) return;

  if (position < unreadEl.length - 1) {
    position++;
  } else {
    if (options.circular) {
      position = 0;
    }
  }

  updateUI();
  scrollToUnread(position);
}


function scrollOver(event) {
  if (event.originalEvent.deltaY > 0) {
    next();
  } else {
    prev(); 
  }

  return false;
}


function scrollToUnread(number) {
  let comment = unreadEl.eq(number);
  let commentOffset = comment.offset();
  let commentHeight = comment.outerHeight();
  let windowHeight = $(window).height();
  let scrollPos = commentOffset.top + commentHeight - 
                  windowHeight + windowHeight / 100 * options.position;

  $('.unread-current').removeClass('unread-current');
  comment.addClass('unread-current');

  if (options.animate) {
    pageRoot.stop().animate({
      scrollTop: scrollPos
    }, options.animationSpeed);
  } else {
    $(window).scrollTop(scrollPos);
  }
}


function start() {
  replaceRefreshCommentsFunction();

  unreadEl = $('.unread');

  if (!unreadEl.length) return;

  initUI();

  updateUI();
}


// Runtime
let position = -1;
let unEl, prevEl, nextEl, unreadEl;

// Wait for comment builder script
setTimeout(start, 100);