GreasyFork Moderator Actions Log Viewer

to view GreasyFork Moderator Actions Log Table

2023-06-13 या दिनांकाला. सर्वात नवीन आवृत्ती पाहा.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey, Greasemonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्क्रिप्ट व्यवस्थापक एक्स्टेंशन इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्क्रिप्ट व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्टाईल व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

// ==UserScript==
// @name          GreasyFork Moderator Actions Log Viewer
// @namespace     http://tampermonkey.net/
// @version       0.2.4
// @description   to view GreasyFork Moderator Actions Log Table
// @author        CY Fung
// @match         https://greatest.deepsurf.us/*/moderator_actions*
// @icon          https://www.google.com/s2/favicons?sz=64&domain=greatest.deepsurf.us
// @grant         none
// @run-at        document-idle
// @license       MIT
// ==/UserScript==

(function () {
  'use strict';

  function formatDateToCustomFormat(date) {
    var year = date.getFullYear();
    var month = padZero(date.getMonth() + 1);
    var day = padZero(date.getDate());
    var hours = padZero(date.getHours());
    var minutes = padZero(date.getMinutes());
    var timeZoneOffset = getTimeZoneOffsetString();

    return year + '.' + month + '.' + day + ' ' + hours + ':' + minutes + ' (GMT' + timeZoneOffset + ')';
  }

  function padZero(value) {
    return value.toString().padStart(2, '0');
  }

  function getTimeZoneOffsetString() {
    var offsetMinutes = new Date().getTimezoneOffset();
    var sign = offsetMinutes > 0 ? '-' : '+';
    var offsetHours = Math.floor(Math.abs(offsetMinutes) / 60);

    return sign + offsetHours;
  }

  function removePrevTextNode(e, k) {
    let tn = e.previousSibling;
    if (tn && tn.nodeType === Node.TEXT_NODE) {
      if (tn.textContent.trim() === k) tn.remove();
    }
  }


  function setupTableContent() {

    for (const s of document.querySelectorAll('.log-table td:nth-child(1) relative-time:not(.jsm)')) {

      s.classList.add('jsm')

      let date = s.date;
      if (date) {

        let e = document.createElement('div');
        let q = formatDateToCustomFormat(date);
        q = q.split(' ');
        // e.textContent = formatDateToCustomFormat(date);
        e.className = 'date-entry';
        s.classList.add('jsm-hidden')
        s.after(e)

        e.appendChild(Object.assign(document.createElement('span'), {
          className: 'date-entry-date',

          textContent: q[0]
        }));

        e.appendChild(Object.assign(document.createElement('span'), {
          className: 'date-entry-time',

          textContent: q[1]
        }));

        e.appendChild(Object.assign(document.createElement('span'), {
          className: 'date-entry-gmt',
          textContent: q[2]
        }));
      }


    }


    for (const s of document.querySelectorAll('.log-table td:nth-child(3)')) {

        if(s.querySelector('a')) continue;

        let t = s.textContent;
        let m;
        t=t.replace(/O script (\d+) foi removido/g,(_,d)=>`Deleted script ${d}`);
        if(m=/([\s\S]*)\b(Deleted user)\s?(\d+)\b([\s\S]*)/i.exec(t)){
            m[1]=m[1].trim();
            m[4]=m[4].trim();
            s.innerHTML=`<a class="user-link deleted" href="/${document.documentElement.lang}/users/${m[3]}">${"Deleted User: "+m[3]}</a>`;
            if(m[1]) s.insertBefore(document.createTextNode(m[1]), s.firstChild);
            if(m[4]) s.appendChild(document.createTextNode(m[4]));
        }else if(m=/([\s\S]*)(Slettet skriptet|Skrip terhapus|ลบสคริปต์|Đã xoá script|סקריפט מחוק|Gelöschtes Skript|삭제된 스크립트|Script supprimé|Silinmiş script|Удалённый скрипт|刪除腳本|Verwijderd script|Изтрит скрипт|已删除的脚本|削除されたスクリプト|Διαγραμμένος κώδικας|Șterge script-ul|Script borrado|Deleted script|Script eliminato|Odstránený skript|Usunięto skrypt|أحذف السكربت)\s?(\d+)\b([\s\S]*)/i.exec(t)){
            m[1]=m[1].trim();

            m[4]=m[4].trim();
            s.innerHTML=`<a class="deleted" href="/${document.documentElement.lang}/scripts/${m[3]}">${"Deleted Script: "+m[3]}</a>`;
            if(m[1]) s.insertBefore(document.createTextNode(m[1]), s.firstChild);
            if(m[4]) s.appendChild(document.createTextNode(m[3]));
        }



    }



    for (const s of document.querySelectorAll('.log-table td:nth-child(3) a[href*="/scripts/"]:not(.jsm)')) {



      s.classList.add('jsm')
      let m = /\/scripts\/(\d+)/.exec(s.href);
      if (m) {
        let e = document.createElement('div');
        e.className = 'script-entry';
        s.replaceWith(e);
        e.appendChild(s);

        let span = document.createElement('span');
        span.className = 'entry-rid';
        span.textContent = m[1]
        e.prepend(span)
        removePrevTextNode(e, 'Script:');
      }


    }

    for (const s of document.querySelectorAll('.log-table td:nth-child(3) a[href*="/users/"]:not(.jsm)')) {



      s.classList.add('jsm')
      let m = /\/users\/(\d+)/.exec(s.href);
      if (m) {
        let e = document.createElement('div');
        e.className = 'user-entry';
        s.replaceWith(e);
        e.appendChild(s);

        let span = document.createElement('span');
        span.className = 'entry-rid';
        span.textContent = m[1]
        e.prepend(span)
        removePrevTextNode(e, 'User:');
      }


    }


    for (const s of document.querySelectorAll('.log-table td:nth-child(4)')) {

      convertToBadges(s);


    }


    for (const s of document.querySelectorAll('.log-table td:nth-child(5)')) {

      convertHyperlinks(s);


    }

  }
  function convertHyperlinks(elm) {
    var walker = document.createTreeWalker(elm, NodeFilter.SHOW_TEXT, null, false);

    while (walker.nextNode()) {
      var textNode = walker.currentNode;
      var parentNode = textNode.parentNode;

      var text = textNode.nodeValue.trim();
      if (text.length > 0 && parentNode.tagName !== 'A') {
        var match = text.match(/(https?:\/\/[^\s]+)/);

        if (match) {
          var link = document.createElement('a');
          link.href = match[0];
          link.textContent = match[0].replace('https://greatest.deepsurf.us/scripts/', 'scripts/');

          var before = document.createTextNode(text.substring(0, match.index));
          var after = document.createTextNode(text.substring(match.index + match[0].length));

          parentNode.insertBefore(before, textNode);
          parentNode.insertBefore(link, textNode);
          parentNode.insertBefore(after, textNode);

          parentNode.removeChild(textNode);
        }
      }
    }
  }

  function makeTextableBadge(tag, message, color) {

    let div = Object.assign(document.createElement('div'), {
      className: 'textable-div',
    })
    let img = Object.assign(document.createElement('img'), {
      src: `https://img.shields.io/badge/${tag}-${message}-${color}`
    });
    div.appendChild(img)
    div.appendChild(Object.assign(document.createElement('span'), {
      className: 'textable-span',
      textContent: `${tag}: ${message}`
    }))
    return div;

  }

  function convertToBadges(elm) {

    const converts = {
      'Ban': () => makeTextableBadge('action', 'ban', 'FF5C5C'),
      'Delete and lock': () => makeTextableBadge('action', 'delete', 'FF9933'),
      'Undelete': () => makeTextableBadge('action', 'undelete', '66CC66'),

    }

    var walker = document.createTreeWalker(elm, NodeFilter.SHOW_TEXT, null, false);

    while (walker.nextNode()) {
      var textNode = walker.currentNode;
      var parentNode = textNode.parentNode;

      var text = textNode.nodeValue.trim();
      if (text.length > 0 && parentNode.tagName !== 'A' && parentNode.tagName !== 'IMG') {
        let t = text.trim();
        if (converts[t]) textNode.replaceWith(converts[t]());
      }
    }
  }



  function convertToAdvancedTable(tableSelector) {

    setupTableContent();
    // Get the table element
    var table = document.querySelector(tableSelector);

    // Add classes to the table and its components
    table.classList.add('advanced-table');
    table.tHead.classList.add('advanced-table-head');
    table.tBodies[0].classList.add('advanced-table-body');

    // Get the table headers
    var headers = Array.from(table.tHead.rows[0].cells);

    var sortOrder = []; // Track sort order for each column

    // Add classes and event listeners to enable sorting
    headers.forEach(function (header, index) {
      header.classList.add('sortable');
      header.addEventListener('click', function (event) {
        if (!event.target.classList.contains('search-input')) {
          sortTable(table, index, sortOrder);
          sortOrder[index] = !sortOrder[index]; // Toggle sort order
        }
      });

      // Create search input element
      var searchInput = document.createElement('input');
      searchInput.setAttribute('type', 'text');
      searchInput.setAttribute('placeholder', 'Search');
      searchInput.classList.add('search-input');
      searchInput.addEventListener('input', function () {
        filterTable(table, index);
      });
      header.appendChild(searchInput);

      // Create sort icon element
      var sortIcon = document.createElement('span');
      sortIcon.classList.add('sort-icon');
      header.appendChild(sortIcon);
    });
  }

  // Function to sort the table by column index
  function sortTable(table, columnIndex, sortOrder) {
    var rows = Array.from(table.tBodies[0].rows);

    rows.sort(function (a, b) {
      var cellA = a.cells[columnIndex].textContent.toLowerCase();
      var cellB = b.cells[columnIndex].textContent.toLowerCase();

      if (sortOrder[columnIndex]) {
        // Sort in descending order
        if (cellA < cellB) return 1;
        if (cellA > cellB) return -1;
        return 0;
      } else {
        // Sort in ascending order
        if (cellA < cellB) return -1;
        if (cellA > cellB) return 1;
        return 0;
      }
    });

    table.tBodies[0].innerHTML = '';
    rows.forEach(function (row) {
      table.tBodies[0].appendChild(row);
    });
  }

  // Function to filter the table by column index
  function filterTable(table, columnIndex) {
    var filterValue = table.tHead.rows[0].cells[columnIndex].querySelector('.search-input').value.toLowerCase();
    var rows = Array.from(table.tBodies[0].rows);

    rows.forEach(function (row) {
      var cellValue = row.cells[columnIndex].textContent.toLowerCase();
      row.style.display = cellValue.includes(filterValue) ? '' : 'none';
    });
  }


  const colsize = (idx) => `.log-table th:nth-child(${idx}), .log-table td:nth-child(${idx}){width:${colsizes[idx - 1]}; max-width:${0};}`

  let colsizes = [28, 34, 120, 32, 82];
  let colsizeSum = colsizes.reduce((a, b) => a + b, 0);
  colsizes = colsizes.map(t => (t / colsizeSum * 100).toFixed(2) + '%');

  document.head.appendChild(document.createElement('style')).textContent = `
  .log-table.advanced-table {
    border-collapse:separate;
    border-spacing: 0 1em;
  }
  .log-table.advanced-table td img{
    display:block;
  }

.advanced-table-head th {
  position: relative;
  padding: 8px;
}

.sortable {
  cursor: pointer;
}

.sort-icon {
  position: absolute;
  top: 50%;
  right: 8px;
  transform: translateY(-50%);
  width: 8px;
  height: 8px;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  transition: transform 0.2s ease;
}

.sortable.asc .sort-icon {
  border-bottom: 4px solid #000;
}

.sortable.desc .sort-icon {
  border-top: 4px solid #000;
}

.search-input {
  width: 100%;
  box-sizing: border-box;
  padding: 4px;
  border: 1px solid #ccc;
  border-radius: 4px;
}



		${colsize(1)}
		${colsize(2)}
		${colsize(3)}
		${colsize(4)}
		${colsize(5)}

    .entry-rid{
      font-size:80%;
      font-family: 'Open Sans',sans-serif,"Segoe UI Emoji";
    }
    .date-entry{
      font-family: 'Open Sans',sans-serif,"Segoe UI Emoji";

    }

        .user-entry, .script-entry{
        display: flex;
        column-gap: 4px;
            place-items: center;
        }

        .script-entry a[href]{
          overflow: hidden;
          white-space: nowrap;
          max-width: 24em;
          text-overflow: ellipsis;
        }


/* Shared styles for both ".user-entry > .entry-rid" and ".script-entry > .entry-rid" */
.user-entry > .entry-rid,
.script-entry > .entry-rid {

    display: inline-flex;
    place-content: center;
  padding: 4px 8px;
  color: #fff; /* Set an appropriate white text color */
  border-radius: 8px; /* Set the desired border radius */
  transition: background-color 0.3s; /* Add transition effect */
  min-width: 4em;
}

/* Styles for ".user-entry > .entry-rid" */
.user-entry > .entry-rid {
  background-color: #4A90E2; /* Set your desired background color */
}

.user-entry > .entry-rid:hover {
  background-color: #77B5FF; /* Set your desired hover background color */
}

/* Styles for ".script-entry > .entry-rid" */
.script-entry > .entry-rid {
  background-color: #B146C2; /* Set your desired background color */
}

.script-entry > .entry-rid:hover {
  background-color: #D27BFF; /* Set your desired hover background color */
}

relative-time.jsm-hidden {
display:none;
}

.date-entry-date{

  display: inline-block;
  padding: 4px 8px;
  color: #fff; /* Set an appropriate white text color */
  border-radius: 8px; /* Set the desired border radius */
  transition: background-color 0.3s; /* Add transition effect */
  font-size:70%;
  background-color: #336699;
}


.date-entry-time{

  display: inline-block;
  padding: 4px 8px;
  color: #fff; /* Set an appropriate white text color */
  border-radius: 8px; /* Set the desired border radius */
  transition: background-color 0.3s; /* Add transition effect */
  font-size:70%;
  background-color: #663366;
}

.date-entry-gmt{

  display: inline-block;
  padding: 4px 8px;
  color: #fff; /* Set an appropriate white text color */
  border-radius: 8px; /* Set the desired border radius */
  transition: background-color 0.3s; /* Add transition effect */
  font-size:40%;
  background-color: #336633;

}


/*
.date-entry-gmt{

  padding: 2px 4px;
  border-radius:4px;
  }*/

  .date-entry{
  display: flex;
  flex-wrap:wrap;
  row-gap:2px;
  column-gap:2px;
      place-items: end;
  }


  .textable-div {
    display: inline-block;
  }
  .textable-span {
    position: fixed;
    left: -100vw;
    top: -100vh;
    transform: scale(0.001);
    font-size: 1pt;
    user-select: none !important;
    pointer-events: none !important;
    transform-origin: 0 0;
    z-index: -1;
  }

  .log-table .deleted {
    background: #aaa;
    color: #222;
    text-decoration: line-through underline;

  }

`

  setInterval(() => {

    let table = document.querySelector('table.log-table:not(.advanced-table)')
    if (table) {
      requestAnimationFrame(() => {
        if (table.classList.contains('advanced-table')) return;
        table = null;
        convertToAdvancedTable('table.log-table')
      });
    }

  }, 100);



  // Your code here...
})();