您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
r(文字列未選択時):ホバー中のレスへのアンカーを記入(R:追記) r(文字列選択時):選択文字列を引用(R:追記) m:ホバー中のレスの10番前からを表示 ,:ホバー中のレス以降を表示 .:ホバー中のレス以前を表示 d:書き込み欄にスクロール y:ホバー中の画像をyandexで画像検索 l:ホバー中のレスへのリンクをコピー
当前为
// ==UserScript== // @name 5chサムネイル表示他 // @description r(文字列未選択時):ホバー中のレスへのアンカーを記入(R:追記) r(文字列選択時):選択文字列を引用(R:追記) m:ホバー中のレスの10番前からを表示 ,:ホバー中のレス以降を表示 .:ホバー中のレス以前を表示 d:書き込み欄にスクロール y:ホバー中の画像をyandexで画像検索 l:ホバー中のレスへのリンクをコピー // @version 0.1.31 // @run-at document-idle // @match *://*.5ch.net/test/read.cgi/* // @match *://*.5ch.net/*/ // @match *://*.5ch.net/*/SETTING.TXT // @match *://*.shitaraba.net/bbs/read.cgi/* // @match *://*.shitaraba.net/bbs/read_archive.cgi/* // @match *://*.2chan.net/* // @grant GM_addStyle // @grant GM.addStyle // @grant GM.setClipboard // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @noframes // @require https://code.jquery.com/jquery-3.4.1.min.js // @require https://code.jquery.com/ui/1.12.1/jquery-ui.min.js // @namespace http://greasyfork.icu/users/181558 // ==/UserScript== (function() { const enableHoverZoom = 1; // 1:画像サムネイルのホバーズームを有効 const DEFAULT_MAIL_ADDRESS = ""; // メールアドレス初期値 ""、"sage"、その他に変更可 const inlineImageThumbnailHeight = 65; // 画像サムネイルの縦サイズ(px) const inlineImageThumbnailBokashi = 0; // 0-10:画像サムネイルのぼかしの強さ const WAIT = performance.now() * 0.5; // ページ開始後のウエイト 不安定なときは大きくする const WAIT_IMAGE_EMBED_INTERVAL = 500; // 画像埋め込みの頻度(ミリ秒) const WAIT_VIDEO_EMBED_INTERVAL = 500; // 動画埋め込みの頻度(ミリ秒) const NUMBER_IMAGE_EMBED_AT_ONCE = 3; // 画像埋め込みの速度(枚数) const inlineImageThumbnailPreloadRadius = window.innerHeight * 1; // 画面外の上下何画面分までを「画面内」とするか const ALTERNATIVE_THUMBNAIL = 0; // 0; // 1:別方式のサムネイル const CONFIRM_FOR_Y = 1; // 1:yキーでyandex検索をする時URLの確認を求める 0:求めない const CONFIRM_FOR_MCP = 0; // 1:M,.キーでURLの確認を求める 0:求めない const CH5_QUOTE_POPUP_SCALING_LOWER_LIMIT = 0.98; // 引用ポップアップが画面下に収まらない時縮小する倍率の下限 0.5~1ぐらい const QUOTE_STYLE = "color:#008080;"; // 引用文のスタイル "";なら無効 const POPUP_STYLE = 1; // 引用ポップアップの見た目 0:シンプル 1:原型維持 const EXPERIMENTAL_COMPLEMENT_YOUTUBE_URL_PARTIAL = 0; // 1:不完全なYouTube URLもリンク化する(実験的) const EXPERIMENTAL_FLOAT_KAKIKOMI_FIRST = 0; // 1:5chでスレッド内を開いたら最初に書き込み欄を浮かせる(実験的) const LEAD_LENGTH = ~~(clientWidth() / 3 / 16.15); // 1-99:>>100等のアンカーに本文の冒頭を引用する文字数 0:無効 const RELATED_SIZE = 1.5; // 1.5-3.5:関連レスの□の大きさ(em) const DEBUG_TIMER = 0; // 1ならかかった時間を計測 const debug = 0; // 1:verbose const marginH = 200; // 元絵と拡大画像の横の余白px const POPUP_Z_INDEX = 10000; // ポップアップ画像がどれくらい手前に来るか const FORCE_USE_HALF_WIDTH = 0; // 1:ホバーズームで必ず画面の横半分のサイズ const marginPe = 8; const isfvw = eleget0('#fvw_menu') const MINIMUM_ROWS = (window.navigator.userAgent.toLowerCase().indexOf('firefox') != -1) ? 2 : 3; const isShitaraba = lh("shitaraba.") var GF = {} var waitTimerA = [] var maey = 0; var poppedUrl = ""; var yandexUrl = ""; var mousex = 0; var mousey = 0; var lastEle = ""; var resFloat = false; var resListen = false; var maet = 0; String.prototype.match0 = function(re) { let tmp = this.match(re); if (!tmp) { return null } else if (tmp.length > 1) { return tmp[1] } else return tmp[0] } $.fn.animate2 = function(properties, duration, ease) { ease = ease || 'ease'; var $this = this; var cssOrig = { transition: $this.css('transition') }; return $this.queue(next => { properties['transition'] = 'all ' + duration + 'ms ' + ease; $this.css(properties); setTimeout(function() { $this.css(cssOrig); next(); }, duration); }); }; if (document.domain.match(/\.2chan\.net$/)) end(document.body, `<span id="fivechthumbnailetc"></span>`) // 使用メッセージを付ける document.addEventListener("mousemove", function(e) { mousex = e.clientX; mousey = e.clientY; }, false); if (EXPERIMENTAL_FLOAT_KAKIKOMI_FIRST) { // スレッド内を開いたら最初に書き込み欄を浮かせる(実験的) if (lh(".5ch.net") && lh("/read.cgi/")) floatKakikomi2({ string: null, addMode: 0, command: "resetHeight", preventFocus: 1 }); } if (lh(".5ch.net") || lh("shitaraba")) { // .relAllAreaon枠に連鎖レス数を書き入れていく if (1 || EXPERIMENTAL_DISPLAY_CHAINED) { GF.resChained = [] setTimeout(() => reschain('.relallAreaon:not([data-filled]'), 4000) // 全部最速でも余裕 function reschain(dash = null) { let org if (!GF.resChained || !GF.resChained?.length) { GF.resChained = elegeta(dash ? dash : '.relallAreaon:not([data-filled]') //',.ftbpu .relallAreaon,#pickbox .relallAreaon)') GF.resTotal = elegeta('.post').length } do { do { org = GF.resChained.shift() } while (org && org?.dataset?.filled) if (org) { let rsc = +org.closest('.post')?.id let lists = getRelatedRsca(rsc) elegeta('.relallAreaon', elegeta('.post').find(e => e.id == rsc)).forEach(w => { w.innerText = "-"; w.dataset.filled = "1" }) lists.forEach(v => { elegeta('.relallAreaon', elegeta('.post').find(e => e.id == v)).forEach(w => { w.innerText = lists?.length //+` (${lists})` w.dataset.filled = "1" if (debug) $(w).effect("highlight"); //org.areaonEle.scrollIntoView() }) }) } } while (dash && org) setTimeout(reschain, (org || dash) ? ((GF?.resTotal || 9) * 3 + 100) : 60000) } } } if (lh(".5ch.net") || lh("shitaraba")) { GM.addStyle(".quoteSpeechBalloon{font-size:95%; display:inline-block; white-space : nowrap ; position:relative; bottom:1px; color:#484; background-color:#ffffee;border-radius:1em; user-select:none; cursor:pointer;}") // qsb:: GM.addStyle(`.quoteSpeechBalloonImg{float:right; clear:right; max-height:2.8em !important; padding:4px; user-select:none; background-color:#ffffee; border-radius:6px; cursor:pointer;}`) // GM.addStyle(`.relallAreaon{outline:2px dotted #789922d0; margin:1px; cursor:pointer;width:${RELATED_SIZE}em; height:${RELATED_SIZE}em; }`) GM.addStyle(`.relallAreaon{display:inline-block; outline:2px dotted #789922d0; margin:1px; cursor:pointer; min-width:${RELATED_SIZE}em; min-height:${RELATED_SIZE}em; }`) GM.addStyle(".allpopup{color:#00f;cursor:pointer;}") GM.addStyle(".ch5pu:not(.ch5puz){max-width:90%;}") GM.addStyle(".post_hover{visibility:hidden;}"); //元からあるポップアップを隠す if (POPUP_STYLE) { GM.addStyle(`.relallArea{ bottom:0; text-align:center; line-height:${RELATED_SIZE}em; color:#78992290; float:right; transform:translate(${RELATED_SIZE+1.5}em);} .ch5pu .relallArea{transform:translate(${RELATED_SIZE+1.5}em);}`); GM.addStyle(`.ch5pu {z-index:11; padding:1em ${RELATED_SIZE+3}em 1em 3em; background-color:#f2f3f7; margin:0 1em 3px 0em; box-shadow: 2px 2px 15px gray; border:1px solid #bbb; border-radius:5px; outline-offset: -1px;}`); GM.addStyle(`.ch5pu:not(.ch5puz) .post{box-shadow:1px 1px 1px #4442; border:1px solid #ddd; padding:0.5em;}`) // padding:0.5em 1.5em; } else { GM.addStyle(`.relallArea { bottom:0; text-align:center; line-height:${RELATED_SIZE}em; color:#78992290; float:right; transform:translate(${RELATED_SIZE+1.5}em);} .ch5pu .relallArea{transform:translate(${1.5}em);}`) GM.addStyle(`.ch5pu {z-index:11; background-color:#efefef; margin:0 1em 3px 0em; box-shadow: 2px 2px 15px gray; border-bottom:0; outline-offset: -1px; }`); } $(document).on("click", ".relallAreaon", e => zf(e.ctrlKey ? "Shift+Z" : "z")) function zf(e) { // z:: let isZ = e == "Shift+Z" elegeta('.ch5pu').forEach(v => v?.classList?.add("ch5puz")) let target = ".ch5puz" //eleget0('.ch5pu') z(isZ ? 2 : 1, isZ ? 2 : 1) function z(SCALE_MIN = 1, SCALE_MAX = 2) { var D_FILENAME_MAXLENGTH = 100 // ファイル名の最大長 if (!eleget0(target)) return let honbun = elegeta(`${target} .message`).map(e => e.innerText)?.join(" ") honbun = honbun?.replace(/\s*\>+[^\s]+/gm, "")?.trim() || res honbun = honbun?.slice(0, 100); // ?.replace(/キタ━+\(゚\∀゚\)━+\!+/gm,"") $(`${target} .ignoreMe,${target} .relallArea,${target} .adddel`).remove() $(`${target},${target} div`).css({ "box-shadow": "none", "transform": "scale(1)" }) POPUP_STYLE && $(`${target}`).css({ "border": "none", "padding": "1em" }) POPUP_STYLE && $(`${target} .post`).css({ "border-width": "1px;", "display": "inline-block;", "border-style": "none solid solid none;", "border-color": "#ddd;", "background-color": "#efefef;", "margin-bottom": "8px;", "padding": "8px;" }) var hrefName = "" var fn = `${signzen(document.title+" "+location.href).substr(0, D_FILENAME_MAXLENGTH-hrefName.length-1)} ${hrefName}`?.trim() var res = honbun res = res?.replace(/^\s*\>.*\s*$/gm, "")?.replace(/キタ━+\(゚\∀゚\)━+\!+/gm, "")?.trim() || res; if (res) fn = `${signzen(document.title+" "+location.href).substr(0, D_FILENAME_MAXLENGTH/2-hrefName.length-1)} ${signzen(res).substr(0, D_FILENAME_MAXLENGTH/2-1)} ${hrefName}`.trim() function signzen(str) { return str.replace(/^\s+/, "").replace(/\\|\/|\:|\;|\,|\+|\&|\=|\*|\?|\"|\'|\>|\<|\./g, c => { return String.fromCharCode(c.charCodeAt(0) + 0xFEE0) }) } let puele = eleget0(`${target}`) let scale = Math.max(1, Math.min(SCALE_MAX, SCALE_MIN + (elegeta('img:not(.quoteSpeechBalloonImg)', puele).length * 0.5))) popup3(`左クリック:ポップアップを保存\n左クリック+Ctrl:ポップアップを保存(高画質)\nScale = ${scale}`) document.dispatchEvent(new CustomEvent('saveDOMAsImage', { detail: { element: puele, filename: fn, scale: scale } })) } } } if (/\.2chan\.net/.test(new URL(location.href).hostname)) { // ふたば $('#ftxa:visible').css({ "padding": "0.5em", "font-size": "14px", "line-height": "1.2", "font-family": "sans-serif" }) //14pxで1.2emか15pxで1.1emか setFloatKakikomi2({ textareaXP: '#ftxa', submitbuttonXP: '//input[@type="submit" and @value="返信する"]', wid: '55%', minwid: 700 }) $('#ftxa:visible').on("change click", () => { floatKakikomi2({ textareaXP: '//textarea[@id="ftxa"]', submitbuttonXP: '//input[@type="submit" and @value="返信する"]', wid: '55%', minwid: 700 }) }) $(eleget0('//input[@type="submit" and @value="返信する"]')).on("click", () => { setTimeout(() => { $('#ftxa:visible').attr("rows", MINIMUM_ROWS) }, 500) }) // レスにホバーしてキー入力 $(document).on("keypress", e => { if (e.target.tagName == 'INPUT' || e.target.tagName == 'TEXTAREA' || e.target.isContentEditable) return; var key = (e.shiftKey ? "Shift+" : "") + (e.altKey ? "Alt+" : "") + (e.ctrlKey ? "Ctrl+" : "") + e.key; var selectedStr = window.getSelection().toString() ? ">" + window.getSelection().toString().replace(/^\r\n/, "").replace(/\r?\n/g, "\r\n>").replace(/^\r?\n|\r?\n$/, "\r\n").replace(/([^\n])$/, "$1\n") : null; if ($('#ftxa:visible').length) { if ((selectedStr && (key === "r" || key === "Shift+R"))) { // r::(文字列選択中)選択中の文字列を引用(R::追記) $('div.slp1').hide() $('#ftxa:visible').attr("rows", MINIMUM_ROWS) $('#ftxa:visible').val((key === "Shift+R" ? $('#ftxa:visible').val() + ($('#ftxa:visible').val()?.slice(-1) != "\n" ? "\n" : "") : "") + selectedStr) floatKakikomi2({ textareaXP: '//textarea[@id="ftxa"]', submitbuttonXP: '//input[@type="submit" and @value="返信する"]', wid: '55%', minwid: 700 }) return false; } if ((!selectedStr && (key === "r" || key === "Shift+R"))) { // r::(文字列不選択)ホバー中のレスNo.を引用(R::追記) レス番にホバーならレス番>本文がキターならレス番>本文ホバーなら本文を引用の順に優先 $('div.slp1').hide() let cno = eleget0('span.cno:hover') // レスNo.にホバーしていればそれ let hoveraimg = eleget0('a img:hover') let hovera = [hoveraimg?.parentNode?.previousElementSibling?.previousElementSibling, hoveraimg?.parentNode?.previousElementSibling?.previousElementSibling?.previousElementSibling, ...elegeta('div.thre>a:first-child:hover,.rtd a+br+a:hover')].find(e => e?.href?.match(/\.(jpg|jpeg|png|gif|bmp|webp|mp4|webm|mkv)$/gmi) && e?.textContent > "") // 画像ファイル名にホバーしていればそれ let honbunclone = eleget0('table:hover blockquote') //?.cloneNode(true) //$('.quoteSpeechBalloon').css({"opacity":"0"}) elegeta('.quoteSpeechBalloon,.quoteSpeechBalloonImg', honbunclone).forEach(e => e.style.display = "none") let honbun = hovera || !cno && honbunclone if (honbun?.innerText == "キタ━━━(゚∀゚)━━━!!" || honbun?.innerText == "キタ━━━━━━(゚∀゚)━━━━━━ !!!!!") honbun = null if (!cno && !honbun) cno = eleget0('table:hover span.cno') if (honbun) { $(honbun).effect("highlight", 500) } else { if (cno) $(cno).effect("highlight", 500); } $('#ftxa:visible').attr("rows", MINIMUM_ROWS) if (cno || honbun) $('#ftxa:visible').val((key === "Shift+R" ? $('#ftxa:visible').val() + ($('#ftxa:visible').val()?.slice(-1) != "\n" ? "\n" : "") : "") + (honbun ? ">" + honbun?.innerText?.replace(/^\r\n/, "").replace(/\r?\n/g, "\r\n>").replace(/^\r?\n|\r?\n$/, "\r\n").replace(/([^\n])$/, "$1\n") : cno ? ">" + cno.textContent + "\n" : "")) floatKakikomi2({ textareaXP: '//textarea[@id="ftxa"]', submitbuttonXP: '//input[@type="submit" and @value="返信する"]', wid: '55%', minwid: 700 }) elegeta('.quoteSpeechBalloon,.quoteSpeechBalloonImg', honbunclone).forEach(e => e.style.display = "inline") return false; } } }); $('body').on('mouseup', function(e) { if (window.getSelection) { var selectedStr = window.getSelection().toString(); if (selectedStr != '' && selectedStr != '\n') { popup2(`r:引用\nR:引用(追記)`, 8); //popup2(`『${selectedStr}』を\nr:引用\nR:引用(追記)`, 5); } } }); } if (/shitaraba/.test(location.href)) { // したらば quote(); setTimeout(hNukiURLHokan, 0); setInterval(videoUmekomi, WAIT_VIDEO_EMBED_INTERVAL); setInterval(imageUmekomi, WAIT_IMAGE_EMBED_INTERVAL); if (enableHoverZoom) setInterval(onmove, 16.667); setFloatKakikomi2({ textareaXP: '//textarea[@name="MESSAGE"]', submitbuttonXP: '//div[@id="form_write"]/form[@method="POST"]/input[@value="書き込む"]' }) return; } if (location.href.match(/SETTING.TXT$/)) { // SETTING var e = document.body.innerText.match(/BBS_LINE_NUMBER=(\d*)/); if (e) { pref(getIta() + " : line_number", e[1]); } var e = document.body.innerText.match(/BBS_MESSAGE_COUNT=(\d*)/); if (e) { pref(getIta() + " : message_count", e[1]); } return; } function getIta() { let name = location.href.match(/^https?:\/\/.+\.5ch\.net\/test\/read.cgi\/([^\/]+)/) || location.href.match(/^https?:\/\/.+\.5ch\.net\/([^\/]+)/); if (name) name = name[1] return name; } var line_number = pref(getIta() + " : line_number") || null; var message_count = pref(getIta() + " : message_count") || null; if (location.href.match(/^https?:\/\/.+\.5ch\.net\/[^/]+\/$/) && eleget0('//div[last()]/form[@method="POST"]/p/input[@value="新規スレッド作成"]')) { // 板トップ elegeta('//div[@class="NEW_THREAD"]/form[@method="POST"]/p/textarea').forEach(mes => { mes.setAttribute("stretchabletextarea", "1"); mes.setAttribute("wrap", "on"); mes.style.width = "90%"; mes.addEventListener("input", () => kakikomiStretch(mes)); mes.addEventListener("focus", () => kakikomiStretch(mes)); }); elegeta('//textarea[not(@stretchabletextarea)]').forEach(mes => { mes.setAttribute("stretchabletextarea", "1"); mes.setAttribute("wrap", "on"); mes.style.width = "90%"; mes.addEventListener("input", () => kakikomiStretch(mes, "nearest")); mes.addEventListener("focus", () => kakikomiStretch(mes, "nearest")); }); return; function kakikomiStretch(target, scrollBlock = "center") { if (target.value == "") target.style.height = "100px"; let lineHeight = target.style.height.match("px") ? Number((target.style.height || "0px").replace("px", "")) : 0; //getAttribute("rows")); let height = target.scrollHeight; //+12; let clientHeight = Math.min(document.documentElement.clientHeight, window.innerHeight) - 165; for (let i = 0; i < 200 && (height >= target.offsetHeight) && target.offsetHeight < clientHeight; i++) { lineHeight += 10; target.style.height = lineHeight + "px"; } setTimeout(() => { target.scrollIntoView({ behavior: "smooth", block: scrollBlock, inline: "center" }); }, 17); displayLineLimit(target); } } if (/^https?:\/\/.+\.5ch\.net\/test\/read\.cgi\/.+/.test(location.href) == false) { return; } // httpならhttpsに if (location.href.indexOf("http://") != -1) { location.href = location.href.replace(/^http:\/\//, "https://"); return; } if (location.href.indexOf("subback.html") != -1) { return; } if (location.href.match(/\/\/.*\.5ch\.net\/\w*\/$/)) { return; } $(".mascot").attr("style", ""); let serverColor = colorFromText(new URL(location.href).hostname, 50, 30); $(document.body).append(`<span id="5chVerticalThreadTitle" style="writing-mode: vertical-rl; top:2em; right:0.3em;position:fixed;z-index:-111; font-size:3em; opacity:0.5; color:${serverColor};">${document.title}</span>`); $(eleget0('//ul/li[@class="menubottomnav"]/a[@class="menuitem" and text()="全部"]/../../..')).before(`<span id="bottomDocTitle" style=" margin:0 0 0 0.2em;font-size:1.5em; opacity:0.9; color:${serverColor};">${document.title}</span>`); function colorFromText(txt, s, l) { return !txt ? "hsl(0,50%,50%)" : `hsl(${Array.from(txt).map(ch => ch.charCodeAt(0)).reduce((a, b) => a+b)**3%360}, ${s}%, ${l}%)`; } // レスにホバーしてキー入力 $(document).on("keypress", e => { if (e.target.tagName == 'INPUT' || e.target.tagName == 'TEXTAREA' || e.target.isContentEditable) return; var key = (e.shiftKey ? "Shift+" : "") + (e.altKey ? "Alt+" : "") + (e.ctrlKey ? "Ctrl+" : "") + e.key; var selectedStr = window.getSelection().toString() ? ">" + window.getSelection().toString().replace(/^\r\n/, "").replace(/\r\n/g, "\r\n>").replace(/^\r\n|\r\n$/, "\r\n") : null; let num = getNearest('.post'); let current = location.href + ((location.href.match(/^https?:\/\/.+\.5ch\.net\/test\/read\.cgi\/\w+\/\d+$/)) ? "/" : ""); // /18246213874 で終わるとバグる if ($('form>p>textarea').length) { if ((selectedStr && (key === "r" || key === "Shift+R"))) { // r::(文字列選択中)ホバー中のレスを引用(R::追記) let res = getNearest('div.message'); if (num && (num?.id >= 2 || (num?.id == 1 && $(window).scrollTop() < 500))) { $(getNearest('div.post')).effect("highlight", 500); floatKakikomi2({ string: ">>" + num?.id + "\r\n" + (selectedStr ? selectedStr : (res.innerText.replace(/^(.+)$/gm, ">$1"))) + "\r\n", addMode: (e.key == "R" || e.key == "C"), command: e.key == "r" ? "resetHeight" : null }); } return false; } if (key == "r" || key == "Shift+R") { // r::ホバー中のレスにアンカー R::レスにアンカー(追記) if (num?.id >= 2 || (num?.id == 1 && $(window).scrollTop() < 500)) { $(getNearest('div.post')).effect("highlight", 500); floatKakikomi2({ string: ">>" + num?.id + "\n", addMode: e.key == "R", command: e.key == "r" ? "resetHeight" : null }); } else { floatKakikomi2({ string: "", addMode: e.key == "R", command: e.key == "r" ? "resetHeight" : null }); } return false; } } if (key === "m" || key === "," || key === "Shift+M" || key === "Shift+<") { // ,::そのレス以降を表示 m::そのレスの10個前以降を表示 (Shiftを押しながらだと新しいタブで開く) let num = getNearest('.post'); if (num) { let num2 = (num.id) - (/m/i.test(key) ? 10 : 0); $(getNearest('div.post')).effect("highlight"); let last = current.replace(/\/l\d+$/, "/").replace(/^.*\/([0-9]{0,4})?-?([0-9n]{0,4}?$)/gm, "$2"); let url = current.replace(/\/l\d+$/, "/").replace(/\/[0-9\-]{0,4}-?[0-9n]{0,4}?$/gm, "/").replace(/(\d)$/, "$1/") + Math.max(1, num2) + "-" + last; if (CONFIRM_FOR_MCP && !confirm(url)) return; if (/Shift\+/i.test(key)) { window.open(url) } else { location.href = url; } } return false; } if (key === "." || key === "Shift+>") { // .::そのレス以前を表示 (Shiftを押しながらだと新しいタブで開く) let num = getNearest('.post'); if (num) { let num2 = (num.id) - (e.key === "m" ? 10 : 0); $(getNearest('div.post')).effect("highlight"); if (num2 > 1) { let url = current.replace(/\/l\d+$/, "/").replace(/(\/[0-9]{0,4})-?[0-9n]{0,4}?$/gm, "$1") + "-" + Math.max(1, num2); if (CONFIRM_FOR_MCP && !confirm(url)) return; if (/Shift\+/i.test(key)) { window.open(url) } else { location.href = url; } } } return false; } if (key === "l") { // l::ホバー中のレスへのリンクをコピー if (num) { $(getNearest('div.post')).effect("highlight", 500); GM.setClipboard(document.title + "\r\n" + location.href.replace(/\/l\d+$/, "").replace(/\/[0-9\-]{0,4}-?[0-9n]{0,4}?$/gm, "/").replace(/(\d)$/, "$1/") + num.id + "\r\n"); } return false; } if (key === "y" && yandexUrl) { // y::ホバー中の画像をyandex画像検索で検索 if (!CONFIRM_FOR_Y || window.confirm(yandexUrl + "\n\nを開きます。よろしいですか?")) window.open(yandexUrl); return false; } }); $(document).on("keydown", e => { if (e.target.tagName == 'INPUT' || e.target.tagName == 'TEXTAREA' || e.target.isContentEditable) return; var key = (e.shiftKey ? "Shift+" : "") + (e.altKey ? "Alt+" : "") + (e.ctrlKey ? "Ctrl+" : "") + e.key; if (key === "d") { // d::書き込み欄にスクロール scrollKakikomi(0, 0, 1); } }); if (enableHoverZoom) setInterval(onmove, 16.667); function onmove() { let ele = document.elementFromPoint(mousex, mousey); if (lastEle !== ele) { $('img.hzP').remove(); poppedUrl = ""; } if (ele) if (ele.tagName === "IMG" && lastEle !== ele) { poppedUrl = pe(ele); yandexUrl = poppedUrl.match(/\;base64\,/i) ? null : "https://yandex.com/images/search?rpt=imageview&url=" + poppedUrl; } lastEle = ele; } function pe(a) { var panel = document.createElement("img") if (!a) return; var src = "" if (a.parentNode && a.parentNode.tagName == "A" && a.parentNode.href.match(/\.png|\.jpg|\.jpeg|\.gif|\.bmp/i)) { src = decodeURIComponent(a.parentNode.href.replace(/https?:\/\/jump\.5ch\.net\/\?|https?:\/\/jbbs\.shitaraba\.net\/bbs\/link\.cgi\?url=/, "")); } else if (a.parentNode && a.parentNode.parentNode && a.parentNode.parentNode.tagName == "A" && a.parentNode.parentNode.href.match(/\.png|\.jpg|\.jpeg|\.gif|\.bmp/i)) { src = decodeURIComponent(a.parentNode.parentNode.href.replace(/https?:\/\/jump\.5ch\.net\/\?|https?:\/\/jbbs\.shitaraba\.net\/bbs\/link\.cgi\?url=/, "")); } panel.className = "hzP"; //notifyMe(panel.src) panel.src = src; setSize(a, panel, a); document.body.appendChild(panel); panel.addEventListener('load', e => { setSize(e.target, e.target, a); }); return panel.src; } function setSize(a, b, s) { var panel = b; let imgAspect = a.naturalWidth / a.naturalHeight; // svg等だとNaN 要.onload let clientAspect = window.innerWidth / 2 / window.innerHeight; let peStyle = 'margin:2px; border-radius:3px; color:#ffffff; box-shadow:3px 3px 8px #0008; border:2px solid #fff;'; let boxPos = (mousey < (window.innerHeight / 2) ? "bottom:0px;" : "top:0px;") + ((mousex < document.documentElement.clientWidth / 2) ? "right:0px; " : "left:0px;"); panel.className = "ignoreMe hzP"; let amariWidth = (mousex < document.documentElement.clientWidth / 2) ? (document.documentElement.clientWidth - (s.getBoundingClientRect()).right) : (s.getBoundingClientRect().left); if (imgAspect && ((window.innerHeight * imgAspect - marginPe) < amariWidth - marginH)) { panel.setAttribute("style", `all:initial;float:none; width:${(window.innerHeight)*imgAspect-marginPe}px; height:${((window.innerHeight))-marginPe}px;${boxPos} z-index:${POPUP_Z_INDEX}; position:fixed; ${peStyle}`); // 縦目いっぱい if (debug) popup(`余り左右:${amariWidth} naturalWidth:${a.naturalWidth} naturalHeight:${a.naturalHeight} a:${Math.round(imgAspect*100)/100} 縦目いっぱい`) } else if (imgAspect && (((amariWidth - marginH) / imgAspect) <= window.innerHeight)) { //&& (amariWidth - marginH - marginPe) > a.width * 2.5)) { panel.setAttribute("style", `all:initial;float:none; width:${amariWidth-marginH-marginPe}px; height:${(amariWidth-marginH)/imgAspect-marginPe}px;${boxPos} z-index:${POPUP_Z_INDEX}; position:fixed; ${peStyle}`); // 元絵の左右にくっつき最大 if (debug) popup(`余り左右:${amariWidth} naturalWidth:${a.naturalWidth} naturalHeight:${a.naturalHeight} a:${Math.round(imgAspect*100)/100} くっつき`) } else if (!imgAspect || window.innerWidth * 0.48 / imgAspect - marginPe <= window.innerHeight) { panel.setAttribute("style", `all:initial;float:none; width:${window.innerWidth*0.48-marginPe}px; height:${window.innerWidth*0.48/imgAspect-marginPe}px; ${boxPos} z-index:${POPUP_Z_INDEX}; position:fixed; ${peStyle}`); // 横48% if (debug) popup(`余り左右:${amariWidth} naturalWidth:${a.naturalWidth} naturalHeight:${a.naturalHeight} a:${Math.round(imgAspect*100)/100} width:${window.innerWidth*0.48-marginPe}px; height:${window.innerWidth*0.48/imgAspect-marginPe}px; 48%`) } else { panel.setAttribute("style", `all:initial;float:none; width:${(window.innerHeight)*imgAspect-marginPe}px; height:${((window.innerHeight))-marginPe}px;${boxPos} z-index:${POPUP_Z_INDEX}; position:fixed; ${peStyle}`); // 縦目いっぱい if (debug) popup(`余り左右:${amariWidth} naturalWidth:${a.naturalWidth} naturalHeight:${a.naturalHeight} a:${Math.round(imgAspect*100)/100} 縦目いっぱい2`) } } function getNearest(xpath) { let e = document.elementFromPoint(mousex, mousey); return e.closest(xpath) } // 書き込み欄までスクロール function scrollKakikomi(loop = 0, tolatestres = 0, onkey = 0) { //d:: var ele = elegeta('.thread .post')?.filter(e => e.offsetHeight)?.slice(-1)?.find(e => e) if (ele && (eleget0('//form/p/textarea[@name="MESSAGE"]') || onkey)) { window.scroll({ top: ele?.getBoundingClientRect()?.bottom + window.pageYOffset - window.innerHeight + 200, left: 0, behavior: "instant" }) } else if (loop) setTimeout(() => { scrollKakikomi(loop - 1, tolatestres) }, 500); } function waitElement(xpath, cb, interval = 333, anywaydo = 9999, start = Date.now()) { //Number.MAX_SAFE_INTEGER, start = Date.now()) { let ea = elegeta(xpath) //.filter(e => e.offsetHeight) // if (ea.length || Date.now() - start >= anywaydo) { cb(ea) } else { waitTimerA.push(setTimeout(() => waitElement(xpath, cb, Math.min(interval + 100, 1000), anywaydo, start), interval)) } // if (ea.length || (Date.now() - start >= anywaydo)) { cb(ea) } else { waitTimerA.push(setTimeout(() => waitElement(xpath, cb, Math.min(interval + 100, anywaydo), anywaydo, start), interval)) } if (ea.length || (Date.now() - start >= anywaydo)) { cb(ea) } else { setTimeout(() => waitElement(xpath, cb, Math.min(interval + 100, anywaydo), anywaydo, start), interval) } } //document.body.addEventListener('DOMNodeInserted', e=>console.log(e.target), false); // 追加されたDOMを全て見る waitElement('form p textarea[name="MESSAGE"]', () => { scrollKakikomi(10, 1) let ma = $(xa('//input[@placeholder="メールアドレス(省略可)"]')); ma.dblclick(() => { ma.val(ma.val() == "sage" ? "" : "sage"); floatKakikomi2(); }); // 細かい調整 $("div.formbox").css("margin", "0"); $('input[placeholder="メールアドレス(省略可)"]').val(DEFAULT_MAIL_ADDRESS); $("form>p>textarea").click(() => { floatKakikomi2(); }); }) waitElement('#optionView,.flex-container.wrap,.searchform_input-close.hidden,#ch5styleoldnewmodend', (e) => { setTimeout(hNukiURLHokan, 0); setInterval(videoUmekomi, WAIT_VIDEO_EMBED_INTERVAL); setInterval(imageUmekomi, WAIT_IMAGE_EMBED_INTERVAL); quote(); }, 333, Math.min(9999, WAIT * 4)) return; function quote() { // >>文字列に本文冒頭の引用を追加 qsb:: setTimeout(() => { var quos = elegeta('a.reply_link').filter(v => v.innerText.match(/^>>\d+$/)) let table = elegeta(`.post`) quos.filter(e => /^\>+\d+$/m.test(e?.textContent)).forEach((e, i) => { //緑文字の引用行文字列 e.dataset.quoted = 1 let origno = e?.textContent?.match0(/>+(\d+)/) let srcTable = table.find(v => v?.id == origno) //after(e,` ${saki}`)/* if (srcTable && (isShitaraba || e?.nextSibling?.nodeValue == " ")) { var lead = eleget0('.message', srcTable)?.innerText?.replace(/^\s*>+.*$/gm, "")?.replaceAll("キタ━━━(゚∀゚)━━━!!", "")?.trim() lead = lead?.trim()?.replace(/\n/gm, " ")?.trim() if (lead) { if (LEAD_LENGTH) { if (lead?.length > LEAD_LENGTH) lead = lead?.slice(0, LEAD_LENGTH)?.trim() + "…" after(e, ` <span class="quoteSpeechBalloon" data-quoteno="${origno}"> >${lead} </span>`) } eleget0('.relallArea', e.closest(".post"))?.classList?.add("relallAreaon") } } }) }, lh("5ch.net") ? 2000 : 5000) if (QUOTE_STYLE != "") { elegeta('//div[@class="message"]|//dd').forEach(e => { if (/〉|》|>|>[^>\d]+/m.test(e.textContent)) { e.outerHTML = e.outerHTML.replace(/<span class="escaped">\s*(((〉|》|>|>)[^>]+))(?!([^<]+)?>)/gmi, `<span class="t5quote" style="cursor:pointer;${QUOTE_STYLE}">$1</span>`).replace(/<dd>\s*(((〉|》|>|>)[^>]+))(?!([^<]+)?>)/gmi, `<dd> <span class="t5quote" style="${QUOTE_STYLE}">$1</span>`).replace(/<br>\s*(((〉|》|>|>)[^>]+))(?!([^<]+)?>)/gmi, `<br> <span class="t5quote" style="cursor:pointer;${QUOTE_STYLE}">$1</span>`) } }); // 引用に着色,タグの外側だけ置き換え // これをやるとシステムのサムネ添付が終わる setTimeout(() => { popuponquote() }, location.href.match0(/shitaraba/) ? 2000 : 0) } setTimeout(() => { elegeta('//a[@class="reply_link"]').forEach(e => e.setAttribute("onclick", "return false;")) }, location.href.match0(/shitaraba/) ? 2000 : 0) document.addEventListener("click", c => { let word if (c?.target?.matches(".quoteSpeechBalloon")) { word = c?.target?.dataset?.quoteno } else { let e = c.target.closest(".t5quote,.allpopup,.reply_link"); if (e) { word = e.textContent.replace(/^〉+|^》+|^>+|^>+/gm, "").trim() } } if (word) { let t = (word.match0(/^[0-9-]+$/) && eleget0(`//div[@id="${word?.match0(/^\d+/)?.replace(/post/,"")}"]`)) || elegeta('.message').filter(f => !f.closest(".ch5pu")).find(c => c.textContent.indexOf(word) !== -1); if ($(t).is(":hidden")) $(t.closest(".post")).show(0).css({ "display": "table" }) // 学園祭で消していたら出す t.scrollIntoView({ behavior: "smooth", block: "center", inline: "center" }); $(t.closest(".post")).effect("highlight", 750); } }) } // h抜きのURLをリンクにする function hNukiURLHokan() { document.querySelectorAll("div.post:not([data-urlcomplemented]),dd:not([data-urlcomplemented])").forEach(function(obj) { var html = obj.innerHTML; if (obj.innerText.match(/^ttps?:\/\//gm)) { var newhtml = html.replace(/[^h](ttps?:\/\/[^<]+)/gm, "<a referrerpolicy='no-referrer' rel='nofollow external noopener noreferrer' href=\"h$1\">$1</a>"); } if (EXPERIMENTAL_COMPLEMENT_YOUTUBE_URL_PARTIAL && obj.innerText.match(/^(youtu\.be\/|www\.youtube\.com\/watch\?v=|www\.youtube\.com\/shorts\/)([a-zA-Z0-9_\-]{11})/gm)) { var newhtml = html.replace(/([^<]+)(youtu\.be\/|www\.youtube\.com\/watch\?v=|www\.youtube\.com\/shorts\/)([a-zA-Z0-9_\-]{11}[^<]*)/gm, "$1<a referrerpolicy='no-referrer' rel='nofollow external noopener noreferrer' href=\"https://$2$3\">$2$3</a>") } if (newhtml) { obj.innerHTML = newhtml; obj.setAttribute("data-urlcomplemented", 1) } }); } function setFloatKakikomi2({ textareaXP = '//form/p/textarea[@name="MESSAGE"]', submitbuttonXP = '//input[@class="submitbtn btn"]', wid = "70%", minwid = 900 } = {}) { var mes = eleget0(textareaXP); if (mes) { mes.addEventListener("input", () => floatKakikomi2({ textareaXP: textareaXP, submitbuttonXP: submitbuttonXP, wid: wid, minwid: minwid })); mes.addEventListener("focus", () => floatKakikomi2({ textareaXP: textareaXP, submitbuttonXP: submitbuttonXP, wid: wid, minwid: minwid })); resListen = 1; } } // 書き込み欄調整、クリックでフロート化 function floatKakikomi2({ string = null, addMode = 0, command = "", textareaXP = '//form/p/textarea[@name="MESSAGE"]', submitbuttonXP = '//input[@class="submitbtn btn"]', wid = '70%', minwid = 900, preventFocus = 0 } = {}) { var textarea = eleget0(textareaXP); if (!textarea) return var submitbutton = eleget0(submitbuttonXP) if (!resFloat) { $(window).resize(() => { $(textarea).css({ "width": wid, "min-width": Math.min(minwid, (window.innerWidth - 100)) + "px", "max-width": (window.innerWidth - 100) + "px" }).attr("wrap", "on").attr("tabIndex", "1"); kakikomiStretch2(textareaXP, "resetHeight") }); } resListen || setFloatKakikomi2() resFloat = true; let curRes = $(textarea).val(); $(textarea).css({ "z-index": "10", "position": "fixed", "right": "1em", "bottom": "3em", "height": "auto" }).attr("tabIndex", "1"); $(textarea).css({ "width": wid, "min-width": Math.min(minwid, (window.innerWidth - 100)) + "px", "max-width": (window.innerWidth - 100) + "px" }).attr("wrap", "on").attr("tabIndex", "1").attr("floated", ""); $(submitbutton).css({ "z-index": "10", "position": "fixed", "right": "1em", "bottom": "0em" }).attr("tabIndex", "2").attr("floated", ""); $(`input[type="file"][name="upfile"]`).css({ "z-index": "999", "position": "fixed", "right": "48em", "left": "auto", "bottom": "3px", "height": "auto", "padding": "0.1em 0.2em", "border": "2px solid #dddddd", "background-color": "#ffffff" }) GM.addStyle('#ftbl,#ftb2,.ftbl,.ftb2{margin:0px 0px 0px 1em !important; left:1em !important;}') if (string > "") $(textarea).val((addMode ? curRes + ((curRes == "" || curRes.slice(-1) == "\n") ? "" : "\n") : "") + string); $(textarea).attr("stretchabletextarea", "1") if (!preventFocus) { $(textarea).focus() } else { textarea.blur() } // .idw:IDスレかIPスレ警告があればしない if ((textarea).value == "" || command === "resetHeight") textarea.rows = MINIMUM_ROWS; kakikomiStretch2(textareaXP); } function kakikomiStretch2(xp, command = "") { if ($(elegeta(xp)).is(":hidden")) return let target = eleget0(xp); var targetvalue = target ? target.value : "" if (command === "resetHeight" || targetvalue.length < 100 || targetvalue.match(/\n/gm).length < 5) target.rows = MINIMUM_ROWS; let lineHeight = Number(target.getAttribute("rows")); let height = target.scrollHeight; //+12; let clientHeight = Math.min(document.documentElement.clientHeight, window.innerHeight) - 165; for (let i = 0; i < 90 && (height >= target.offsetHeight) && target.offsetHeight < clientHeight; i++) { lineHeight++; target.setAttribute("rows", lineHeight); } displayLineLimit(target); return target; } function displayLineLimit(target) { let line = target.value.split(/\r\n|\r|\n/).length; if (line_number && message_count) { target.style.backgroundColor = line > line_number * 2 ? "#fff0f0" : (new Blob([target.value]).size) > message_count ? "#fffff0" : "#ffffff"; } } function sortDescendMiddle(array) { return array.sort((a, b) => { return Math.abs(a.getBoundingClientRect().top - document.documentElement.clientHeight / 2) > Math.abs(b.getBoundingClientRect().top - document.documentElement.clientHeight / 2) ? 1 : -1 }); } // 画像と動画をインライン埋め込み function imageUmekomi() { // 画像埋め込み var i = 0; sw1("removeSysThumbs"); for (let a of sortDescendMiddle(elegeta('//a[@imge="af"]/div[@div="thumb5ch"]'))) { if (isinscreen(a)) setTimeout((function(a) { return function() { { a.remove(); } } })(a), WAIT_IMAGE_EMBED_INTERVAL * 3 / NUMBER_IMAGE_EMBED_AT_ONCE) } sw2("removeSysThumbs"); sw1("umegazo") for (let ele of sortDescendMiddle(elegeta('//a[not(@imge)]'))) { let isImg = 0; var url = ele.href || ($(ele).text()); url = url.replace(/^(ttps?:\/\/)/m, "h$1"); try { var urlImg = decodeURIComponent(url.replace(/https?:\/\/jump\.5ch\.net\/\?|https?:\/\/jbbs\.shitaraba\.net\/bbs\/link\.cgi\?url=/, "")) //.replace(/https?:\/\/jbbs\.shitaraba\.net\/bbs\/link\.cgi\?url=/,""); } catch (e) { var urlImg = url.replace(/https?:\/\/jump\.5ch\.net\/\?|https?:\/\/jbbs\.shitaraba\.net\/bbs\/link\.cgi\?url=/, "") } if ($(ele).text().match(/\.jpg|\.jpeg|\.png|\.gif|\.bmp/)) { isImg = 1; } else { isImg = 0; } if (!isImg) ele.setAttribute("imge", "!i"); if ((!isinscreen(ele))) continue; // 画面内に無い if (isImg) { //notifyMe(url)//notifyMe(decodeURIComponent(urlImg)) let next = ele.children ? ele.children[0] : null; if (next && next.tagName === "DIV") { // システムのサムネイルあり if (ALTERNATIVE_THUMBNAIL) next.outerHTML = '<br><a class="ignoreMe" referrerpolicy="no-referrer" rel="nofollow external noopener noreferrer" href=' + url + ' target="_blank"><img referrerpolicy="no-referrer" src=' + urlImg + ' height="' + inlineImageThumbnailHeight + '" ' + (inlineImageThumbnailBokashi ? 'style="filter: blur(' + inlineImageThumbnailBokashi + 'px);"' : '') + '/></a>'; ele.setAttribute("imge", "rp"); } else { // if ( /^ttp/.test(ele.textContent)){ // システムのサムネ添付はhtmlに変更があると諦めて中断される $(ele).after($('<br><a class="ignoreMe" referrerpolicy="no-referrer" rel="nofollow external noopener noreferrer" href=' + url + ' target="_blank"><img class="ignoreMe" referrerpolicy="no-referrer" src=' + urlImg + ' height="' + inlineImageThumbnailHeight + '" ' + (inlineImageThumbnailBokashi ? 'style="filter: blur(' + inlineImageThumbnailBokashi + 'px);"' : '') + '/></a>')); ele.setAttribute("imge", "af"); } if (++i >= NUMBER_IMAGE_EMBED_AT_ONCE) break; // 一度に設定枚数ずつしかやらない } } sw2("umegazo") }; function videoUmekomi() { sw1("umedouga") // ニコ動埋め込み(PrivacyBadger等は要Disable) for (let ele of sortDescendMiddle(elegeta('//a[contains(@href,"www.nicovideo.jp")][not(@nde)]|//a[contains(@href,"//nico.ms/sm")][not(@nde)]'))) { if ((!isinscreen(ele))) continue; // 画面内に無い //if (!isinscreen(ele) && Date.now() - (GF?.lateVideo || 0) < 5000) continue; else GF.lateVideo = Date.now() // 画面内にない&5秒未満 let url = ele.innerText.replace(/^ttp/i, "http"); ele.setAttribute("nde", "nde"); var nico = url.match(/h?ttps?:\/\/www.nicovideo.jp\/watch\/(.*)/i); if (!nico) var nico = url.match(/h?https:\/\/nico\.ms\/(.*)/i); if (!nico) continue $(ele).after(`<br><iframe class="ignoreMe" referrerpolicy="no-referrer" rel="nofollow external noopener noreferrer" allowfullscreen="allowfullscreen" allow="autoplay" src="https://embed.nicovideo.jp/watch/${nico[1]}${nico[1].match0(/\?/)?"&":"?"}persistence=1&oldScript=1&allowProgrammaticFullScreen=1" style="max-width: 100%;" width="312" height="176" frameborder="0"></iframe>`) // 埋め込み外部プレイヤー版 break; // 一度に1つずつしかやらない } sw2("umedouga") sw1("umeYT") // youtube埋め込み for (let ele of sortDescendMiddle(elegeta('//a[contains(@href,"youtube.com")][not(@yte)]|//a[contains(@href,"youtu.be")][not(@yte)]'))) { if ((!isinscreen(ele))) continue; // 画面内に無い //if (!isinscreen(ele) && Date.now() - (GF?.lateVideo || 0) < 5000) continue; else GF.lateVideo = Date.now() // 画面内にない&5秒未満 //let url = ele.href; let url = ele.href?.replace(/jump.5ch.net\/\?/, ""); ele.setAttribute("yte", "yte"); var sm = (url.match(/(?:h?ttps?:\/\/)youtu\.be\/([a-zA-Z0-9_\-]{11}).*[\?\&]t=(\d*).*$/i) || url.match(/(?:h?ttps?:\/\/)(?:www\.)?youtube\.com\/watch\?v=([a-zA-Z0-9_\-]{11}).*&t=(\d*).*$/i)) || url.match(/(?:h?ttps?:\/\/)youtu\.be\/([a-zA-Z0-9_\-]{11})/i) || url.match(/(?:h?ttps?:\/\/)(?:www\.)?youtube\.com\/shorts\/([a-zA-Z0-9_\-]{11})/i) || url.match(/(?:h?ttps?:\/\/)(?:www\.)?youtube\.com\/watch\?v=([a-zA-Z0-9_\-]{11})/i); if (!sm) continue var pl = (url.match0(/[\&\?]list=([a-zA-Z0-9_\-]+)/)); $(ele).after('<p class="ignoreMe" style="margin:0 0 0px;"><iframe class="ignoreMe" referrerpolicy="no-referrer" src="https://www.youtube.com/embed/' + sm[1] + (pl ? `?list=${pl}` : "") + (sm[2] ? `${pl?"&":"?"}start=` + sm[2] : "") + '" id="ytplayer" type="text/html" width=321 height=181 frameborder=0 allowfullscreen allow="picture-in-picture"></p>'); break; // 一度に1つずつしかやらない }; sw2("umeYT") // video.twimg埋め込み for (let ele of sortDescendMiddle(elegeta('//a[contains(@href,"ttps://video.twimg.com/ext_tw_video/")][not(@yte)]|.//a[contains(@href,"ttps://i.imgur.com/") and contains(@href,".mp4")][not(@yte)]'))) { if ((!isinscreen(ele))) continue; // 画面内に無い //if (!isinscreen(ele) && Date.now() - (GF?.lateVideo || 0) < 5000) continue; else GF.lateVideo = Date.now() // 画面内にない&5秒未満 // if ((!isinscreen(ele))) continue; // 画面内に無い let url = ele.innerText; url = url.replace(/^ttp/i, "http"); ele.setAttribute("yte", "yte"); var poster = url.match0('https://i.imgur.com/') ? ` poster="${url.replace(/\.mp4/,".jpg")}" ` : ""; $(ele).after(`<p class="ignoreMe"><video class="ignoreMe" referrerpolicy="no-referrer" ${poster} rel="nofollow external noopener noreferrer" width="312" height="176" allowfullscreen="allowfullscreen" preload="none" src="${ url }" controls="" loop="" > <source src="${url}" type="video/mp4"> </video></p>`) break; // 一度に1つずつしかやらない }; sw2("umeVideoTwimg") } // eleはスクロール画面内に入ってる? function isinscreen(ele) { if (!ele) return; var eler = ele.getBoundingClientRect(); return (eler.top > 0 - inlineImageThumbnailPreloadRadius && eler.left > 0 && eler.left < document.documentElement.clientWidth && eler.top < Math.min(window.innerHeight, document.documentElement.clientHeight) + inlineImageThumbnailPreloadRadius); } function elegeta(xpath, node = document) { if (!xpath || !node) return []; let xpath2 = xpath.replace(/:inscreen|:visible|:text\*=[^:]*/g, "") // text*=~中で:は使えない let array = [] try { if (!/^\.?\//.test(xpath)) { array = [...node.querySelectorAll(xpath2)] } else { var snap = document.evaluate("." + xpath2, node, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null) let l = snap.snapshotLength for (var i = 0; i < l; i++) array[i] = snap.snapshotItem(i) } if (/:visible/.test(xpath)) array = array.filter(e => e.offsetHeight) else if (/:inscreen/.test(xpath)) array = array.filter(e => { var eler = e.getBoundingClientRect(); return (eler.bottom >= 0 && eler.right >= 0 && eler.left <= document.documentElement.clientWidth && eler.top <= document.documentElement.clientHeight) }) // 画面内に1ピクセルでも入っている if (/:text\*=./.test(xpath)) { let text = xpath.replace(/^.*:text\*=([^:]*)$/, "$1"); if (text) array = array.filter(e => new RegExp(text).test(e?.textContent)) } } catch (e) { return []; } return array } function eleget0(xpath, node = document) { if (!xpath || !node) return null; if (/:inscreen|:visible|:text\*=/.test(xpath)) return elegeta(xpath, node)?.shift(); if (!/^\.?\//.test(xpath)) return node.querySelector(xpath); try { var ele = document.evaluate("." + xpath, node, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); return ele.snapshotLength > 0 ? ele.snapshotItem(0) : null; } catch (e) { alert(e + "\n" + xpath + "\n" + JSON.stringify(node)); return null; } } function xa(xpath, node = document) { if (!xpath) return []; if (xpath.match(/^\//)) { try { var array = []; var ele = document.evaluate("." + xpath, node, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); let l = ele.snapshotLength; for (var i = 0; i < l; i++) array[i] = ele.snapshotItem(i); return array; } catch (e) { return []; } } else { return $(xpath); } } function sw1(s) { if (DEBUG_TIMER) console.time(s); } function sw2(s) { if (DEBUG_TIMER) console.timeEnd(s); } function pref(name, store = null) { // prefs(name,data)で書き込み(数値でも文字列でも配列でもオブジェクトでも可)、prefs(name)で読み出し if (store === null) { // 読み出し let data = GM_getValue(name) || GM_getValue(name); if (data == undefined) return null; // 値がない if (data.substring(0, 1) === "[" && data.substring(data.length - 1) === "]") { // 配列なのでJSONで返す try { return JSON.parse(data || '[]'); } catch (e) { alert("データベースがバグってるのでクリアします\n" + e); pref(name, []); return; } } else return data; } if (store === "" || store === []) { // 書き込み、削除 GM_deleteValue(name); return; } else if (typeof store === "string") { // 書き込み、文字列 GM_setValue(name, store); return store; } else { // 書き込み、配列 try { GM_setValue(name, JSON.stringify(store)); } catch (e) { alert("データベースがバグってるのでクリアします\n" + e); pref(name, ""); } return store; } } function popup(text, color = "#6080ff") { text = String(text); text = text.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/`/g, '`').replace(/</g, "<").replace(/>/g, ">").replace(/\n/gm, "<br>") var e = document.getElementById("hzbox"); if (e) { e.remove(); } var e = document.body.appendChild(document.createElement("span")); e.innerHTML = '<span id="hzbox" style="all:initial; position: fixed; right:1em; top: 1em; z-index:1000000; opacity:1; font-size:15px; font-weight:bold; margin:0px 1px; text-decoration:none !important; text-align:left; padding:1px 6px 1px 6px; border-radius:12px; background-color:' + color + '; color:white; white-space: nowrap;" onclick=\'var a = document.createElement(\"textarea\"); a.value = \"' + text.replace(/<br>/gm, "\\n") + '\"; document.body.appendChild(a); a.select(); document.execCommand(\"copy\"); a.parentElement.removeChild(a);\'">' + text + '</span>'; setTimeout((function(e) { return function() { e.remove(); } })(e), 5000); } function popup2(text, i = 0) { text = text.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/`/g, '`').replace(/</g, "<").replace(/>/g, ">").replace(/\n/gm, "<br>") var mae = eleget0('//span[@id="yhmbox"]'); if (maet && mae) { mae.remove(); clearTimeout(maet); } let bgcol = (pref("translucent") != "on") ? "#6080ff" : "#909090"; var ele = $('<span id="yhmbox" class="ignoreMe" style="all:initial; font-family:sans-serif; cursor:pointer; position: fixed; right:0em; bottom: ' + (i * 2 + 2) + 'em; z-index:2147483647; opacity:1; font-size:15px; margin:0px 1px; text-decoration:none !important; padding:1px 6px 1px 6px; word-break: break-all !important; border-radius:12px; background-color:' + bgcol + '; color:white; ">' + text + '</span>').appendTo('body'); maet = setTimeout(function() { var mae = eleget0('//span[@id="yhmbox"]'); if (mae) { mae.remove(); } }, 5000); $(ele).attr("title", "クリックでこのガイドを一時的に消す").click(function() { $(this).fadeOut(200).queue(function() { $(this).remove(); clearTimeout(maet); }) }); } function notifyMe(body, title = "") { if (!("Notification" in window)) return; else if (Notification.permission == "granted") new Notification(title, { body: body }); else if (Notification.permission !== "denied") Notification.requestPermission().then(function(permission) { if (permission === "granted") new Notification(title, { body: body }); }); } // >~にポップアップ function popuponquote() { // 引用ホバー if (/\.shitaraba\.net\/bbs\/read_archive.cgi\//.test(location.href) && eleget0('//HTML/BODY/DL[1]')) { eleget0('//HTML/BODY/DL[1]').id = "thread-body" } if (eleget0('//div[@class="post" and not(@id)]')) elegeta('.date').forEach(e => { e.closest('.post').id = e.textContent.trim().match0(/^(\d+)\s:/) || ""; }) var latestHover; var mesEleA = elegeta('.thread .message,#thread-body .message').slice(0, 1000); // したらばで1000以上のスレは重すぎて実質固まるので1000までしか処理しない var greenEleA = elegeta(".t5quote").slice(0, 1000) // 旧5chに引用ポップアップの仕込み elegeta('//div[@class="message"]/span/a[contains(text(),">>")]|//div[@class="message"]/span/span/a[contains(text(),">>")]').slice(0, 3000).forEach(e => { if (e.innerText.match(/\>\>\d+/)) e.classList.add("reply_link") }) // 右肩のバックリンクを付ける elegeta('//div[@class="meta"]').slice(0, 1000).forEach(e => { let ep = e.closest(".post"); if (ep) { let eID = ep.id let quoted = gatherRes(new RegExp(`>>${eID}[^0-9]`), ep, 2) if (quoted.length) { $('.back-links>a', ep).remove(); quoted.forEach(c => { $(e).append(` <span class="allpopup" id="post${c}">\>\>${c}</span>`) }) e.dataset.quospan = "1"; end(e, `<span style="display:none"> [</span><span class="ignoreMe relallArea relallAreaon" data-resno="${ep.id}"></span><span style="display:none">]</span>`) // 見えない「>>」以降はGMヤフオクで項目のキーから外される } else { end(e, `<span style="display:none"> [</span><span class="ignoreMe relallArea" data-resno="${ep.id}"></span><span style="display:none">]</span>`) // 見えない「>>」以降はGMヤフオクで項目のキーから外される } } }) greenEleA.forEach(e => eleget0('.relallArea', e.closest(".post"))?.classList?.add("relallAreaon")) setTimeout(() => $('.back-links>a').remove(), 2000) // 回線が遅くてまれに本家ポップアップがダブリで付く現象の防止? function gatherRes(eWord = "", ele = null, find = 0) { if (find == 2) { // find=2なので高速版。>>nn形式のリストだけ作ってreturn let findEle = (typeof eWord === "string") ? // (mesEleA || []).filter(e => e.textContent.indexOf(eWord) !== -1 && Number(e.closest(".post").id) > Number(ele.closest(".post").id)) // ">>nn" 元のレスより前のレスは引用しない (mesEleA || []).filter(e => e.textContent.indexOf(eWord) !== -1) // ">>nn" 元のレスより前のレスも引用する : // (mesEleA || []).filter(e => e.textContent.match(eWord) && Number(e.closest(".post").id) > Number(ele.closest(".post").id)) // ">>nn" 元のレスより前のレスは引用しない (mesEleA || []).filter(e => e.textContent.match(eWord)) // ">>nn" 元のレスより前のレスも引用する if (!ele) return false; findEle.forEach(e => { eleget0('.relallArea', e.closest(".post"))?.classList?.add("relallAreaon") }) let mesA = mesEleA var list = (findEle.concat(greenEleA.filter(e => { var eWord = (e.textContent.match0(/^[>>]+(.+)$/m) || "").trim(); if (eWord) { var ep = e?.closest(".post") if (ep && ep != ele && ele?.textContent?.indexOf(eWord) !== -1 && Number(ep?.id) >= Number(ele?.id)) { eleget0('.relallArea', ep)?.classList?.add("relallAreaon") return true; } else { return null; } // >>ALLの場合元のレスより前のレスは引用しない } }))).map(c => c?.closest(".post")?.id) || [] return [...new Set(list)].sort((a, b) => a - b); } dc("order:" + eWord) var gathered = []; // 引用するレス要素(オリジナル)を入れていく配列 let mesA = mesEleA if (ele) { // eleが指定されているなら右肩の逆引用なので(eleレスにある文字列が引用されているレス|>>"eleのレス番"があるもの)も列挙して連結 gathered = gathered.concat([ele.closest(".post")]).concat(greenEleA.filter(e => { var eWord = (e.textContent.match0(/^[>>]+(.+)$/m) || "").trim() if (eWord) { var ep = e.closest(".post") if (ep != ele && ele.textContent.indexOf(eWord) !== -1 && Number(ep.id) >= Number(ele.id)) { return e.closest(".post"); } else { return null; } // >>ALLの場合元のレスより前のレスは引用しない // if (ep != ele && ele.textContent.indexOf(eWord) !== -1 ) { return e; } else { return null; } // >>ALLの場合元のレスより前のレスでも引用する } })) dc("<<逆引用:" + gathered.map(e => e.closest(".post").id)); } if (String(eWord).match0(/^>>\d+$/)) { // eWordが>>100のように「>>半角数字」だけの時は()>>レス番のレス|同じアンカーがあるレス)を連結 var ankNo = (String(eWord).match0(/^[>>]+(.+)$/m) || "").trim() dc(`本文>>数字(${String(eWord)}):${ ankNo}`) mesEleA.filter(e => (e.closest(".post").id == ankNo) || (e.textContent.match0(new RegExp(">>" + ankNo + "[^0-9]")))).forEach(e => { var b = e.closest(".post") //a.classList.add("rtdAttract"); //集めた元レスを目立たせる gathered.push(b); }) } else { gathered = gathered.concat((typeof eWord === "string") ? (mesEleA.filter(e => eWord ? e.textContent.indexOf(eWord) !== -1 : false) || false) : (mesEleA.filter(e => eWord ? e.textContent.match(eWord) : false) || false) ) dc("本文>>文字列:" + gathered.map(e => e.closest(".post").id)) } // dc(gathered) let resNumA = gathered.map(e => e.closest(".post")).map(e => e && e.id).filter(e => e).sort((a, b) => a - b) // 一旦レス番号に変換して若い順にソート&重複削除 resNumA = [...new Set(resNumA)]; dc(resNumA) let gathered2 = []; // 引用するレス要素(クローン)を入れていく配列 // >>文字列を含む列を列挙して連結 resNumA.forEach(e => { var b = $(eleget0(`//div[@id="${e}"]`)).clone(true, true); //a.classList.add("rtdAttract"); //集めた元レスを目立たせる gathered2.push(b); }) if (POPUP_STYLE) gathered2.forEach(b => $(b).css({ "display": "inline-block" })) else gathered2.forEach(b => $(b).css({ "display": "block", "padding": "1em 3em 0.3em 3em", "border-bottom": "1px solid gray", "margin": "0" })) var quoteDesEle0 = gathered2.length ? gathered2[0] : null; return [gathered2, quoteDesEle0, gathered2.map(c => Number(c[0].id))]; } document.addEventListener("mousemove", function(e) { // hover:: $('.post_hover').remove(); var hoverEle = (document.elementFromPoint(mousex, mousey)) if (!hoverEle) return let hoverPost = hoverEle?.closest(".post") let level = hoverEle.closest(".ch5pu")?.dataset.level + 0 || 0; if (latestHover != hoverEle) { mesEleA = elegeta('.thread .message,#thread-body .message').slice(0, 1000) if (hoverEle.className === "allpopup") { // >>ALL if (!hoverPost) return; var [gathered, quoteDesEle0, list] = gatherRes(new RegExp(`>>${hoverEle.closest(".post").id}[^0-9]`), hoverEle.closest(".post")) } else if (hoverEle.matches(".t5quote,.quoteSpeechBalloon")) { // 青緑色の引用文 var eWord = (hoverEle.textContent.match0(/^[>>]+(.+)$/m) || "").trim() || hoverEle.dataset.quoteno if (!eWord) return; if (/^\d+$/.test(eWord)) { var [gathered, quoteDesEle0, list] = gatherRes(">>" + eWord); if (gathered.length < 1) return; } else { var [gathered, quoteDesEle0, list] = gatherRes(eWord) if (gathered.length <= 1) return; } } if (hoverEle.className === "reply_link") { // 元からある逆引用の>>100のポップアップ var eWord = (hoverEle.textContent.match0(/^(>>\d+)/)).trim(); //alert(eWord) if (!eWord) return; var [gathered, quoteDesEle0, list] = gatherRes(eWord); if (gathered.length < 1) return; } if (hoverEle.parentNode.className === "back-links") { // 元からある逆引用の>>100のポップアップ var eWord = (hoverEle.textContent.match0(/(>>\d+)/)).trim(); //alert(eWord) if (!eWord) return; var [gathered, quoteDesEle0, list] = gatherRes(eWord); if (gathered.length < 1) return; } if (hoverEle.matches(".relallAreaon")) { let p3 = getRelatedRsca(hoverEle.dataset.resno) let p1 = p3.map(v => $(elegeta('.post').find(e => e.id == v)).clone(true, true)) let p2 = p1[0] if (POPUP_STYLE) p1.forEach(b => $(b).css({ "display": "inline-block" })) else p1.forEach(b => $(b).css({ "display": "block", "padding": "1em 3em 0.3em 3em", "border-bottom": "1px solid #aaa", "margin": "0" })) //if ($(p1).is(":hidden")) $(p1.closest(".post")).show(0).css({ "display": "table" }) // 学園祭で消していたら出す var [gathered, quoteDesEle0, list] = [p1, p2?.[0], p3] } if ((quoteDesEle0 && gathered.length && !eleget0(`//span[contains(@class,"ch5pu")][@data-list="${list.join(",")}"]`)) // 新しく黄土色文字か>>ALLの上に入った && (!eleget0(`//span[contains(@class,"ch5pu")][@data-list="${list.slice(1).join(",")}"]`))) { // 新しく黄土色文字か>>ALLの上に入った let xr = mousex > window.innerWidth * 2 / 3 let x = `` var pos = `position:absolute;` var hitpostsEle = `<span class="ch5pu ignoreFilter ignoreMe" id="ch5pu" data-level="${level+1}" data-list="${list.join(",")}" style="${pos}" ondblclick="this.remove();"></span>`; if (!hoverEle.matches(".relallAreaon") && gathered[0][0].id == hoverPost.id) { gathered.shift(); } // 先頭レスが引用元レスと同じ時だけは削除 let ch5pu = end(document.body, hitpostsEle) if (gathered.length) { gathered.forEach(e => { $(ch5pu).append(e) if (POPUP_STYLE) $(ch5pu).append("<br>") }) let puY = (window.scrollY + hoverEle.getBoundingClientRect()?.top + (hoverEle.getBoundingClientRect()?.height)) - (ch5pu.getBoundingClientRect()?.height / 2) + 2 let fitR = mousex - 16 + gbcr(ch5pu).width <= clientWidth() let fitD = gbcr(ch5pu).height <= (clientHeight() - gbcr(hoverEle).bottom) - 10 if (fitD && fitR) { ch5pu.style.left = `${mousex-16}px` ch5pu.style.top = `${(window.scrollY + hoverEle.getBoundingClientRect()?.bottom)}px` // ホバーの下縁 } else if (!fitD && fitR) { ch5pu.style.left = `${hoverEle.getBoundingClientRect().right-1}px` let puY = (window.scrollY + hoverEle.getBoundingClientRect()?.top) puY = Math.min(window.scrollY + window.innerHeight - gbcr(ch5pu).height - 10, puY) puY = Math.max(window.scrollY + 5, puY) ch5pu.style.top = `${puY}px` } else if (!fitR) { ch5pu.style.top = `${(window.scrollY + hoverEle.getBoundingClientRect()?.bottom)}px` // ホバーの下縁 ch5pu.style.right = `0px` } let eley = $(hoverEle).offset().top - $(window).scrollTop() + $(hoverEle).outerHeight() + 3; let marginheight = Math.min(window.innerHeight, document.documentElement.clientHeight) - eley let eleheight = $(hitpostsEle).height() if (marginheight / eleheight < 1) $(hitpostsEle).css({ "transform-origin": xr ? "top right" : "top left" }).delay(100).animate2({ "transform": `scale(${Math.max(CH5_QUOTE_POPUP_SCALING_LOWER_LIMIT,marginheight/eleheight)})`, "opacity": "1" }, 100) } } else { if (latestHover != hoverEle && !hoverEle.closest(".ftbpu,.ch5pu,.post_hover") && !quoteDesEle0) { //&& !eWord) { $(".ch5pu").remove(); //$(".rtdAttract").removeClass("rtdAttract") } if (latestHover != hoverEle && !hoverEle.closest(".allpopup,.reply_link") && hoverEle.closest(".ftbpu,.ch5pu,.post_hover")) { // levelが低い要素に降りたら上のは消す elegeta(".ch5pu").forEach(e => { if (e.dataset?.level > level) { e.remove() } }) } } } latestHover = hoverEle }, false); } // レス番rscに引用・非引用が連鎖するレスのレス番rscを全て配列で返す rsc => [rsc,rsc,rsc,...] function getRelatedRsca(rsc) { let tableRsc = elegeta('.post').sort((a, b) => a.id === b.id ? 0 : Number(a.id) > Number(b.id) ? 1 : -1) // レステーブル[登場順] let tableRscOrder = [] // レステーブル[rsc] tableRsc.forEach(v => tableRscOrder[v.id] = v) // 速度のためにキャッシュする let ress = getRelCno([+rsc], tableRsc, tableRscOrder) while (1) { let r = getRelCno(ress, tableRsc, tableRscOrder) if (r.length <= ress.length) { ress = r; break } ress = r } if (ress?.length < 2) return [] //null return ress.sort(new Intl.Collator("ja", { numeric: true, sensitivity: 'base' }).compare) } function getRelCno(resa, tableRsc, tableRscOrder) { let a = resa || [] resa.forEach(res => { a = a.concat(elegeta(`.allpopup`, tableRscOrder[res]).map(v => +v.innerText.replace(/^>>/, ""))) // 右肩の>>100のバックリンク let rscText = elegeta(`.reply_link`, tableRscOrder[res]).map(e => +e?.innerText?.replace(/^>+/, "")?.trim()) // 本文中の>>100の引用 rscText.forEach(v => { let hitno = tableRsc.find(w => w.id == v) if (hitno) a.push(+v) }) // let rscText2 = elegeta(`.t5quote`, tableRscOrder[res]).map(e => +e?.innerText?.replace(/^>+/, "")?.trim()) // 本文中の>○○の引用 let rscText2 = elegeta(`.t5quote`, tableRscOrder[res]).map(e => e?.innerText?.replace(/^>+/, "")?.trim()) // 本文中の>○○の引用 rscText2.forEach(v => { if (v.length > 3) { // 4文字以上でなければ無視 let hitno2 = tableRsc.filter(w => eleget0('.message', w).innerText.indexOf(v) !== -1).map(e => +e?.id) if (hitno2.length) a = a.concat(hitno2) } }) }) let ret = [...new Set(a?.flat())].filter(v => v !== undefined && v !== null).sort() return ret } function dc(str, force = 0) { if (debug || force) popup3(str, 0, 1, 5000, "top"); return str; } function popup3(text, i = 0, lf = 1, timer = 15000, alignY = "bottom") { if (text == undefined) text = "null" if (typeof text == "string") text = text.slice(0, 200); if (typeof text != "number") text = String(text); text = String(text) text = text.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/`/g, '`').replace(/</g, "<").replace(/>/g, ">").replace(/\n/gm, "<br>") let id = Math.random().toString(36).substring(2); var ele = $('<span id="yhmbox' + id + '" class="ignoreMe yhmpu3" style="all:initial;font-family:sans-serif; position: fixed; right:0em; ' + alignY + ':' + ((maey) + i * 18) + 'px; z-index:2147483647; opacity:1; font-size:15px; margin:0px 1px; text-decoration:none !important; max-width:33%; padding:1px 6px 1px 6px; word-break: break-all !important; border-radius:12px; background-color:#6080ff; color:white; ">' + text + '</span>').appendTo('body'); let ey = ele[0].getBoundingClientRect().height; if (ele[0].getBoundingClientRect().bottom >= (window.innerHeight)) { elegeta('.yhmpu3').forEach(e => { e.style.top = parseFloat(e?.style?.top) - (ey) - 2 + "px" }) } else { maey = (maey + (ele[0]?.getBoundingClientRect()?.height + 2)) } if (typeof text == "string") { maey += (text.match(/<br>/gmi) || []).length || 0; } //console.log((text.match(/<br>/gmi) || [] ).length) } setTimeout(() => { eleget0('//span[@id="yhmbox' + id + '"]').remove(); if (!eleget0('.yhmpu3')) maey = 0; }, timer); } function before(e, html) { e.insertAdjacentHTML('beforebegin', html); return e?.previousElementSibling; } function begin(e, html) { e.insertAdjacentHTML('afterbegin', html); return e?.firstChild; } function end(e, html) { e.insertAdjacentHTML('beforeend', html); return e?.lastChild; } function after(e, html) { e.insertAdjacentHTML('afterend', html); return e?.nextElementSibling; } function gbcr(e) { return e?.getBoundingClientRect() } function clientHeight() { return Math.min(document.documentElement.clientHeight, window.innerHeight) } function clientWidth() { return document.documentElement.clientWidth } function ct(callback, name = "test", time = 1) { console.time(name); for (let i = time; i--;) { callback() } console.timeEnd(name) } // 速度測定 function lh(re) { let tmp = location.href.match(re); if (!tmp) { return null } else if (tmp.length > 1) { return tmp[1] } else return tmp[0] } // gフラグ不可 })();