メール欄があるレスだけ表示

某画像掲示板でメール欄が入力されているレスのリストを表示します

目前為 2018-09-21 提交的版本,檢視 最新版本

// ==UserScript==
// @name     メール欄があるレスだけ表示
// @version  2.16
// @grant    none
// @include  https://*.2chan.net/*/res/*
// @description 某画像掲示板でメール欄が入力されているレスのリストを表示します
// @namespace https://gf.qytechs.cn/users/114367
// ==/UserScript==

// ----------------------------------------
// 設定
var CONFIG = {
  // ページ読み込み時の動作
  onPageLoaded: {
    // 自動起動する
    autoStart: true,
    // メールアドレスが0件でも「✉ x 0」と表示する
    show0Count: false,
    // n件以上あったら最小化して表示する
    countOfMailsToCompactMode: 5
  },
  // その他の設定
  // 除外するメールアドレスの正規表現
  ignoreRegex: /^sage$/
};

// ----------------------------------------
// CSS
var css = `
  /* ✉ボタン */
  .ML-toggle-btn {
    cursor: pointer;
  }
  .ML-toggle-btn-on {
    color: #00e;
  }
  /* リストの表示位置とか */
  .ML-list-container {
    bottom: 20px;
    display: block;
    font-size: 12px;
    position: fixed;
    right: 5px;
    transition: .2s;
    width: 300px;
    z-index: 99;
  }
  /* 件数表示 */
  .ML-count-label {
    cursor: pointer;
    display: block;
  }
  /* レス */
  @keyframes ML-fadein {
    0% { opacity: 0; }
    100% { opacity: 1; }
  }
  .ML-res {
    animation: ML-fadein .2s;
    background: #ccc9;
    border-radius: 5px;
    color: #800;
    margin: 5px;
    padding: 5px;
    position: relative;
  }
  .ML-titlebar {
    cursor: pointer;
  }
  /* メールアドレス */
  .ML-mail {
    color: #00f;
    cursor: pointer;
  }
  .ML-mail:hover {
    text-decoration: underline;
  }
  /* レス本文 */
  .ML-text-list {
    max-height: calc(25vh - 50px);
    overflow: auto;
  }
  .ML-text {
    animation: ML-fadein .5s;
    border-top: 1px dotted #0005;
    font-size: 90%;
    margin: 0;
    padding: 5px;
    width: auto;
  }
  /* レス最小化 */
  .ML-min {
    max-height: 16px;
    overflow: hidden;
  }
  .ML-min>.ML-text-list {
    display: none;
  }
  .ML-has-new {
    background: #bbea;
  }
  .ML-text-last {
    display: none;
    margin-left: 8px;
    max-height: 16px;
    overflow: hidden;
    pointer-events: none;
  }
  .ML-min>.ML-titlebar>.ML-text-last {
    display: inline;
  }
  /* リストOFF時 */
  .ML-list-off {
    opacity: 0;
    pointer-events: none;
  }
`;
style = document.createElement('STYLE');
style.type = 'text/css';
style.appendChild(document.createTextNode(css));
document.head.appendChild(style);

// ----------------------------------------
// Utils
var newElement = (tag, clazz, text) => {
  var e = document.createElement(tag);
  e.className = clazz;
  if (text) {
    e.textContent = text;
  }
  return e;
};

// ----------------------------------------
// Properties
var mails = [];
var resList = {};
var show0Count = CONFIG.onPageLoaded.show0Count;

// ----------------------------------------
// 全体の入れ物
var resListContainer = newElement('DIV', 'ML-list-container ML-list-off');
resListContainer.addEventListener('click', e => {
  if (e.target.parentNode.classList.contains('ML-min')) {
    e.target.parentNode.classList.remove('ML-min', 'ML-has-new');
    return;
  }
  if (e.target.classList.contains('ML-titlebar')) {
    e.target.parentNode.classList.add('ML-min');
    return;
  }
  if (e.target.classList.contains('ML-mail')) {
    resList[e.target.textContent].mailElement.scrollIntoView({ behavior: 'smooth'});
    return;
  }
});

// 件数表示部分
var countLabel = newElement('DIV', 'ML-res ML-count-label');
var refreshCountLabel = () => { countLabel.textContent = '✉ x ' + mails.length; };
var toggleCompactMode = e => {
  var resElements = document.getElementsByClassName('ML-res');
  if (e !== false) {
    // 全部最小化する
    var isUpdated = false;
    for (var res of resElements) {
      if (!res.classList.contains('ML-min') && !res.classList.contains('ML-count-label')) {
        res.classList.add('ML-min');
        isUpdated = true;
      }
    }
    if (isUpdated || e === true) return;
  }
  // 全部復元する
  for (var res of resElements) {
    res.classList.remove('ML-min', 'ML-has-new');
  }
};
countLabel.addEventListener('click', toggleCompactMode);
refreshCountLabel();

resListContainer.appendChild(countLabel);
document.body.appendChild(resListContainer);

// ----------------------------------------
// Main
var refleshList = isForceShowList => {
  var rtds = document.getElementsByClassName('rtd');
  var newRtds = [];
  for (var i = rtds.length - 1, newRtd; newRtd = rtds[i]; i --) {
    if (newRtd.getAttribute('data-mail-searched')) break;
    newRtd.setAttribute('data-mail-searched', '1');
    newRtds.unshift(newRtd);
  }
  var hasNewMail = false;
  newRtds.forEach(rtd => {
    var mailElement =
        rtd.querySelector('[href^="mailto:"]') || // default
        rtd.getElementsByClassName('KOSHIAN_meran')[0] || // kosian
        rtd.querySelector('[color="#005ce6"]'); // futakuro
    if (!mailElement) return;
    var mail = mailElement.href ? decodeURI(mailElement.href).replace('mailto:', '') : mailElement.textContent.replace(/^\[|\]$/g, '');
    if (mail.match(CONFIG.ignoreRegex)) return;
    var res = resList[mail];
    if (!res) {
      mails.push(mail);
      hasNewMail = true;
      res = {
        resContainer: newElement('DIV', 'ML-res'),
        textList: newElement('DIV', 'ML-text-list'),
        lastText: newElement('DIV', 'ML-text-last')
      };
      resList[mail] = res;
      var titlebar = newElement('DIV', 'ML-titlebar', ' ');
      titlebar.appendChild(newElement('SPAN', 'ML-mail', mail));
      titlebar.appendChild(res.lastText);
      res.resContainer.appendChild(titlebar);
      res.resContainer.appendChild(res.textList);
      resListContainer.insertBefore(res.resContainer, countLabel);
    }
    res.mailElement = mailElement;
    // BLOCKQUOTEタグを変なところにコピーすると一部のアドオンの動作がおかしくなるので本文はDIVにコピーする
    var text = newElement('DIV', 'ML-text');
    var bq = rtd.getElementsByTagName('BLOCKQUOTE')[0];
    var n = bq.firstChild;
    while (n) {
      if (!n.tagName || n.tagName !== 'INPUT' && n.tagName !== 'BUTTON') { // アドオンで追加されたUIは除外する
        text.appendChild(n.cloneNode(true));
      }
      n = n.nextSibling;
    }
    res.textList.appendChild(text);
    res.textList.scrollTo(0, res.textList.scrollHeight);
    if (res.resContainer.classList.contains('ML-min')) {
      res.resContainer.classList.add('ML-has-new');
    }
    res.lastText.textContent = text.textContent;
  });
  if (show0Count || mails.length) {
    if (hasNewMail) {
      refreshCountLabel();
    }
    resListContainer.classList.remove('ML-list-off');
  }
};

// ----------------------------------------
// DOMの監視
var timer;
var observer = new MutationObserver(rec => {
  clearTimeout(timer);
  // アドオンとかがNodeを弄るかもしれないので1秒くらい待つ
  timer = setTimeout(refleshList, 1000); // refleshListでNodeを追加するから2回動いちゃうけどまぁいっか!
});

// ----------------------------------------
// List ON OFF
var listOn = () => {
  refleshList();
  mailButton.classList.add('ML-toggle-btn-on');
  observer.observe(document.body, { childList: true, subtree: true });
};

var listOff = () => {
  observer.disconnect();
  resListContainer.classList.add('ML-list-off');
  mailButton.classList.remove('ML-toggle-btn-on');
};

var toggle = e => {
  if (resListContainer.classList.contains('ML-list-off')) {
    show0Count = true;
    listOn();
  } else {
    listOff();
  }
};

// [✉]ボタン
var mailButton = newElement('SPAN', 'ML-toggle-btn', '[✉]');
var addMailButton = () => {
  mailButton.addEventListener('click', toggle);
  var futakuroBorderArea = document.getElementById('border_area');
  if (futakuroBorderArea) {
    futakuroBorderArea.appendChild(mailButton);
  } else {
    document.getElementById('contres').insertBefore(mailButton, document.getElementById('contdisp').nextSibling);
  }
};

// ----------------------------------------
// Start HERE !
setTimeout(() => {
  addMailButton();
  if (CONFIG.onPageLoaded.autoStart) {
    listOn();
    if (CONFIG.onPageLoaded.countOfMailsToCompactMode <= mails.length) {
        toggleCompactMode(true);
    }
  }
}, 1000); // wait for futakuro

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址