TGFC ban troll

让讨厌的苍蝇走开!屏蔽指定用户的主帖和回帖,感谢原作者 taxidriver、jun4rui

// ==UserScript==
// @name         TGFC ban troll
// @namespace    http://club.tgfcer.com/20060602
// @version      0.89
// @license      MIT
// @description  让讨厌的苍蝇走开!屏蔽指定用户的主帖和回帖,感谢原作者 taxidriver、jun4rui
// @author       20060602
// @supportURL	 housekeeper1997@gmail.com
// @include      http://*.tgfcer.com/*
// @include      http://*.tgfcer.net/*
// @include      https://*.tgfcer.com/*
// @include      https://*.tgfcer.net/*
// @grant        GM.xmlHttpRequest
// @grant        GM_info
// ==/UserScript==

//  console.log('Hello Tgfcer from "tgfc-ban-troll.js".');

//	global datas for storage
var BanList, BanListArray, ShowBanTip, BanTip, BanNegJisao, JisaoMin, BanQuote;
var CookieName = "TgfcBanTrollData";
var MenuTitle = "让TGFCER更美好的设置,由 taxidriver、jun4rui 两位坛友原创 v" + version();
var MenuText = 'TGGM<span style="font-size:.75em">(v' + version() + ')</span>'
var UserName = null;
var DownloadSuccessMessage = `数据下载成功,请:\n\n  点击"TGGM"关闭面板,保存到本地;或者\n  (不点"TGGM")刷新页面,继续用本地数据。`;
var UpDownTitle = "相同用户名的数据,可跨设备、浏览器、域名同步,\n纯手动,每次同步都需要先上传、再下载。\n免费存储服务,服务器数据可能不定期清除。";
var MagicVersion = "0.88";

// console.log('The Begin of logic.');
main();

function main() {
  Array.prototype.contains = contains;
  loadData();
  test();
  //console.log('Data loaded.');
  if (underWapUrls()) {
    processWap();
  } else if (underWebUrls()) {
    // console.log('gonna execute processWeb.');
    processWeb();
  }
}

function supportGMv4(){
  var nonSupportBrowsers = ["Via"];
  var handler = GM_info.scriptHandler;
  if (nonSupportBrowsers.includes(handler)){
    return false;
  }
  return true;
}

function version(){
  if(supportGMv4()){
    return GM_info.script.version;
  }
  return MagicVersion;
}

function getXHR(){
  if(supportGMv4()){
    return GM.xmlHttpRequest;
  }
  return null;//GM_xmlhttpRequest;
}

// UserName related start
function getUserName() {
  return UserName;
}

function setUserName(userName) {
  UserName = userName;
}
// UserName related end

function underWapUrls() {
  // console.log('Using new underWapUrls().');
  var wapUrls = [
    'http://club.tgfcer.com/wap/',
    'http://wap.tgfcer.com/',
    'http://club.tgfcer.net/wap/',
    'http://wap.tgfcer.net/',
    'https://club.tgfcer.com/wap/',
    'https://wap.tgfcer.com/',
    'https://club.tgfcer.net/wap/',
    'https://wap.tgfcer.net/',
    'http://s.tgfcer.com/wap/',
    'https://s.tgfcer.com/wap/',
    'http://s.tgfcer.net/wap/',
    'https://s.tgfcer.net/wap/'
  ];
  return underURLs(wapUrls);
}

function underWebUrls() {
  // console.log('Using new underWebUrls().');
  var webUrls = [
    'http://club.tgfcer.com/',
    'http://club.tgfcer.net/',
    'https://club.tgfcer.com/',
    'https://club.tgfcer.net/',
    'http://bbs.tgfcer.com/',
    'http://bbs.tgfcer.net/',
    'https://bbs.tgfcer.com/',
    'https://bbs.tgfcer.net/',
    'http://s.tgfcer.com/',
    'http://s.tgfcer.net/',
    'https://s.tgfcer.com/',
    'https://s.tgfcer.net/'
  ];
  return underURLs(webUrls);
}

//console.log('The End of logic.');

function getLocalStorage(name, defaultValue) {
  if (typeof (localStorage[name]) === 'undefined') {
    localStorage[name] = defaultValue;
    // console.log(name + ' initialed with:' + defaultValue);
  }
  return localStorage[name];
}

function postLoad() {
  BanListArray = BanList.split(',');
  JisaoMin = parseInt(localStorage.JisaoMin);
  var idxEmpty = BanListArray.indexOf('');
  if (idxEmpty !== -1) {
    BanListArray.splice(idxEmpty, 1);
  }
}

function loadData() {
  //先判断有没有localStorage保存的设置数据,没有则新建
  BanList = getLocalStorage('BanList', '');
  ShowBanTip = getLocalStorage('ShowBanTip', true) === 'true';
  BanTip = getLocalStorage('BanTip', 'Blocked!!!!!');
  BanNegJisao = getLocalStorage('BanNegJisao', false) === 'true';
  JisaoMin = getLocalStorage('JisaoMin', 0);
  BanQuote = getLocalStorage('BanQuote', false) === 'true';

  postLoad();
  // console.log(localStorage);
}

function getJsonValue(key, defaultValue, jobj) {
  if (!jobj.hasOwnProperty(key)) {
    jobj[key] = defaultValue;
  }
  return jobj[key];
}

function saveData(banList, showTip, banTip, banNegJisao, jisaoMin, banQuote) {
  BanList = banList;
  ShowBanTip = showTip;
  BanListArray = BanList.split(',');
  localStorage.BanList = BanList;
  localStorage.ShowBanTip = ShowBanTip;
  localStorage.BanTip = banTip;

  if (banNegJisao !== undefined) {
    localStorage.BanNegJisao = banNegJisao;
  }

  if (jisaoMin !== undefined) {
    localStorage.JisaoMin = jisaoMin;
  }

  if (banQuote !== undefined) {
    localStorage.BanQuote = banQuote;
  }

  BanTip = localStorage.BanTip;
}

function getJson(banList, showBanTip, banTip, banNegJisao, jisaoMin, banQuote) {
  json = {
    BanList: banList,
    ShowBanTip: showBanTip,
    BanTip: banTip,
    BanNegJisao: banNegJisao,
    JisaoMin: jisaoMin,
    BanQuote: banQuote
    // ,un: getUserName()
  };
  var jstr = JSON.stringify(json);
  return jstr;
}

function loadJson(jstr) {
  var jdata;
  try {
    jdata = JSON.parse(jstr);
  } catch (err) {
    console.log(err);
    return false;
  }
  if (!jdata.hasOwnProperty('BanList')) {
    return false;
  }
  BanList = getJsonValue('BanList', '', jdata);
  ShowBanTip = getJsonValue('ShowBanTip', true, jdata);
  BanTip = getJsonValue('BanTip', 'Blocked!!!!!', jdata);
  BanNegJisao = getJsonValue('BanNegJisao', false, jdata);
  JisaoMin = getJsonValue('JisaoMin', 0, jdata);
  BanQuote = getJsonValue('BanQuote', false, jdata);
  // console.log(getJsonValue('ShowBanTip', true, jdata));
  postLoad();
  return true;
}

function getTopLevelDomain() {
  var parts = document.domain.split('.');
  var l = parts.length;
  if (l < 2) {
    return document.domain;
  }
  return '.' + parts[l - 2] + '.' + parts[l - 1];
}

function instanceEditBanList(funcEdit) {
  loadData();
  funcEdit();
  saveData(BanListArray.join(','), ShowBanTip, BanTip, BanNegJisao, JisaoMin.toString(), BanQuote);
}

function removeFromBanList(username) {
  instanceEditBanList(function () {
    while (true) {
      var idx = BanListArray.indexOf(username);
      if (idx === -1) break;
      BanListArray.splice(idx, 1);
    }
  });
  location.reload();
}

function addToBanList(username) {
  // console.log("gonna remove username:" + username);
  // console.log(BanListArray);
  instanceEditBanList(function () {
    var idx = BanListArray.indexOf(username);
    if (idx !== -1) return;
    BanListArray.push(username);
  });
  // console.log(BanListArray);
  location.reload();
}


function processWap() {
  //不让图片尺寸超过屏幕的最大宽度,有时候图片太大了看起来好累
  addGlobalStyle('div.message>img {max-width:100%;}');
  addGlobalStyle('#tgbs button {padding:2px .8em;margin-right:.5em;}');
  //让顶部导航栏浮动固定
  addGlobalStyle('#scroller>.navbar {position:fixed;height:28px;line-height:28px;width:100%;top:0;left:0;box-shadow: 5px 1px 5px #888888;} body {padding-top:36px;}');
  addGlobalStyle('#scroller>.navigation {position:fixed;height:28px;line-height:28px;width:100%;top:0;left:0;box-shadow: 5px 1px 5px #888888;} body {padding-top:36px;}');

  addWapLink();

  //在原生导航栏中加入设置模块
  //console.log($('a[href="#bottom"]').parent().parent());
  var hookPoint = $('div.navbar');
  if (hookPoint.length === 0) {
    hookPoint = $('a[href="#bottom"]').parent().parent();
    hookPoint.append('<li><a href="#" class="nav_link" id="tgbs-btn" title="' + MenuTitle + '">' + MenuText + '</a></li>');
  } else {
    hookPoint.append('&nbsp;|&nbsp;<a href="#" class="nav_link" id="tgbs-btn" title="' + MenuTitle + '">' + MenuText + '</a>');
    hookPoint.css('z-index', 2);
  }
  //点击模块的处理
  $('#scroller').delegate('#tgbs-btn', 'click', function (e) {
    e.preventDefault();
    if ($('#tgbs').css('display') == 'none') {
      loadData();
      wapLoadUserName();
      $('#tgbs').css({ 'display': '' });
      // $('#tgbs').css('top', $('#tgbs-btn').position().top + 20);
      $('#tgbs').css('top', '32px');
      $('#tgbs').css('left', 2);
      $('#tgbs textarea').focus();
    } else {
      //关闭设置菜单时,关闭设置面板
      saveAndClose();
    }
  });
  function saveAndClose(){
      $('#tgbs').css({ 'display': 'none' });
      // 保存数据到localStorage
      savePanelData();
  }
  // save panel data
  function savePanelData() {
    BanList = $('#banlist-textarea').val();
    BanListArray = BanList.split(',');
    ShowBanTip = $("#showBanTip").prop('checked');
    BanTip = $('#ban-tip').val();
    BanQuote = $("#banQuote").prop('checked');
    saveData(BanList, ShowBanTip, BanTip, undefined, undefined, BanQuote);
  }
  // pop panel data
  function popWapPanelData(banList, showBanTip, banTip, banQuote,) {
    $('#banlist-textarea').val(banList);
    $("#showBanTip").prop('checked', showBanTip);
    $('#ban-tip').val(banTip);
    $("#banQuote").prop('checked', banQuote);
  }
  //在原生导航栏下面加入设置表单
  //$('div.navbar')
  hookPoint.append('<div id="tgbs" class="list_item_top" style="z-index:999;color:#f0f0f0;border-radius:.25em;width:356px;padding:.25em;position:fixed; display:none; overflow:hidden;box-shadow: rgb(51, 51, 51) 1px 1px 19px;background-color: #436193;">' +
    '<div style="vertical-align:bottom;">'+
    '<p style="float:left;margin-top:6px;">屏蔽ID列表:</p>' +
    '<p style="float:right;padding-bottom:2px;"><button id="save-close" style="margin-right:0;">Save & Close</button></p>' +
    '</div>' + '<div style="clear: both;"></div>' +
    '<textarea id="banlist-textarea" style="width:350px;height:160px;resize:vertical;padding:2px;">' + BanList + '</textarea>' +
    '<form><input id="showBanTip" type="checkbox" name="showBanTip" ' + (ShowBanTip ? "checked" : "") + ' /> 显示屏蔽提示&nbsp;|&nbsp;' +
    '提示信息&nbsp;<input id="ban-tip" style="font-size:1em;padding:2px;padding-left:4px;margin:0px;margin-top:5px;width:196px;" value="' + BanTip + '"/>' +
    '<hr/> <input id="banQuote" type="checkbox" name="banQuote" ' + (BanQuote ? "checked" : "") + ' /> 如果该用户位于屏蔽列表,屏蔽其被引用的发言' +
    '</form>' +
    '<hr/> <button id="sort-list">排序屏蔽列表</button>' +
    '<button id="download" title="' + UpDownTitle + '">下载</button>' +
    '<button id="upload" title="' + UpDownTitle + '">上传</button>' +
    '<button id="erase" title="' + UpDownTitle + '">擦除</button>' +
    '<hr> <a href="https://s.tgfcer.com/wap/index.php?action=my" style="color:#f0f0f0;">水区我的</a>' +
    '</div>');

  //点击屏蔽区将展开屏蔽内容
  $('#scroller').delegate('.list-ban-section', 'click', function (e) {
    e.preventDefault();
    var targetNode = $(this).parent().parent();
    if (targetNode.css('height') == '21px') {
      targetNode.css({ 'height': 'auto' });
    } else {
      targetNode.css({ 'height': '21px' });
    }
  });

  var btnSaveAndClose = document.getElementById("save-close");
  btnSaveAndClose.onclick = function (e) {
    e.preventDefault();
    saveAndClose();
  }

  var btnSortList = document.getElementById("sort-list");
  btnSortList.onclick = function (e) {
    e.preventDefault();
    var textareaBanList = document.getElementById('banlist-textarea');
    sortBanList(textareaBanList);
  }

  var btnDownload = document.getElementById("download");
  btnDownload.onclick = function (e) {
    e.preventDefault();
    fetchFromCloud((json) => {
      // console.log(json);
      popWapPanelData(json.BanList, json.ShowBanTip, json.BanTip, json.BanQuote);
      notify(DownloadSuccessMessage)
    });
  }

  var btnUpload = document.getElementById("upload");
  btnUpload.onclick = function (e) {
    e.preventDefault();
    pushToCloud((json) => {
      // console.log(json);
    });
  }

  var btnRemove = document.getElementById("erase");
  btnRemove.onclick = function(e){
    e.preventDefault();
    eraseFromCloud((json)=>{});
  }

  //列表页面
  var ForumPagePart = 'index.php?action=forum';
  //帖子内文页面
  var ThreadPagePart = 'index.php?action=thread';

  //如果当前页面是列表页面的处理
  if (hasURLPart(ForumPagePart)) {
    //console.log('当前在列表页面');
    $('.dTitle').each(function () {
      var author = $(this).find('span.author').text();
      for (var i in BanListArray) {
        //判断发帖人是否在屏蔽列表中
        if (author.indexOf(BanListArray[i]) == 1) {
          //console.log(BanListArray[i]);
          if (!ShowBanTip) {
            $(this).css({ display: 'none' });
            continue;
          }
          //console.log(author.indexOf(BanListArray[i]),BanListArray[i]);
          // $(this).addClass('list-ban-section');
          $(this).prepend('<div style="width:auto;text-align:center;border:1px dashed #AAAAAA;color:#AAAAAA; line-height:19px;"><a class="list-ban-section" href="#">查看标题</a> <strong><s> ' + BanListArray[i] + ' </s></strong>' + BanTip + ' <a class="remove-ban" href="#" value="' + BanListArray[i] + '">不再屏蔽</a>' + '</div>');
          $(this).css({ 'height': '21px', 'overflow': 'hidden' });
        }
      }
    });
  }

  var setDisplay = function (startNode, val) {
    startNode.css({ 'display': val });
    startNode.next().css({ 'display': val });
    startNode.next().next().css({ 'display': val });
    //        startNode.next().next().next().css({ 'display': val });
    //        startNode.next().next().next().next().css({ 'display': val });
  }
  $('#scroller').delegate('.info-ban-section', 'click', function (e) {
    e.preventDefault();
    if ($(this).parent().next().css('display') == 'none') {
      setDisplay($(this).parent().next(), 'inherit');
    } else {
      setDisplay($(this).parent().next(), 'none');
    }
  });
  $('#scroller').delegate('.remove-ban', 'click', function (e) {
    e.preventDefault();
    removeFromBanList($(this).attr('value'));
  });

  //如果当前页面是内容页的处理
  if (hasURLPart(ThreadPagePart)) {
    markJiSao();
    if (BanQuote) {
      filterQuote(BanListArray,
        function () { return document.getElementsByClassName("quote"); },
        function (node) { return node.getElementsByClassName("quote-bd"); },
        function (author, reason) {
          return author +
            '</s> ' + BanTip +
            reason;
        });
    }
    $('.infobar').each(function () {
      var author = $(this).find('a').eq(1).text();
      for (var i in BanListArray) {
        //判断发帖人是否在屏蔽列表中
        if (author == BanListArray[i]) {
          // console.log(author.indexOf(BanListArray[i]), BanListArray[i]);
          if (ShowBanTip) {
            $(this).before('<div style="width:auto;text-align:center;border:1px dashed #BCBCBC;color:#BCBCBC; line-height:19px;"><a class="info-ban-section" href="#">查看内容</a> <strong><s>' + author + '</s></strong>' + BanTip + ' <a class="remove-ban" href="#" value="' + author + '">不再屏蔽</a>' + '</div>');
          }
          //依次连续隐藏5个(含自己)元素
          setDisplay($(this), 'none');
        }
      }
      var authorA = $(this).find('a').eq(1);
      authorA.after(' <a class="ban-author" href="#" value="' + author + '">屏蔽</a> ');
    });
    $('#scroller').delegate('.ban-author', 'click', function (e) {
      e.preventDefault();
      addToBanList($(this).attr('value'));
    });
  }
}

function processWeb() {
  // console.log('processWeb begin');
  //   调整 “最后发表” 列的宽度,避免部分较长的 ID 导致此栏换行
  addGlobalStyle('.threadlist td.lastpost {width:160px;}');

  closeLeftAdv();
  // console.log('processWeb end');

  //在原生导航栏中加入设置模块
  var newSpan = document.createElement('span');
  newSpan.innerHTML = '<a href="#" class="nav_link" id="tgbs-btn" title="' + MenuTitle + '">' + MenuText + '</a>&nbsp;|&nbsp;';
  //  console.log(newSpan);
  var myTag = document.getElementById('my');
  setUserName(myTag.textContent);
  var hookPoint = myTag.parentNode.parentNode;
  //  console.log(hookPoint);
  hookPoint.appendChild(newSpan);
  //  console.log(navP);
  var btn = document.getElementById('tgbs-btn');
  //  console.log(btn);


  var floatDiv = createFloatDiv();
  newSpan.appendChild(floatDiv);

  var banlistTextarea = document.getElementById('ban-list');
  var showCheckbox = document.getElementById('show-ban-info');
  var banTip = document.getElementById('ban-tip');

  var banNegJisaoCheckbox = document.getElementById('ban-neg-jisao');
  var jisaoMin = document.getElementById('jisao-min');
  var banQuote = document.getElementById('ban-quote');

  //console.log(floatDiv);

  // 显示、隐藏tggm面板
  btn.onclick = function (e) {
    e.preventDefault();
    //  console.log('showCheckbox.checked:' + showCheckbox.checked + '    ShowBanTip:' + ShowBanTip);
    if (floatDiv.style.display === 'none') {
      loadData();
      floatDiv.style.display = '';
      floatDiv.style.top = getElementTop(newSpan) + 20 + 'px';
      floatDiv.style.left = getElementLeft(newSpan) - 365 + 'px';
      showCheckbox.checked = ShowBanTip;
      banlistTextarea.value = BanList;
      banlistTextarea.focus();
    } else {
      saveAndClose();
    }
  };

  function saveAndClose(){
      floatDiv.style.display = 'none';
      saveData(banlistTextarea.value, showCheckbox.checked, banTip.value, banNegJisaoCheckbox.checked, jisaoMin.value, banQuote.checked);
  }

  var btnSaveAndClose = document.getElementById('save-close');
  btnSaveAndClose.onclick = function(e){
    e.preventDefault();
    saveAndClose();
  }

  function addBanLink(cite, author) {
    cite.innerHTML += '<a class="ban-author" href="#" value="' + author + '">屏蔽</a>';
  }

  // 列表页面
  filterBlackList(
    function () { return document.getElementsByTagName('tbody'); },
    2,
    function (author, reason) {
      return '<tr><td style="background-color:#e5e5e5" class="folder"></td><td style="background-color:#e5e5e5" class="icon"></td><th class="" style="text-align:center;"><label></label><span>' +
        '<a class="show-thread-title" href="#">查看标题</a> ' +
        '<s>' + author + '</s> ' + BanTip + reason + ' <a class="remove-ban" href="#" value="' + author + '">不再屏蔽</a>' +
        '</span></th><td style="background-color:#e5e5e5;text-align:center" class="author"></td><td class="nums"></td><td style="background-color:#e5e5e5" class="lastpost"></td></tr>';
    }
  );

  // 内容页面
  filterBlackList(
    function () { return document.getElementsByClassName('viewthread'); },
    1,
    function (author, reason) {
      return '<table cellspacing="0" cellpadding="0"><tbody><tr><td class="postauthor"></td><td class="postcontent">' +
        '<a class="show-content" href="#">查看内容</a> <s>' +
        author +
        '</s> ' + BanTip +
        reason + ' <a class="remove-ban" href="#" value="' + author + '">不再屏蔽</a>' +
        '</td></tr></tbody></table></div>';
    },
    addBanLink
  );

  var contentA = document.getElementsByClassName("show-content");
  for (var i = 0; i < contentA.length; ++i) {
    var link = contentA[i];

    link.onclick = function (e) {
      e.preventDefault();
      var targetNode = this.parentElement.parentElement.parentElement.parentElement.parentElement.nextSibling;
      if (targetNode.style.display === 'none') {
        targetNode.style.display = 'block';
      } else {
        targetNode.style.display = 'none';
      }
    }
  }

  var titleA = document.getElementsByClassName("show-thread-title");
  for (i = 0; i < titleA.length; ++i) {
    link = titleA[i];
    link.onclick = function (e) {
      e.preventDefault();
      var targetNode = this.parentElement.parentElement.parentElement.parentElement.nextSibling;
      if (targetNode.style.display === 'none') {
        targetNode.style.display = 'table-row-group';
      } else {
        targetNode.style.display = 'none';
      }
    }
  }

  var removeBanA = document.getElementsByClassName("remove-ban");
  for (i = 0; i < removeBanA.length; ++i) {
    link = removeBanA[i];
    link.onclick = function (e) {
      e.preventDefault();
      console.log(this.getAttribute('value'));
      removeFromBanList(this.getAttribute('value'));
    }
  }

  var banA = document.getElementsByClassName("ban-author");
  for (i = 0; i < banA.length; ++i) {
    link = banA[i];
    link.onclick = function (e) {
      e.preventDefault();
      console.log(this.getAttribute('value'));
      addToBanList(this.getAttribute('value'));
    }
  }

  if (BanQuote) {
    filterQuote(BanListArray,
      function () { return document.getElementsByClassName('quote'); },
      function (node) { return node.getElementsByTagName('blockquote'); },
      function (author, reason) {
        return author +
          '</s> ' + BanTip +
          reason;
      });
  }

}



//添加全局CSS样式的方法
function addGlobalStyle(css) {
  var head, style;
  head = document.getElementsByTagName('head')[0];
  if (!head) { return; }
  style = document.createElement('style');
  style.type = 'text/css';
  style.innerHTML = css;
  head.appendChild(style);
}

function markJiSao() {
  //正激骚
  addGlobalStyle('a.positive-sao {color:#f00;}');
  //负激骚
  addGlobalStyle('a.negative-sao {color:#00bb00;}');

  var regex = /^骚\((-?\d+)\)$/g;
  $('a').each(function () {
    var atag = $(this);
    var match = regex.exec(atag.text());
    if (match && match[1] != '0') {
      //console.log(match[1]);
      if (match[1].indexOf('-') === 0) {
        atag.addClass("negative-sao");
      } else {
        atag.addClass("positive-sao");
      }
    }
  });
}


function addWapLink() {
  var webLink = /^http:\/\/club\.tgfcer\.com\/thread-([\d]+)-.+html/ig;
  var webLinkNet = /^http:\/\/club\.tgfcer\.net\/thread-([\d]+)-.+html/ig;
  var tidStr = 'http://wap.tgfcer.com/index.php?action=thread&tid=TidDummy&sid=&vt=1&tp=100&pp=100&sc=0&vf=0&sm=0&iam=&css=&verify=&fontsize=0';
  var tidStrNet = 'http://club.tgfcer.net/wap/index.php?action=thread&tid=TidDummy&sid=&vt=1&tp=100&pp=100&sc=0&vf=0&sm=0&iam=&css=&verify=&fontsize=0';

  var webLinkS = /^https:\/\/club\.tgfcer\.com\/thread-([\d]+)-.+html/ig;
  var webLinkNetS = /^https:\/\/club\.tgfcer\.net\/thread-([\d]+)-.+html/ig;
  var tidStrS = 'https://wap.tgfcer.com/index.php?action=thread&tid=TidDummy&sid=&vt=1&tp=100&pp=100&sc=0&vf=0&sm=0&iam=&css=&verify=&fontsize=0';
  var tidStrNetS = 'https://club.tgfcer.net/wap/index.php?action=thread&tid=TidDummy&sid=&vt=1&tp=100&pp=100&sc=0&vf=0&sm=0&iam=&css=&verify=&fontsize=0';

  var tags = document.getElementsByTagName('a');
  for (var i = 0; i < tags.length; ++i) {
    var tag = tags[i];
    tryConvert(tag, webLink, tidStr);
    tryConvert(tag, webLinkNet, tidStrNet);

    tryConvert(tag, webLinkS, tidStrS);
    tryConvert(tag, webLinkNetS, tidStrNetS);
    continue;
    var href = tag.href;
    var execResult = webLink.exec(href);
    if (execResult) {
      var threadId = execResult[1];
      var wapLink = tidStr.replace('TidDummy', threadId);
      //console.log(wapLink);
      var newSpan = document.createElement('span');
      newSpan.innerHTML = '&nbsp;&nbsp;<a href="' + wapLink + '" title="">(wap点我)</a>&nbsp;';
      tag.parentNode.insertBefore(newSpan, tag.nextSibling);
    }
  }
}

function tryConvert(aTag, regex, targetPattern) {
  var href = aTag.href;
  var execResult = regex.exec(href);
  if (execResult) {
    var threadId = execResult[1];
    var wapLink = targetPattern.replace('TidDummy', threadId);
    //console.log(wapLink);
    var newSpan = document.createElement('span');
    newSpan.innerHTML = '&nbsp;&nbsp;<a href="' + wapLink + '" title="">(wap点我)</a>&nbsp;';
    aTag.parentNode.insertBefore(newSpan, aTag.nextSibling);
  }
}

function getElementTop(element) {
  var actualTop = element.offsetTop;
  var current = element.offsetParent;
  while (current !== null) {
    actualTop += current.offsetTop;
    current = current.offsetParent;
  }
  return actualTop;
}

function getElementLeft(element) {
  var actualLeft = element.offsetLeft;
  var current = element.offsetParent;
  while (current !== null) {
    actualLeft += current.offsetLeft;
    current = current.offsetParent;
  }
  return actualLeft;
}

function popPanel(banList, showBanTip, banTip, banNegJisao, jisaoMin, banQuote) {
  var banlistTextarea = document.getElementById('ban-list');
  var showCheckbox = document.getElementById('show-ban-info');
  var banTipText = document.getElementById('ban-tip');
  var banNegJisaoCheckbox = document.getElementById('ban-neg-jisao');
  var jisaoMinNumber = document.getElementById('jisao-min');
  var banQuoteCheckBox = document.getElementById('ban-quote');

  banlistTextarea.value = banList;
  showCheckbox.checked = showBanTip;
  banTipText.value = banTip;
  banNegJisaoCheckbox.checked = banNegJisao;
  jisaoMinNumber.value = jisaoMin;
  banQuoteCheckBox.checked = banQuote;
}

function createFloatDiv() {
  var floatDiv = document.createElement('div');
  floatDiv.setAttribute('id', 'tgbs');
  floatDiv.setAttribute('style', 'color:#FFF;width:400px;border-radius:.25em;padding:.25em;position:fixed; display:none; overflow:hidden;box-shadow: rgb(51, 51, 51) 1px 1px 19px;background-color: #00b23d;text-align:left;');
  var titleText = document.createElement('div');
  titleText.innerHTML = '<p style="float:left;margin-top:8px;">屏蔽ID列表:<p><p style="float:right;margin-bottom:2px;"><button id="save-close">Save & Close</button><p>';
  floatDiv.appendChild(titleText);
  var banlistTextarea = document.createElement('textarea');
  banlistTextarea.setAttribute('id', 'ban-list');
  banlistTextarea.style.width = '98%';
  banlistTextarea.style.height = '160px';
  banlistTextarea.style.marginBottom = '4px';
  banlistTextarea.style.resize = 'vertical';
  banlistTextarea.value = BanList;
  floatDiv.appendChild(banlistTextarea);


  var form = document.createElement('form');
  floatDiv.appendChild(form);
  var showCheckbox = document.createElement('input');
  form.appendChild(showCheckbox);
  showCheckbox.setAttribute('type', 'checkbox');
  showCheckbox.setAttribute('id', 'show-ban-info');
  showCheckbox.checked = ShowBanTip;

  var checkText = document.createElement('span');
  checkText.innerHTML = '显示屏蔽提示&nbsp;&nbsp;|&nbsp;&nbsp;提示信息&nbsp;';
  form.appendChild(checkText);

  var banTip = document.createElement('input');
  form.appendChild(banTip);
  banTip.setAttribute('type', 'text');
  banTip.setAttribute('id', 'ban-tip');
  banTip.style.fontSize = '1em';
  banTip.style.padding = '0px 5px';
  banTip.style.margin = '0px';
  banTip.style.width = '200px';
  //  banTip.style.color = '#cc0000';
  banTip.value = BanTip;

  var lineBreak = document.createElement('hr');
  form.appendChild(lineBreak);

  var banNegJisaoCheckbox = document.createElement('input');
  form.appendChild(banNegJisaoCheckbox);
  banNegJisaoCheckbox.setAttribute('type', 'checkbox');
  banNegJisaoCheckbox.setAttribute('id', 'ban-neg-jisao');
  banNegJisaoCheckbox.checked = BanNegJisao;

  var checkTextBanNegJiSao = document.createElement('span');
  checkTextBanNegJiSao.innerHTML = '屏蔽,如果该用户激骚小于&nbsp;';
  form.appendChild(checkTextBanNegJiSao);

  var jisaoMin = document.createElement('input');
  form.appendChild(jisaoMin);
  jisaoMin.setAttribute('type', 'number');
  jisaoMin.setAttribute('id', 'jisao-min');
  jisaoMin.style.fontSize = '1em';
  jisaoMin.style.padding = '0px 5px';
  jisaoMin.style.margin = '0px';
  jisaoMin.style.width = '112px';
  //  jisaoMin.style.color = '#cc0000';
  jisaoMin.value = JisaoMin;

  lineBreak = document.createElement('hr');
  form.appendChild(lineBreak);

  var banQuoteCheckbox = document.createElement('input');
  form.appendChild(banQuoteCheckbox);
  banQuoteCheckbox.setAttribute('type', 'checkbox');
  banQuoteCheckbox.setAttribute('id', 'ban-quote');
  banQuoteCheckbox.checked = BanQuote;

  var checkTextBanQuote = document.createElement('span');
  checkTextBanQuote.innerHTML = '如果该用户位于屏蔽列表,屏蔽其被引用的发言';
  form.appendChild(checkTextBanQuote);

  lineBreak = document.createElement('hr');
  form.appendChild(lineBreak);

  function createButton(caption, onclick, id = null) {
    var btn = document.createElement('BUTTON');
    var text = document.createTextNode(caption); // Create a text node
    btn.appendChild(text); // Append the text to <button>
    btn.style.marginRight = '0.4em';
    if (id)
      btn.setAttribute('id', id);
    form.appendChild(btn);
    if (onclick)
      btn.onclick = onclick;
    return btn;
  }

  function jisaoEditable(e) {
    e?.preventDefault();
    var taReason = document.getElementsByName('reason');
    //console.log(taReason);
    if (taReason) {
      for (var i = 0, len = taReason.length; i < len; i++) {
        var ta = taReason[i];
        ta.removeAttribute('readonly');
      }
    }

    jisaoNoPM();
    return false;
  }
  var btnJisaoEdit = createButton('让“激骚理由”可编辑', jisaoEditable);

  function sortList(e) {
    e.preventDefault();
    sortBanList(banlistTextarea);
  }
  var btnSortBanList = createButton('排序屏蔽列表', sortList);

  function downloadData(e) {
    e.preventDefault();
    fetchFromCloud((json) => {
      console.log(json);
      popPanel(json.BanList, json.ShowBanTip, json.BanTip, json.BanNegJisao, json.JisaoMin, json.BanQuote);
      notify(DownloadSuccessMessage)
    });
  }
  var btnDownload = createButton('下载', downloadData, 'btn-download');
  btnDownload.setAttribute('title', UpDownTitle);

  function uploadData(e) {
    e.preventDefault();
    pushToCloud((json) => {
      // console.log(json);
    });
  }
  var btnUpload = createButton('上传', uploadData, 'btn-upload');
  btnUpload.setAttribute('title', UpDownTitle);

  function eraseData(e) {
    e.preventDefault();
    eraseFromCloud((json) => {
      // console.log(json);
    });
  }
  var btnErase = createButton('擦除', eraseData, 'btn-erase');
  btnErase.setAttribute('title', UpDownTitle);

  return floatDiv;
}

function jisaoNoPM(event) {
  var taReason = document.getElementsByName('sendreasonpm');
  //console.log(taReason);
  if (taReason) {
    for (var i = 0, len = taReason.length; i < len; i++) {
      var ta = taReason[i];
      ta.removeAttribute('disabled');
    }
  }

  //event.preventDefault();
  return false;
}

function sortBanList(textareaBanList) {
  function onlyUnique(value, index, self) {
    return value && self.indexOf(value) === index;
  }
  function startWithASCII(s) {
    if (s.length > 0) {
      var code = s.charCodeAt(0);
      return code < 256;
    }
    return false;
  }
  function pinyinCompare(a, b) {
    var aIsASCII = startWithASCII(a);
    var bIsASCII = startWithASCII(b);
    if (aIsASCII == bIsASCII) {
      return a.localeCompare(b, "zh");
    }
    if (aIsASCII) {
      return -1;
    }
    return 1;
  }

  BanListArray.sort(pinyinCompare);
  BanListArray = BanListArray.filter(onlyUnique);
  BanList = BanListArray.join(',');
  // console.log(BanList);
  textareaBanList.value = BanList;
}

function banReason(node, cite, author) {
  if (cite[0].getElementsByTagName('a')[0] == null) {
    return null;
  }

  author = cite[0].getElementsByTagName('a')[0].innerHTML;
  if (BanListArray.contains(author)) {
    return '';
  }

  if (BanNegJisao) {
    var dl = node.getElementsByTagName('dl');
    if (dl && dl.length > 0) {
      dl = dl[0];
      var dds = dl.getElementsByTagName('dd');
      var jisaoText = dds[3].innerText;
      var jisao = parseInt(jisaoText);
      //                     console.log(jisao);
      if (jisao < JisaoMin) {
        return ' 激骚值:' + jisao;
      }
    }
  }

  return null;
}

function filterBlackList(nodeFunc, citeCount, tipFunc, citeFunc = null) {
  var allTextareas, cite, author;
  allTextareas = nodeFunc();
  // console.log(allTextareas);
  if (!allTextareas.length) {
    return;
  }

  var nodesToProcess = [];
  for (var index = 0; index < allTextareas.length; index++) {
    var node = allTextareas[index];
    cite = node.getElementsByTagName('cite');
    if (cite.length < citeCount) {
      continue;
    }

    var mainCite = cite[0];
    author = mainCite.getElementsByTagName('a')[0].innerHTML;
    if (citeFunc) {
      citeFunc(mainCite, author);
    }

    //console.log(author);
    var reason = banReason(node, cite, author);
    if (reason !== null) {
      // can't insert node in for loop, process later
      nodesToProcess.push(node);
    }
  }

  nodesToProcess.forEach(function (node) {
    var cite = node.getElementsByTagName('cite');
    var author = cite[0].getElementsByTagName('a')[0].innerHTML;
    var reason = banReason(node, cite, author);
    if (ShowBanTip) {
      var tipNode = node.cloneNode(false);
      if (tipNode.id !== null) {
        console.log(tipNode.id)
        tipNode.id = tipNode.id + "-shadow";
      }
      tipNode.innerHTML = tipFunc(author, reason);
      node.parentNode.insertBefore(tipNode, node);
      node.style.display = 'none';
    } else {
      node.style.display = 'none';
    }
  });


}

var BqStart = undefined;

function isQuoteBanned(array, quoteText) {
  if (BqStart === undefined) {
    BqStart = {}
    array.forEach(elem => { BqStart["原帖由 @" + elem] = elem });
    array.forEach(elem => { BqStart["原帖由 " + elem] = elem });
  }

  for (var key in BqStart) {
    if (BqStart.hasOwnProperty(key)) {
      if (quoteText.startsWith(key)) {
        return BqStart[key];
      }
    }
  }

  return null;
}

function filterQuote(banListArray, nodeFunc, bqFunc, tipFunc) {
  var allTextareas, blockquote, author;
  allTextareas = nodeFunc();
  // console.log(allTextareas.length);
  if (!allTextareas.length) {
    return;
  }

  for (var index = 0; index < allTextareas.length; index++) {
    var node = allTextareas[index];
    blockquote = bqFunc(node);
    if (blockquote.length <= 0) {
      continue;
    }

    // console.log(blockquote);
    author = isQuoteBanned(banListArray, blockquote[0].innerText);
    // console.log("got author: " + author);
    var inBanList = author !== null;
    // console.log("inBanList = " + inBanList);
    if (!inBanList) {
      continue;
    }

    //console.log(author);
    var reason = " (勾选屏蔽)";
    if (reason !== null) {
      if (ShowBanTip) {
        var div = document.createElement("div");
        div.appendChild(createReadA());
        div.appendChild(crerateTip(author, reason));
        div.appendChild(createRemoveA(author));
        node.prepend(div);
        setDisplay(div.nextSibling, 'none');
      } else {
        var br = document.createElement("br");
        node.parentNode.insertBefore(br, node);
        node.style.display = 'none';
      }

      function setDisplay(targetNode, val) {
        targetNode.style.display = val;
        if (targetNode.nextSibling) {
          targetNode.nextSibling.style.display = val;
        }
      }
      function createReadA() {
        var readA = document.createElement("a");
        var text = document.createTextNode("查看内容");
        readA.appendChild(text);
        readA.href = "#"
        readA.onclick = function (e) {
          e.preventDefault();
          var targetNode = this.parentElement.nextSibling;
          if (targetNode.style.display == 'none') {
            setDisplay(targetNode, 'block');
          } else {
            setDisplay(targetNode, 'none');
          }
        };
        return readA;
      }

      function crerateTip(a, r) {
        var span = document.createElement('span');
        span.innerHTML = ' <s>' + tipFunc(a, r) + ' ';
        return span;
      }

      function createRemoveA(a) {
        var removeA = document.createElement("a");
        var text = document.createTextNode("不再屏蔽");
        removeA.appendChild(text);
        removeA.href = "#"
        removeA.onclick = function (e) {
          e.preventDefault();
          removeFromBanList(a);
        };
        return removeA;
      }
    }

  }
}

function underURLs(urls) {
  //console.log('underURLs begin')
  var PageCurrent = window.location.href;

  var result = false;
  for (var i = 0; i < urls.length; i++) {
    var prefix = urls[i];
    if (PageCurrent.indexOf(prefix) === 0) {
      result = true;
      break;
    }
  }

  //console.log('underURL returned with: ' + result);
  return result;
}

function hasURLPart(part) {
  var PageCurrent = window.location.href;
  return PageCurrent.indexOf(part) >= 0;
}

function contains(obj) {
  var index = this.length;
  while (index--) {
    if (this[index] === obj) {
      return true;
    }
  }
  return false;
}

function closeLeftAdv() {
  if (true) {
    return;
  }
  console.log('closeLeftAdv begin');
  writeCookie('leftadv1', '1', 700);
  document.getElementById('leftadv').style.display = 'none';
  document.getElementById('content_main').style.margin = '0 0 0 0';
  console.log('closeLeftAdv end');
}

function wapLoadUserName(callback = null) {
  if (getUserName() !== null) {
    // already fetched
    if (callback != null) {
      callback(getUserName());
    }
    return;
  }
  var aTag = Array.from(document.querySelectorAll("#footer a")).find(a => a.textContent == "我的")
  // console.log(aTag);
  if (!aTag) return;
  var url = aTag.href;
  var xhr = getXHR();
  if (xhr===null){
    return;
  }
  xhr({
    method: "GET",
    url: url,
    onload: function (response) {
      var parser = new DOMParser();
      var doc = parser.parseFromString(response.responseText, "text/html");
      var a = Array.from(doc.querySelectorAll('#scroller a')).find(a => a.textContent === '>>web')
      var un = a.parentElement.getElementsByTagName('b')[0].textContent;

      setUserName(un);
      if (callback != null) {
        callback(getUserName());
      }
      // console.log(`UserName is: ${getUserName()}`)
    }
  });
}

//
//  test function to test newly developing feature
//
async function test() {
}

function notify(message) {
  alert(message);
}

// cloud storage related start
var SALT = "fd6ca0ea-5bad-438e-8be5-be26e7d9ead0";
async function genKey(userStr) {
  // console.log(`gonna genKey for userStr: ${userStr}`);
  var key = await sha256(userStr + SALT);
  return key;
}

function UrlBase() {
  // return "http://localhost:5000/kv";
  // return "http://localhost:3000/kv";
  // return "https://housekeeper1997.pythonanywhere.com/kv";
  return "https://kvlite.vercel.app/kv";
}
async function fetchFromCloud(callback) {
  var userKey = await genKey(getUserName());
  // console.log(`gonna fetch for userKey: ${userKey}`);
  var xhr = getXHR();
  if (xhr===null){
    alert("Browser not support:\n  GM.xmlHttpRequest!");
    return;
  }
  xhr({
    method: "GET",
    url: UrlBase() + "/" + userKey,
    onload: function (response) {
      // console.log(response.responseText);
      var data = JSON.parse(response.responseText)
      if (!data.hasOwnProperty('value')) {
        notify(`下载操作结果:${data.result}(${data.desc})`);
        return;
      }
      var time = new Date(parseFloat(data.time) * 1000)
      // console.log(`last upload time: ${time}`);
      var jobj = JSON.parse(data.value);
      callback(jobj);
    },
    onerror: function (response) {
      notify(`下载操作结果:失败(数据未传输)`);
    }
  });
}

async function pushDataToCloud(callback, data, opName) {
  var userKey = await genKey(getUserName());
  var banData = data;
  // console.log(banData);
  var data = { key: userKey, value: banData };
  // console.log(data);

  var xhr = getXHR();
  if (xhr===null){
    alert("Browser not support:\n  GM.xmlHttpRequest!");
    return;
  }
  xhr({
    method: "POST",
    url: UrlBase(),
    data: JSON.stringify(data),
    headers: {
      "Content-Type": "application/json"
    },
    onload: function (response) {
      // console.log(response.responseText);
      try {
        var json = JSON.parse(response.responseText);
        notify(`${opName}操作结果:${json.result}(${json.desc})`);
        // console.log(json);
        callback(json);
      } catch (SyntaxError) {
        notify(`${opName}操作结果:失败(服务器内部错误)`);
      }
    },
    onerror: function (response) {
      notify(`${opName}操作结果:失败(数据未传输)`);
    }
  });
}

async function pushToCloud(callback) {
  var banData = getJson(BanList, ShowBanTip, BanTip, BanNegJisao, JisaoMin, BanQuote);
  await pushDataToCloud(callback, banData, "上传");
}

async function eraseFromCloud(callback) {
  await pushDataToCloud(callback, "{}", "擦除");
}


// cloud storage related end

// digest functions
async function sha1(d) { return await digest(d, "SHA-1"); }
async function sha256(d) { return await digest(d, 'SHA-256'); }
async function digest(data, algorithm) {
  // console.log(`gonna digest for data: ${data} with algorithm: ${algorithm}`);
  // encode as UTF-8
  const msgBuffer = new TextEncoder('utf-8').encode(data);

  // hash the message
  const hashBuffer = await window.crypto.subtle.digest(algorithm, msgBuffer);

  // convert ArrayBuffer to Array
  const hashArray = Array.from(new Uint8Array(hashBuffer));

  // convert bytes to hex string
  const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('');
  // console.log(hashHex);
  return hashHex;
}