V2EXcellent.js

A Better V2EX

// ==UserScript==
// @name           V2EXcellent.js
// @namespace      http://vitovan.github.io/v2excellent.js/
// @version        1.1.11
// @description    A Better V2EX
// @author         VitoVan
// @include        http*://*v2ex.com/*
// @require https://cdnjs.cloudflare.com/ajax/libs/markdown-it/8.4.2/markdown-it.min.js
// @grant          none
// ==/UserScript==

$('document').ready(function() {
  window.loaded = true;
});

var POST_PROCESS_FUNCS = [
  function done() {
    console.log('V2EXcellented!');
  },
];

//Fix night mode
var divWrapper = document.getElementById("Wrapper");
if(divWrapper.className == 'Night'){
    divWrapper.style.backgroundColor ='#00000000';
    divWrapper.style.backgroundImage ="url('/static/img/shadow.png'), url('//static.v2ex.com/bgs/pixels.png')";
}

// markdown-it 初始化
var md = window.markdownit({
    html: true,
    linkify: true,
    breaks: true,
    langPrefix: "hljs ",
    highlight: function (str, lang) {
    if (lang && hljs.getLanguage(lang)) {
      try {
        return hljs.highlight(lang, str).value;
      } catch (__) { }
    }
    return ''; // use external default escaping
  }
});

// 图片链接自动转换成图片 代码来自caoyue@v2ex
POST_PROCESS_FUNCS.push(function linksToImgs() {
  var links = document.links;
  for (var x in links) {
    var link = links[x];
    if (
      /^http.*\.(?:jpg|jpeg|jpe|bmp|png|gif)/i.test(link.href) &&
      !/<img\s/i.test(link.innerHTML)
    ) {
      link.innerHTML =
        "<img title='" + link.href + "' src='" + link.href + "' style='max-width:100%' />";
    }
  }
});


// 回复内容做markdown渲染
POST_PROCESS_FUNCS.push(function mdRender() {
   $(".reply_content").each(function(index, item) {
       var replyContent = $(item).html();
       replyContent = replyContent.replace(/<br\s*\/?>/gi,"\r\n");
       $(item).html(md.render(replyContent));
   });
});

function postProcess() {
  $(POST_PROCESS_FUNCS).each(function(i, f) {
    if (typeof f === 'function') {
      f();
      console.log('V2EXcellent Post Processing: ' + f.name);
    }
  });
}

var language = (
  window.navigator.userLanguage || window.navigator.language
).toLowerCase();

var currentLocation = location.href;
//If this is the thread page
if (currentLocation.match(/\/t\/\d+/g)) {
  //Enable Reply Directly Feature
  $('div.topic_buttons').append(
    ' &nbsp;<a " href="#;" onclick="$(\'#reply_content\').focus();" class="tb">回复</a>',
  );
  //Enable Img Uploader Feature
  enableUploadImg();
  var comments = [];
  //loading
  showSpinner();
  //Get comments from current page
  fillComments($('body'));
  //Get other pages comments
  var CURRENT_PAGE_URLS = [];
  $('a[href].page_normal').each(function(i, o) {
    if (CURRENT_PAGE_URLS.indexOf(o.href) === -1) {
      CURRENT_PAGE_URLS.push(o.href);
    }
  });
  var LEFT_PAGES_COUNT = CURRENT_PAGE_URLS.length;
  var CURRENT_PAGE = 0;
  var DOMS = [$(document)];
  if (LEFT_PAGES_COUNT > 0) {
    $(CURRENT_PAGE_URLS).each(function(i, o) {
      $.get(o, function(result) {
        var resultDom = $('<output>').append($.parseHTML(result));
        DOMS.push(resultDom);
        fillComments(resultDom);
        // 替换收藏的链接
        var collectUrl = resultDom
          .find('.topic_buttons .tb:contains("收藏")')
          .attr('href');
        $('.topic_buttons .tb:contains("收藏")').attr('href', collectUrl);
        CURRENT_PAGE++;
        //if all comments are sucked.
        if (CURRENT_PAGE === LEFT_PAGES_COUNT) {
          //stack'em
          stackComments();
          //reArrange
          reArrangeComments();
          // post process functions
          postProcess();
        }
      });
    });
  } else {
    stackComments();
    //reArrange
    reArrangeComments();
    // post process functions
    postProcess();
  }
  // Clear Default Pager
  $('a[href^="?p="]')
    .parents('div.cell')
    .remove();
} else if (currentLocation.match(/\/new/)) {
  $(
    '<a href="https://imgur.com/upload" target="_blank" style="padding:0 5px;">上传图片</a>',
  ).insertAfter($('button[onclick="previewTopic();"]'));
}

function jumpToReply() {
  var floorSpecArr = currentLocation.match(/#reply\d+/g);
  var floorSpec = floorSpecArr && floorSpecArr.length ? floorSpecArr[0] : false;
  if (floorSpec) {
    floorSpec = floorSpec.match(/\d+/g)[0];
    var specFloor = $('span.no').filter(function() {
      return $(this).text() === floorSpec;
    });
    var scrollFunc = function() {
      window.scrollTo(0, specFloor.offset().top - $('body').offset().top);
    };
    if (window.loaded) {
      scrollFunc();
    } else {
      window.onload = function() {
        setTimeout(function() {
          scrollFunc();
        }, 1);
      };
    }
  }
}

//Remove #reply42 from index
$('span.item_title>a').attr('href', function(i, val) {
  return val.replace(/#reply\d+/g, '');
});

function fillComments(jqDom) {
  jqDom.find('div[id^="r_"]').each(function(i, o) {
    var cmno = parseInt(
      $(o)
        .find('span.no')
        .text(),
    );
    comments[cmno] = {
      id: $(o).attr('id'),
      no: cmno,
      user: $(o)
        .find('strong>a')
        .text(),
      content: $(o)
        .find('div.reply_content')
        .text(),
      mentioned: (function() {
        var mentionedNames = [];
        $(o)
          .find('div.reply_content>a[href^="/member/"]:not("dark")')
          .each(function(i, o) {
            mentionedNames.push(o.innerHTML);
          });
        return mentionedNames;
      })(),
      subComments: [],
    };
  });
}

//Enable Floor Specification Feature
$('a[href="#;"]:has(img[alt="Reply"])').click(function(e) {
  var floorNo = $(e.currentTarget)
    .parent()
    .find('span.no')
    .text();
  replyContent = $('#reply_content');
  oldContent = replyContent.val().replace(/^#\d+ /g, '');
  postfix = ' ' + '#' + floorNo + ' ';
  newContent = '';
  if (oldContent.length > 0) {
    if (oldContent != postfix) {
      newContent = oldContent + postfix;
    }
  } else {
    newContent = postfix;
  }
  replyContent.focus();
  replyContent.val(newContent);
  moveEnd($('#reply_content'));
});

//Enable Gift ClickOnce Feature
$('a[href="/mission/daily"]')
  .attr('id', 'gift_v2excellent')
  .attr('href', '#')
  .click(function() {
    $('#gift_v2excellent').text('正在领取......');
    $.get('/mission/daily', function(result) {
      var giftLink = $('<output>')
        .append($.parseHTML(result))
        .find('input[value^="领取"]')
        .attr('onclick')
        .match(/\/mission\/daily\/redeem\?once=\d+/g)[0];
      $.get(giftLink, function(checkResult) {
        var okSign = $('<output>')
          .append($.parseHTML(checkResult))
          .find('li.fa.fa-ok-sign');
        if (okSign.length > 0) {
          $.get('/balance', function(result) {
            var amount = $('<output>')
              .append($.parseHTML(result))
              .find('table>tbody>tr:contains("每日登录"):first>td:nth(2)')
              .text();
            $('#gift_v2excellent').html(
              '已领取 <strong>' + amount + '</strong> 铜币。',
            );
            setTimeout(function() {
              $('#Rightbar>.sep20:nth(1)').remove();
              $('#Rightbar>.box:nth(1)').remove();
            }, 2000);
          });
        }
      });
    });
    return false;
  });

//Get comment's parent
function findParentComment(comment) {
  var parent;
  if (comment) {
    var floorRegex = comment.content.match(/#\d+ /g);
    if (floorRegex && floorRegex.length > 0) {
      var floorNo = parseInt(floorRegex[0].match(/\d+/g)[0]);
      parent = comments[floorNo];
    } else {
      for (var i = comment.no - 1; i > 0; i--) {
        var cc = comments[i];
        if (cc) {
          if (
            $.inArray(cc.user, comment.mentioned) !== -1 &&
            parent === undefined
          ) {
            parent = cc;
          }
          //If they have conversation, then make them together.
          if (
            comment.mentioned.length > 0 &&
            cc.user === comment.mentioned[0] &&
            cc.mentioned[0] === comment.user
          ) {
            parent = cc;
            break;
          }
        }
      }
    }
  }
  return parent;
}

//Stack comments, make it a tree
function stackComments() {
  for (var i = comments.length - 1; i > 0; i--) {
    var parent = findParentComment(comments[i]);
    if (parent) {
      parent.subComments.unshift(comments[i]);
      comments.splice(i, 1);
    }
  }
}

function getCommentDom(id) {
  var commentDom;
  $.each(DOMS, function(i, o) {
    var result = o.find('div[id="' + id + '"]');
    if (result.length > 0) {
      commentDom = result;
    }
  });
  return commentDom;
}

function moveComment(comment, parent) {
  if (comment) {
    var commentDom = getCommentDom(comment.id);
    $.each(comment.subComments, function(i, o) {
      moveComment(o, commentDom);
    });
    commentDom.appendTo(parent);
  }
}

function getCommentBox() {
  var commentBox = $('#Main>div.box:nth(1)');
  if (commentBox.length === 0) {
    // Maybe using mobile
    commentBox = $('#Wrapper>div.content>div.box:nth(1)');
    if ($('#v2excellent-mobile-tip').length === 0) {
      $(
        '<div class="cell" id="v2excellent-mobile-tip" style="background: #CC0000;font-weight: bold;text-align: center;"><span><a style="color:white;text-decoration:underline;" target="_blank" href="https://github.com/VitoVan/v2excellent.js/issues/7#issuecomment-304674654">About V2EXcellent.js on Mobile</a></span></div>',
      ).insertBefore('#Wrapper>div.content>div.box:nth(1)>.cell:first');
    }
  }
  return commentBox;
}

function showSpinner() {
  var commentBox = getCommentBox();
  $('body').append(
    '<style>.spinner{width:40px;height:40px;position:relative;margin:100px auto}.double-bounce1,.double-bounce2{width:100%;height:100%;border-radius:50%;background-color:#333;opacity:.6;position:absolute;top:0;left:0;-webkit-animation:sk-bounce 2.0s infinite ease-in-out;animation:sk-bounce 2.0s infinite ease-in-out}.double-bounce2{-webkit-animation-delay:-1.0s;animation-delay:-1.0s}@-webkit-keyframes sk-bounce{0%,100%{-webkit-transform:scale(0.0)}50%{-webkit-transform:scale(1.0)}}@keyframes sk-bounce{0%,100%{transform:scale(0.0);-webkit-transform:scale(0.0)}50%{transform:scale(1.0);-webkit-transform:scale(1.0)}}</style>',
  );
  $(
    '<div class="spinner"><div class="double-bounce1"></div><div class="double-bounce2"></div></div>',
  ).insertBefore(commentBox);
  commentBox.hide();
}

function reArrangeComments() {
  $('div.inner:has(a[href^="/t/"].page_normal)').remove();
  var commentBox = getCommentBox();
  $.each(comments, function(i, o) {
    moveComment(o, commentBox);
  });
  $('div[id^="r_"]>table>tbody>tr>td:first-child').attr('width', '20');
  $('td img.avatar').css('width', '');
  $('body').append(
    '<style>.cell{background-color: inherit;}.cell .cell{padding-bottom:0;border-bottom:none;min-width: 250px;padding-right:0;}div[id^="r_"] img.avatar{width:20px;height:20px;border-radius:50%;}div[id^="r_"]>div{margin-left: 5px;}</style>',
  );
  commentBox.show();
  //removeSpinner
  $('.spinner').remove();
  jumpToReply();
}

function enableUploadImg() {
  $('div.cell:contains("添加一条新回复")').append(
    '<div class="fr"><a href="https://imgur.com/upload" target="_blank"> 上传图片</a> - </div>',
  );
}