返回首頁 

2ch Thread List

2ちゃんねるの各板のトップページに整形したスレッド一覧を表示


Install this script?
// ==UserScript==// @name         2ch Thread List// @namespace    https://greasyfork.org/users/1009-kengo321// @version      10// @description  2ちゃんねるの各板のトップページに整形したスレッド一覧を表示// @grant        none// @match        *://*.2ch.net/*// @match        *://*.5ch.net/*// @license      MIT// @noframes// @run-at       document-start// ==/UserScript==;(function() {'use strict'var byId = function(id) {return document.getElementById(id)}var getBoardId = function() {var p = window.location.pathnamereturn p.slice(1, p.indexOf('/', 1))}var pars###bjectText = (function() {var lineRegExp = /^(\d+)\.dat<>(.*)\s*\((\d+)\)$/gmvar matchedR###lts = function(str) {var r###lt = [], matched = nullwhile ((matched = lineRegExp.exec(str))) r###lt.push(matched)return r###lt}var removeCopyright = function(str) {return str.replace('[転載禁止]', '').replace('[無断転載禁止]', '').replace('&copy;2ch.net', '').replace('&#169;2ch.net', '').replace('&copy;bbspink.com', '').replace('&#169;bbspink.com', '')}var newThreadInfo = function(matchedR###lt, i) {return {line: i + 1,id: parseInt(matchedR###lt[1], 10),title: removeCopyright(matchedR###lt[2].trim()),resNum: parseInt(matchedR###lt[3], 10),}}return function(subjectText) {return matchedR###lts(subjectText).map(newThreadInfo)}})()const millis = {toSeconds(millis) {return Math.trunc(millis / 1000)},}const addForceProperty = (threadInfos, nowAsSeconds) => {const secondsInMinute = 60const secondsInDay = 86400const lowerLimit = elapsed => Math.max(elapsed, secondsInMinute * 3)return threadInfos.map(i => {const elapsed = nowAsSeconds - i.idconst force = elapsed < 0? 0: Math.trunc(i.resNum / (lowerLimit(elapsed) / secondsInDay))return Object.assign({}, i, {force})})}const threadInfos = () => {return threadInfos.data.slice()}threadInfos.data = []threadInfos.set = infos => {threadInfos.data = infos.slice()}var sortThreadInfos = (function() {var cmp = function(prop) {return function(a, b) {if (a[prop] < b[prop]) return -1if (a[prop] > b[prop]) return 1return 0}}var negate = function(func) {return function() { return -func.apply(null, arguments) }}var cmpSeq = function(comparators) {return function(a, b) {for (var i = 0; i < comparators.length; i++) {var r = comparators[i](a, b)if (r !== 0) return r}return 0}}var reversableCmpObj = function(prop) {return {asc: cmpSeq([cmp(prop), line.asc]),desc: cmpSeq([negate(cmp(prop)), line.asc]),}}var line = {asc: cmp('line'), desc: negate(cmp('line'))}var title = reversableCmpObj('title')var resNum = reversableCmpObj('resNum')var id = reversableCmpObj('id')var force = reversableCmpObj('force')var current = line.ascvar setOrReverseCmp = function(comp) {return function() {current = (current === comp.asc ? comp.desc : comp.asc)}}var r###lt = function(threadInfos) {return threadInfos.slice().sort(current)}r###lt.byLineInAsc = function() { current = line.asc }r###lt.byLine = setOrReverseCmp(line)r###lt.byTitle = setOrReverseCmp(title)r###lt.byResNum = setOrReverseCmp(resNum)r###lt.byId = setOrReverseCmp(id)r###lt.byForce = setOrReverseCmp(force)return r###lt})()var newThreadList = (function() {var addCells = function(row) {;[].slice.call(arguments, 1).forEach(function(content) {var cell = row.insertCell()if (['string', 'number'].indexOf(typeof(content)) >= 0) {cell.textContent = content} else {cell.appendChild(content)}})}var sorter = function(setSortType) {return function() {setSortType()updateThreadList(threadInfos())}}var setTHead = function(tHead) {var r = tHead.insertRow()var addTh = function(e) {var th = r.ownerDocument.createElement('th')th.textContent = e[0]th.addEventListener('click', e[1])r.appendChild(th)};[['番号', sorter(sortThreadInfos.byLine)],['タイトル', sorter(sortThreadInfos.byTitle)],['レス', sorter(sortThreadInfos.byResNum)],['勢い', sorter(sortThreadInfos.byForce)],['作成日時', sorter(sortThreadInfos.byId)],].forEach(addTh)}var threadUrl = function(threadId) {return '/test/read.cgi/'+ getBoardId()+ '/'+ threadId+ '/'}var decodeEntityRefs = (function() {var e = document.createElement('span')return function(html) {e.innerHTML = htmlreturn e.textContent}})()var newLink = function(threadInfo) {var r###lt = document.createElement('a')r###lt.target = '_blank'r###lt.href = threadUrl(threadInfo.id)r###lt.textContent = decodeEntityRefs(threadInfo.title)return r###lt}var padZero = function(dateUnit) {return dateUnit <= 9 ? '0' + dateUnit : '' + dateUnit}var toZeroPaddingString = function(date) {var monthDay = [date.getMonth() + 1, date.getDate()]var times = [date.getHours(), date.getMinutes(), date.getSeconds()]return date.getFullYear()+ '/'+ monthDay.map(padZero).join('/')+ ' '+ times.map(padZero).join(':')}var setTBody = function(tBody, threadInfos) {;(threadInfos || []).forEach(function(info) {addCells(tBody.insertRow(), info.line, newLink(info), info.resNum, info.force, toZeroPaddingString(new Date(info.id * 1000)))})}return function(threadInfos) {var r###lt = document.createElement('table')r###lt.id = 'thread-list'setTHead(r###lt.createTHead())setTBody(r###lt.createTBody(), threadInfos)return r###lt}})()var addThreadListBoxIfAbsent = function() {if (!byId('thread-list-box')) {var b = document.bodyb.insertBefore(newThreadListBox(), b.firstChild)}}var replaceThreadListBy = function(threadList) {var old = threadList.ownerDocument.getElementById('thread-list')old.parentNode.replaceChild(threadList, old)}var newTopBar = function() {var message = document.createElement('span')message.id = 'thread-list-error-message'var button = document.createElement('input')button.id = 'thread-list-reload-button'button.type = 'button'button.value = '更新'button.addEventListener('click', function() {button.disabled = truemessage.textContent = ''requestSubjectText(getBoardId())})var r###lt = document.createElement('div')r###lt.id = 'thread-list-top-bar'r###lt.appendChild(button)r###lt.appendChild(message)return r###lt}var newThreadListBox = function() {var r###lt = document.createElement('div')r###lt.id = 'thread-list-box'r###lt.appendChild(newTopBar())r###lt.appendChild(newThreadList())return r###lt}const updateThreadList = threadInfos => {const sorted = sortThreadInfos(threadInfos)const list = newThreadList(sorted)replaceThreadListBy(list)}var subjectTextLoaded = function(event) {var xhr = event.targetif (xhr.status === 200) {const parsed = pars###bjectText(xhr.responseText)const added = addForceProperty(parsed, millis.toSeconds(Date.now()))threadInfos.set(added)updateThreadList(added)} else {byId('thread-list-error-message').textContent = xhr.statusText}}var requestSubjectText = (function() {var handler = function(errorMessage, fn) {return function f(event) {if (document.body) {addStyleIfAbsent()addThreadListBoxIfAbsent()byId('thread-list-reload-button').disabled = falsebyId('thread-list-error-message').textContent = errorMessageif (fn) fn(event)} else {document.addEventListener('DOMContentLoaded', f.bind(this, event))}}}function getSubjectTxtURL(boardId) {const l = window.locationreturn `${l.protocol}//${l.host}/${boardId}/subject.txt`}return function(boardId) {var xhr = new XMLHttpRequest()xhr.timeout = 30000xhr.open('GET', getSubjectTxtURL(boardId))xhr.overrideMimeType('text/plain; charset=shift_jis')xhr.addEventListener('load', handler('', subjectTextLoaded))xhr.addEventListener('timeout', handler('タイムアウト'))xhr.addEventListener('error', handler('エラー'))xhr.send()}})()var addStyleIfAbsent = function() {if (byId('thread-list-style')) returnvar style = document.createElement('style')style.id = 'thread-list-style'style.innerHTML = ['#thread-list {','  margin: 0 auto;','  border-collapse: collapse;','}','#thread-list th {','  color: white;','  background-color: steelblue;','  cursor: default;','}','#thread-list th:hover {','  background-color: cornflowerblue;','}','#thread-list th:active {','  background-color: mediumblue;','}','#thread-list td {','  color: black;','}','#thread-list th, #thread-list td {','  border: solid thin lightsteelblue;','  padding: 0 0.5em;','  line-height: 1.5em;','}','#thread-list tbody tr:nth-child(odd) {','  background-color: azure;','}','#thread-list tbody tr:nth-child(even) {','  background-color: aliceblue;','}','#thread-list tbody tr:nth-child(5n) {','  border-bottom: solid medium lightsteelblue;','}','#thread-list td:nth-child(4n+1),','#thread-list td:nth-child(4n+3),','#thread-list td:nth-child(4n+4) {','  text-align: right;','}','#thread-list a:link {','  color: black;','  text-decoration: none;','}','#thread-list a:visited {','  color: purple;','}','#thread-list a:hover {','  color: maroon;','  text-decoration: underline;','}','#thread-list-box {','  background-color: silver;','}','#thread-list-top-bar {','  text-align: center;','}','#thread-list-error-message {','  color: red;','}',].join('\n')document.head.appendChild(style)}var isBoardTopPage = function() {return /^\/[^/]+\/(?:index\.html)?$/.test(window.location.pathname)}var main = function() {if (!isBoardTopPage()) returnrequestSubjectText(getBoardId())}main()})()