// ==UserScript== // @name 5ch Plus // @name:ja 5ch プラス // @description 5ch を拡張します // @author null-chan // @version 0.1.0 // @run-at document-body // @include http*://*5ch.net/* // @include http*://imgur.com/* // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/twemoji/12.0.4/twemoji.min.js // @grant none // @copyright Copyright (c) 2019 null-chan // @license MIT // @namespace https://greasyfork.org/users/379206 // @downloadURL https://update.greasyfork.icu/scripts/390516/5ch%20Plus.user.js // @updateURL https://update.greasyfork.icu/scripts/390516/5ch%20Plus.meta.js // ==/UserScript== (() => { 'use strict' // ################ 設定 ################ // // // true = 有効 | false = 無効 // const config = { background_url: 'https://picsum.photos/1920/1080', // 背景画像。 base64 使用可 background_blur: true, // 背景画像のぼかし preview_limit: 100, // テキストファイルプレビュー時の文字数制限 } // ################ 設定 ################ // $(window).on('load', () => console.info('window.on(\'load\')')) const parse_json = object => { let x let ret = '' for (x in object) { let y let dec = '' for (y in object[x]) dec += `${y}:${object[x][y]}!important;` ret += `${x}{${dec}}` } return ret } // https://stackoverflow.com/questions/1484506/random-color-generator const generateColor = () => { const letters = '0123456789abcdef' let color = '' for (let i = 0; i < 6; i++) color += letters[Math.floor(Math.random() * 16)] return color } const preset = { 'imgur.com/[a-zA-Z0-9]{7}': { script: () => (location.href = $('meta[name="twitter:image"]').attr('content')), }, global_5ch: { hostname: '5ch.net', style: { html: { 'font-family': 'メイリオ,Arial', cursor: 'progress', }, hidden: { display: 'none', opacity: 0, }, 'img.emoji': { height: '1em', width: '1em', 'margin-bottom': '0.1em', 'user-select': 'all', }, 'img.emoji::selection': { background: 'white', }, '.gray_om': { color: '#808080', }, }, script: () => twemoji.parse(document.body), }, '(.*[^jump][.]5ch[.]net/[a-z]{1,}/(#[0-9]{1,2})?)|.*[.]5ch[.]net/test/read.cgi/.*': { script: () => { $(window).on('load', () => { $('html').attr('style', 'cursor:default!important') $('a[href^="https://"],a[href^="http://"],a[href^="/"],a[href^="../"]').each(function () { let href = $(this).attr('href') console.info(`Detected link: %c${$(this).html()}%c - %c${href}`, 'text-decoration:underline', '', 'text-decoration:underline') if (/^(https?:)?\/\/jump[.]5ch[.]net\/[?]/.test($(this).attr('href'))) href = $(this).text() $(this) .addClass('rinku') .text(decodeURI($(this).text())) .attr({ href: href, rel: 'noreferrer noopener', target: '_blank', title: 'ページは新しいタブで開かれます (rel="noreferrer noopener")', }) }) $(document).on('mouseenter', 'dl.thread a,.post .message .rinku,.post_hover .message .rinku', function () { console.info(`Hovered: ${$(this).attr('href')}`) if ($(this).hasClass('loadeding') || $(this).hasClass('reply_link')) return let original_element = $(this).parents('.post_hover').length ? `#${$(this).parents('.post_hover').attr('data-ha')} .message .rinku:nth-of-type(${$(this).parents('.post_hover').find('.message .rinku').index(this) + 1})` : null $(this).addClass('loadeding').attr('title', 'Loading...') const uralu = (new RegExp('^(https?:)?//.*5ch[.]net/').test($(this).attr('href')) ? '' : 'https://cors-anywhere.herokuapp.com/') + encodeURI($(this).attr('href')) if (original_element) $(original_element).addClass('loadeding').attr('title', 'Loading...') console.info(`Loading: ${uralu}`) $.get(uralu, (res, _, xhr) => { const type = xhr.getResponseHeader('content-type') console.info(`Loaded: ${uralu} - ${type}`) let title let element if (type.includes('text/html')) { title = $(res).filter('title').text() || 'No title found' element = `
${title}` } else if (type.includes('image/')) { title = 'DuckDuckGo Image Proxy' element = `
` } else if (type.includes('application/pdf')) { title = 'PDF ファイルのプレビューは対応していません' element = '
PDF ファイルのプレビューは対応していません' } else if (type.includes('application/json')) { if (res.length > config.preview_limit) { title = `${config.preview_limit}文字を超えているため、プレビューを表示できません` element = `
${config.preview_limit}文字を超えているため、プレビューを表示できません` return } if (type.includes('application/json')) { title = 'JavaScript Object Notation (JSON)' element = `
${JSON.stringify(res)}` } } else { title = `Preview is not available - ${type}` element = `
Preview is not available - ${type}` } if (original_element) $(original_element).attr('title', title).after(element) $(this).attr('title', title).after(element) }) }) $(document).on('click', '.preview_img', function () { $(this).toggleClass('hidden') $(this).siblings('.preview_img').toggleClass('hidden') if ($(this).parents('.post_hover').length) { const original_element = `#${$(this).parents('.post_hover').attr('data-ha')} .message ${$(this)[0].tagName}.preview_img:nth-of-type(${$(this).parents('.post_hover').find('.message .preview_img').index(this) + 1})` $(original_element).toggleClass('hidden') $(original_element).siblings('.preview_img').toggleClass('hidden') } }) }) }, }, 'http://.*5ch[.]net.*': { protocol: true, script: () => location.href = location.href.replace(new RegExp('^http://'), 'https://'), }, '.*[^jump][.]5ch[.]net/[a-z]{1,}/(#[0-9]{1,2})?': { style: { body: { background: `url(${config.background_url}) center no-repeat fixed`, 'background-size': 'cover', }, 'div[style="margin: 0; padding: 0 0 0 0.5em; border-top: 0.5em solid #BEB; border-bottom: 0.5em solid #BEB; border-radius: 0.50em / 0.50em; padding: 0 0 1.0em 0.5em; height: 25em; overflow-y: scroll; background: #BEB;"]': { height: 'auto', 'max-height': '25em', }, '.ADVERTISE_AREA,div[style="margin-top:10px;margin-bottom:10px;width:100%;text-align:center;"]': { display: 'none', }, 'p[style="margin: 0.75em 30% 0 30%; padding: 0.5em; border-radius: 0.50em / 0.50em; background: #FFF; color: #666;"]': { display: 'none', }, 'body > div': { background: 'rgba(0, 0, 0, 0.7)', }, '.board_header,div[style="margin: 0; padding: 0 0 0 0.5em; border-top: 0.5em solid #BEB; border-bottom: 0.5em solid #BEB; border-radius: 0.50em / 0.50em; padding: 0 0 1.0em 0.5em; height: 25em; overflow-y: scroll; background: #BEB;"]': { background: 'rgba(0, 0, 0, 0.7)', color: 'white', 'border-radius': '15px', border: 'none', }, 'p[style="margin:0; padding: 0; font-size: 0.75em; background: #BEB;"]': { background: 'none', }, a: { color: '#42a5f5', }, dd: { color: 'white', }, '.thread a': { 'text-shadow': '1px 1px 10px white', transition: 'all 0.2s ease-in-out', }, '.thread a:hover': { 'text-shadow': 'none', }, }, }, '.*[.]5ch[.]net/test/read.cgi/.*': { style: { '.navbar-fixed-top': { background: 'rgba(0, 0, 0, 0.7)', }, '.container_body': { background: 'none', margin: 0, padding: 0, }, '#background': { height: '100%', width: '100%', background: `url(${config.background_url}) center no-repeat fixed`, 'background-size': 'cover', 'z-index': -1, position: 'fixed', top: 0, filter: `blur(${config.background_blur ? '5' : '0'}px)`, transition: 'all 0.3s linear', }, '#background.cleek': { filter: 'blur(0)', }, '#search-text,#search-button': { 'border-radius': '30px 0 0 30px', background: 'none', border: '1px solid rgba(255,255,255,0.5)', filter: 'invert(0.4)', }, '#search-button': { 'border-radius': '0 30px 30px 0', 'border-left': 'none', transition: 'all 0.2s ease-in-out', }, '#search-text:focus,#search-button:hover': { filter: 'invert(0)', }, '.title': { 'margin-top': '70px', 'font-size': '30px', color: '#f5f5f5', 'text-shadow': '0 1px 10px #212121', }, '.stoplight': { background: 'rgba(255, 23, 68, 0.6)', 'margin-bottom': '10px', 'text-align': 'center', }, '.thread': { 'text-align': 'center', }, '.post,.post_hover,.menuitem': { 'border-radius': '15px', background: 'rgba(0, 0, 0, 0.7)', 'box-shadow': '5px 5px 30px black', transition: 'all 0.2s ease-in-out', }, '.post,.post_hover': { 'margin-bottom': '15px', 'max-width': '40%', padding: 0, }, '.meta': { background: 'rgba(0, 0, 0, 0.4)', 'border-radius': '15px 15px 0 0', padding: '5px 10px 5px 10px', 'text-align': 'left', }, '.meta > *': { 'word-break': 'break-all', }, '.message': { margin: '0 10px', padding: '5px 0 10px', 'text-align': 'left', }, '.message > .escaped': { color: 'white', }, '.message > .escaped > hr': { 'border-top': '1px solid #616161', }, '.meta > .number,.date,.margin_right': { color: '#bdbdbd', }, '.be': { 'margin-left': '5px', }, '.escaped > a': { color: '#42a5f5', 'text-shadow': '1px 1px 10px white', transition: 'all 0.2s ease-in-out', }, '.escaped > a:hover': { 'text-shadow': 'none', }, '.meta a': { color: 'green', }, 'br,nav': { 'user-select': 'none', }, '.menuitem': { 'box-shadow': '3px 3px 20px black', }, '.menuitem:hover': { 'box-shadow': '1px 1px 20px black', }, '.footer': { padding: '50px 0 20px 0', }, '.search-logo': { filter: 'invert(0.5)', transition: 'all 0.2s ease-in-out', }, '.search-logo:hover': { filter: 'invert(1)', }, '#post-form-inner': { background: 'rgba(0, 0, 0, 0.7)', border: 'none', }, '.navbar-fixed-top,.pagestats,.menuitem,.topmenu,.bottommenu,.post,.post_hover': { border: 'none', color: 'white', }, '.socialmedia,#banner,.ad--right,.ad--bottom': { display: 'none', }, '.post[data-id="1002"]': { display: 'none', }, '.margin_right': { 'margin-right': '5px', }, // https://qiita.com/semind/items/e18d5f3131de4904b2c9 '.rainbow': { background: 'linear-gradient(to right,rgb(255,0,0),rgb(255,69,0),rgb(255,255,0),rgb(0,128,0),rgb(0,0,255),rgb(75,0,130),rgb(238,130,238))', 'background-clip': 'text', }, '.meta.rainbow *,.message.rainbow *': { color: 'transparent', }, '.post img': { 'max-width': '100%', }, '#scroll': { position: 'fixed', background: 'rgba(0, 0, 0, 0.9)', width: '60px', height: '60px', 'border-radius': '50px', right: '30px', bottom: '30px', 'z-index': '9999', 'font-size': '50px', 'text-align': 'center', }, }, lang: { '.menuitem:contains(全部)': 'All', '.menuitem:contains(最新50)': 'Latest 50', '.menuitem:contains(スマホ版)': 'Mobile (Not Supported)', '.menuitem:contains(掲示板に戻る)': 'Back to Board Top', '.menuitem:contains(ULA版)': 'ULA (Not Supported)', '.metastats:contains(コメント)': ['コメント', ' Comments'], }, script: () => { let long_timer let long_cleek = false $('body').append('
') $(window).scroll(function () { if ($(this).scrollTop() > $(window).height()) $('#scroll').text('↑').addClass('top') else $('#scroll').text('↓').removeClass('top') }) $(document).on('click', '#scroll', function () { $('html').stop().animate({ scrollTop: $(this).hasClass('top') ? 0 : $(document).height() }, 500) }) $('.container_body').append('
') $('#background') .on('dblclick', () => $('#background').toggleClass('cleek')) .on('mouseup', () => { if (long_cleek) $('#background').attr('style', `background:url(${config.background_url}?${new Date().getTime()}) center no-repeat fixed!important`) long_cleek = false clearTimeout(long_timer) return true }) .on('mousedown', () => { long_timer = setTimeout(() => long_cleek = true, 500) return true }) $('.post').hover(function () { const element = $(this).attr('data-userid') ? `.post[data-userid="${$(this).attr('data-userid')}"]` : this $(element).attr('style', 'transform:scale(1.05, 1.05)!important;box-shadow:none!important') }, function () { const element = $(this).attr('data-userid') ? `.post[data-userid="${$(this).attr('data-userid')}"]` : this $(element).removeAttr('style') }) $(window).on('load', () => { const ids = [] $('.post[data-id="1001"] .name b').text('オーバースレッドゥ') if ($('#1 hr').length) { const nodevalu = $('#1 hr')[0].nextSibling.nodeValue if (nodevalu.includes(':vvvvv:')) $('#1 .escaped').append('
強制コテハン') else if (nodevalu.includes(':vvvvvv:')) $('#1 .escaped').append('
強制コテハン + IP表示') else if (nodevalu.includes(':none:')) $('#1 .escaped').append('
ID非表示') else $('#1 .escaped').append('
めんどいからここらへんみとけ
https://info.5ch.net/index.php/新生VIPQ2#.21extend:
https://info.5ch.net/index.php/BBS_SLIP
') } if ($('#\\31').attr('data-userid')) $(`.post[data-userid="${$('#\\31').attr('data-userid')}"] > .meta .name`).each(function () { $(this).after('(スレ主)') }) $('.uid').each(function () { if (!$(this).text()) return $(this).css('color', '#bdbdbd').attr('title', 'IDないですこの人').text('No') const id = $(this).text().slice(3) $(this).text(id !== 'ead' ? id : 'Thread') const ids_ = ids.filter(item => item[0] === id) if (!ids_.length) { if (id === 'ead') return $(this).parents('.post').children('.meta,.message').addClass('rainbow') const color = generateColor() ids.push([id, color]) if (id.toLowerCase().includes('krsw')) console.info(`KRSW Included: ${id}`) console.info(`%cID: ${id}, #${color}`, `color:#${color}`) $(this).attr('title', `#${color}`) return $(this).css('color', `#${color}`) } $(this).attr('title', `#${ids_[0][1]}`) return $(this).css('color', `#${ids_[0][1]}`) }) $('.name b a[href^="mailto:"]').each(function () { let supeisharu = ['フメイ', $(this).attr('href').slice(7)] if (supeisharu[1] === 'sage') supeisharu[0] = 'サゲ' else if (supeisharu[1] === 'age') supeisharu[0] = 'アゲ' else if (['agete', 'sagete'].includes(supeisharu[1])) { if ($(this).contents().first()[0].textContent === 'Please Click Ad !!@アドセンスクリックお願いします ') $(this).contents().first()[0].textContent = 'Please Install Adblock !!@アドブロックインストールお願いします' $(this).children('small').css('margin-left', '5px') supeisharu[0] = supeisharu[1] === 'agete' ? 'アゲロー' : 'サゲロー' } else if (supeisharu[1].test(/@(yahoo.co.jp)$/)) supeisharu[0] === 'ヤホー' else if (supeisharu[1].test(/@(protonmail.com|protonmail.ch|pm.me)$/)) supeisharu[0] === 'プロト' $(this).parents('.meta').children('.date').before(`(${supeisharu[0]})`) }) $('.name a[href^="mailto:"]').each(function () { $(this).removeAttr('href') }) }) $('.date').each(function () { $(this).html($(this).text().replace(' ', '')) }) }, }, } Object.keys(preset).map(item => { const path = preset[item] if (item.startsWith('global')) { const global_match = location.hostname.endsWith(path.hostname) console.info(`%cMatch: global - ${path.hostname} - ${global_match}`, `color:${global_match ? 'green' : 'red'}`) if (global_match) { $('head').append(``) return path.script() } } let url = location.href if (!preset[item].protocol) url = location.href.slice(location.protocol.length + 2) const match = new RegExp(`^${item}$`).test(url) console.info(`%cMatch: ^${item}$ - ${url} - ${match}`, `color:${match ? 'green' : 'red'}`) if (match) { if (typeof path.style === 'object') $('head').append(``) if (typeof path.lang === 'object') Object.keys(path.lang).map(item => { if (typeof path.lang[item] === 'string') $(item).text(path.lang[item]) else if (typeof path.lang[item] === 'object') $(item).html($(item).html().replace(path.lang[item][0], path.lang[item][1])) }) if (typeof path.script === 'function') path.script() } }) })()