GreasyFork Moderator Actions Log Viewer

to view GreasyFork Moderator Actions Log Table

目前为 2023-05-23 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name GreasyFork Moderator Actions Log Viewer
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1.0
  5. // @description to view GreasyFork Moderator Actions Log Table
  6. // @author CY Fung
  7. // @match https://greasyfork.org/*/moderator_actions*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=greasyfork.org
  9. // @grant none
  10. // @run-at document-idle
  11. // @license MIT
  12. // ==/UserScript==
  13.  
  14. (function () {
  15. 'use strict';
  16.  
  17. function formatDateToCustomFormat(date) {
  18. var year = date.getFullYear();
  19. var month = padZero(date.getMonth() + 1);
  20. var day = padZero(date.getDate());
  21. var hours = padZero(date.getHours());
  22. var minutes = padZero(date.getMinutes());
  23. var timeZoneOffset = getTimeZoneOffsetString();
  24.  
  25. return year + '.' + month + '.' + day + ' ' + hours + ':' + minutes + ' (GMT' + timeZoneOffset + ')';
  26. }
  27.  
  28. function padZero(value) {
  29. return value.toString().padStart(2, '0');
  30. }
  31.  
  32. function getTimeZoneOffsetString() {
  33. var offsetMinutes = new Date().getTimezoneOffset();
  34. var sign = offsetMinutes > 0 ? '-' : '+';
  35. var offsetHours = Math.floor(Math.abs(offsetMinutes) / 60);
  36.  
  37. return sign + offsetHours;
  38. }
  39.  
  40.  
  41.  
  42. for (const s of document.querySelectorAll('.log-table td:nth-child(1) relative-time:not(.jsm)')) {
  43.  
  44. s.classList.add('jsm')
  45.  
  46. let date = s.date;
  47. if (date) {
  48.  
  49. let e = document.createElement('div');
  50. let q = formatDateToCustomFormat(date);
  51. q = q.split(' ');
  52. // e.textContent = formatDateToCustomFormat(date);
  53. e.className = 'date-entry';
  54. s.classList.add('jsm-hidden')
  55. s.after(e)
  56.  
  57. e.appendChild(Object.assign(document.createElement('span'), {
  58. className: 'date-entry-date',
  59.  
  60. textContent: q[0]
  61. }));
  62.  
  63. e.appendChild(Object.assign(document.createElement('span'), {
  64. className: 'date-entry-time',
  65.  
  66. textContent: q[1]
  67. }));
  68.  
  69. e.appendChild(Object.assign(document.createElement('span'), {
  70. className: 'date-entry-gmt',
  71. textContent: q[2]
  72. }));
  73. }
  74.  
  75.  
  76. }
  77.  
  78.  
  79. for (const s of document.querySelectorAll('.log-table td:nth-child(3) a[href*="/scripts/"]:not(.jsm)')) {
  80.  
  81.  
  82.  
  83. s.classList.add('jsm')
  84. let m = /\/scripts\/(\d+)/.exec(s.href);
  85. if (m) {
  86. let e = document.createElement('div');
  87. e.className = 'script-entry';
  88. s.replaceWith(e);
  89. e.appendChild(s);
  90.  
  91. let span = document.createElement('span');
  92. span.className = 'entry-rid';
  93. span.textContent = m[1]
  94. e.prepend(span)
  95. }
  96.  
  97.  
  98. }
  99.  
  100.  
  101. for (const s of document.querySelectorAll('.log-table td:nth-child(3) a[href*="/users/"]:not(.jsm)')) {
  102.  
  103.  
  104.  
  105. s.classList.add('jsm')
  106. let m = /\/users\/(\d+)/.exec(s.href);
  107. if (m) {
  108. let e = document.createElement('div');
  109. e.className = 'user-entry';
  110. s.replaceWith(e);
  111. e.appendChild(s);
  112.  
  113. let span = document.createElement('span');
  114. span.className = 'entry-rid';
  115. span.textContent = m[1]
  116. e.prepend(span)
  117. }
  118.  
  119.  
  120. }
  121.  
  122.  
  123. function convertToAdvancedTable(tableSelector) {
  124. // Get the table element
  125. var table = document.querySelector(tableSelector);
  126.  
  127. // Add classes to the table and its components
  128. table.classList.add('advanced-table');
  129. table.tHead.classList.add('advanced-table-head');
  130. table.tBodies[0].classList.add('advanced-table-body');
  131.  
  132. // Get the table headers
  133. var headers = Array.from(table.tHead.rows[0].cells);
  134.  
  135. var sortOrder = []; // Track sort order for each column
  136.  
  137. // Add classes and event listeners to enable sorting
  138. headers.forEach(function (header, index) {
  139. header.classList.add('sortable');
  140. header.addEventListener('click', function (event) {
  141. if (!event.target.classList.contains('search-input')) {
  142. sortTable(table, index, sortOrder);
  143. sortOrder[index] = !sortOrder[index]; // Toggle sort order
  144. }
  145. });
  146.  
  147. // Create search input element
  148. var searchInput = document.createElement('input');
  149. searchInput.setAttribute('type', 'text');
  150. searchInput.setAttribute('placeholder', 'Search');
  151. searchInput.classList.add('search-input');
  152. searchInput.addEventListener('input', function () {
  153. filterTable(table, index);
  154. });
  155. header.appendChild(searchInput);
  156.  
  157. // Create sort icon element
  158. var sortIcon = document.createElement('span');
  159. sortIcon.classList.add('sort-icon');
  160. header.appendChild(sortIcon);
  161. });
  162. }
  163.  
  164. // Function to sort the table by column index
  165. function sortTable(table, columnIndex, sortOrder) {
  166. var rows = Array.from(table.tBodies[0].rows);
  167.  
  168. rows.sort(function (a, b) {
  169. var cellA = a.cells[columnIndex].textContent.toLowerCase();
  170. var cellB = b.cells[columnIndex].textContent.toLowerCase();
  171.  
  172. if (sortOrder[columnIndex]) {
  173. // Sort in descending order
  174. if (cellA < cellB) return 1;
  175. if (cellA > cellB) return -1;
  176. return 0;
  177. } else {
  178. // Sort in ascending order
  179. if (cellA < cellB) return -1;
  180. if (cellA > cellB) return 1;
  181. return 0;
  182. }
  183. });
  184.  
  185. table.tBodies[0].innerHTML = '';
  186. rows.forEach(function (row) {
  187. table.tBodies[0].appendChild(row);
  188. });
  189. }
  190.  
  191. // Function to filter the table by column index
  192. function filterTable(table, columnIndex) {
  193. var filterValue = table.tHead.rows[0].cells[columnIndex].querySelector('.search-input').value.toLowerCase();
  194. var rows = Array.from(table.tBodies[0].rows);
  195.  
  196. rows.forEach(function (row) {
  197. var cellValue = row.cells[columnIndex].textContent.toLowerCase();
  198. row.style.display = cellValue.includes(filterValue) ? '' : 'none';
  199. });
  200. }
  201.  
  202.  
  203. const colsize = (idx) => `.log-table th:nth-child(${idx}), .log-table td:nth-child(${idx}){width:${colsizes[idx - 1]}; max-width:${colsizes[idx - 1]};}`
  204.  
  205. let colsizes = [8, 10, 30, 12, 25];
  206. let colsizeSum = colsizes.reduce((a, b) => a + b, 0);
  207. colsizes = colsizes.map(t => (t / colsizeSum * 100).toFixed(2) + '%');
  208.  
  209. document.head.appendChild(document.createElement('style')).textContent = `
  210. .advanced-table-head th {
  211. position: relative;
  212. padding: 8px;
  213. }
  214.  
  215. .sortable {
  216. cursor: pointer;
  217. }
  218.  
  219. .sort-icon {
  220. position: absolute;
  221. top: 50%;
  222. right: 8px;
  223. transform: translateY(-50%);
  224. width: 8px;
  225. height: 8px;
  226. border-left: 4px solid transparent;
  227. border-right: 4px solid transparent;
  228. transition: transform 0.2s ease;
  229. }
  230.  
  231. .sortable.asc .sort-icon {
  232. border-bottom: 4px solid #000;
  233. }
  234.  
  235. .sortable.desc .sort-icon {
  236. border-top: 4px solid #000;
  237. }
  238.  
  239. .search-input {
  240. width: 100%;
  241. box-sizing: border-box;
  242. padding: 4px;
  243. border: 1px solid #ccc;
  244. border-radius: 4px;
  245. }
  246.  
  247.  
  248.  
  249. ${colsize(1)}
  250. ${colsize(2)}
  251. ${colsize(3)}
  252. ${colsize(4)}
  253. ${colsize(5)}
  254.  
  255.  
  256. /* Shared styles for both ".user-entry > .entry-rid" and ".script-entry > .entry-rid" */
  257. .user-entry > .entry-rid,
  258. .script-entry > .entry-rid {
  259. display: inline-block;
  260. padding: 4px 8px;
  261. color: #fff; /* Set an appropriate white text color */
  262. border-radius: 8px; /* Set the desired border radius */
  263. transition: background-color 0.3s; /* Add transition effect */
  264. }
  265.  
  266. /* Styles for ".user-entry > .entry-rid" */
  267. .user-entry > .entry-rid {
  268. background-color: #4287f5; /* Set your desired background color */
  269. }
  270.  
  271. .user-entry > .entry-rid:hover {
  272. background-color: #1b4a99; /* Set your desired hover background color */
  273. }
  274.  
  275. /* Styles for ".script-entry > .entry-rid" */
  276. .script-entry > .entry-rid {
  277. background-color: #f57d1f; /* Set your desired background color */
  278. }
  279.  
  280. .script-entry > .entry-rid:hover {
  281. background-color: #994d17; /* Set your desired hover background color */
  282. }
  283.  
  284. relative-time.jsm-hidden {
  285. display:none;
  286. }
  287.  
  288. .date-entry-date{
  289.  
  290. display: inline-block;
  291. padding: 4px 8px;
  292. color: #fff; /* Set an appropriate white text color */
  293. border-radius: 8px; /* Set the desired border radius */
  294. transition: background-color 0.3s; /* Add transition effect */
  295. font-size:70%;
  296. background-color: #9932cc;
  297. }
  298.  
  299.  
  300. .date-entry-time{
  301.  
  302. display: inline-block;
  303. padding: 4px 8px;
  304. color: #fff; /* Set an appropriate white text color */
  305. border-radius: 8px; /* Set the desired border radius */
  306. transition: background-color 0.3s; /* Add transition effect */
  307. font-size:70%;
  308. background-color: #dc143c;
  309. }
  310.  
  311. .date-entry-gmt{
  312.  
  313. display: inline-block;
  314. padding: 4px 8px;
  315. color: #fff; /* Set an appropriate white text color */
  316. border-radius: 8px; /* Set the desired border radius */
  317. transition: background-color 0.3s; /* Add transition effect */
  318. font-size:40%;
  319. background-color: #3cb371;
  320.  
  321. }
  322.  
  323. `
  324.  
  325.  
  326.  
  327. convertToAdvancedTable('table.log-table')
  328.  
  329. // Your code here...
  330. })();