// ==UserScript== // @name 5chサムネイル表示他 // @description r:ホバー中のレスへのアンカーを記入(R:追記) c(文字列非選択時):ホバー中のレスを引用 c(文字列選択時):選択文字列を引用 .:ホバー中のレス以降を表示 ,:ホバー中のレスの10個前からを表示 Esc:書き込み欄にスクロール F:re.Find2chで検索 // @version 0.1.4 // @run-at document-idle // @match *://*.5ch.net/test/read.cgi/* // @grant GM_addStyle // @grant none // @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 https://greasyfork.org/users/181558 // @downloadURL none // ==/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 debugTimer = 0; // 1ならかかった時間を計測 const inlineImageThumbnailPreloadRadius = Math.max(window.innerHeight, 1080); // 画面外の上下何ピクセルまでを「画面内」とするか const ALTERNATIVE_THUMBNAIL = 0; // 1:別方式のサムネイル // 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; } // レスにホバーしてキー入力 $(document).on("keypress", e => { if (/input|textarea/i.test(e.target.tagName)) return; if ($('form>p>textarea').length) { if (e.key == "r" || e.key == "R") { // r::レスにアンカー R::レスにアンカー(追記) let num = getNearest('//span[@class="number"]'); if (num.innerText >= 2 || (num.innerText == 1 && $(window).scrollTop() < 500)) { $(getNearest('//div[@class="message"]/..')).effect("highlight", 500); floatKakikomi(">>" + num.innerText + "\n", e.key == "R"); $('form>p>textarea').effect("highlight", 500); } return false; } if (e.key === "c") { // c::レスを引用 let num = getNearest('//span[@class="number"]'); let res = getNearest('//div[@class="message"]'); if (num.innerText >= 2 || (num.innerText == 1 && $(window).scrollTop() < 500)) { 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; $(getNearest('//div[@class="message"]/..')).effect("highlight", 500); floatKakikomi(">>" + num.innerText + "\r\n" + (selectedStr ? selectedStr : (res.innerText.replace(/^>.*$\n/gm, "").replace(/^(.+)$/gm, ">$1"))) + "\r\n"); $('form>p>textarea').effect("highlight", 500); } return false; } } if (e.key === "." || e.key === ",") { // .::そのレス以降を表示 ,::そのレスの10個前以降を表示 let num = getNearest('//span[@class="number"]'); if (num) { let num2 = (num.innerText) - (e.key === "," ? 10 : 0); $(getNearest('//div[@class="message"]/..')).effect("highlight"); location.href = location.href.replace(/\/l\d+$/, "").replace(/\/[0-9\-]{0,4}-?[0-9n]{0,4}?$/gm, "/").replace(/(\d)$/, "$1/") + Math.max(1, num2) + "-"; } return false; } if (e.key === "F") { // F::re.Find2chで検索 let query = prompt("re.Find2chでキーワード検索します\n\nhttps://refind2ch.org/search?q=${キーワード}&sort=rate\n\n"); if (query) window.open(`https://refind2ch.org/search?q=${query}&sort=rate`); return false; } }); $(document).on("keydown", e => { if (/input|textarea/i.test(e.target.tagName)) return; if ($('form>p>textarea').length) { if (e.key == "Escape") { // Esc::書き込み欄にスクロール scrollKakikomi(false); return false; } } }); var mousex = 0; var mousey = 0; document.addEventListener("mousemove", function(e) { mousex = e.clientX; mousey = e.clientY; }, false); function getNearest(xpath) { let ele = document.elementFromPoint(mousex, mousey); return getEleFromParent(ele, xpath); } function getEleFromParent(ele, xpath) { // ele要素の親をさかのぼりxpathを持つ要素 for (let i = 0; i < 9; i++) { var ele2 = elegeta(xpath, ele); if (ele2.length) return ele2[0]; if (ele === document) return; ele = ele.parentNode; } return; } var resFloat = false; // 書き込み欄までスクロール function scrollKakikomi(loop = true) { if (resFloat) return; var ele = eleget0('//div[@class="formbody"]/form/p/textarea'); if (ele) { var $target = $('p>input.submitbtn'), offset = $target.offset() || { top: 0, left: 0 }, outerHeight = $target.outerHeight(); $("html,body").animate({ scrollTop: (offset.top - window.innerHeight + outerHeight) }); } else if (loop) setTimeout(scrollKakikomi, 500); } setTimeout(hNukiURLHokan, WAIT); setTimeout(scrollKakikomi, WAIT); setTimeout(() => { setInterval(videoUmekomi, WAIT_VIDEO_EMBED_INTERVAL); setInterval(imageUmekomi, WAIT_IMAGE_EMBED_INTERVAL); //$(eleget0('//p/input[@name="mail"]')).css("ime-mode", "inactive"); let ma = $(xa('//input[@placeholder="メールアドレス(省略可)"]')); ma.dblclick(() => { ma.val(ma.val() == "sage" ? "" : "sage"); floatKakikomi(); }); // 細かい調整 $(".mascot").attr("style", ""); $("div.formbox").css("margin", "0"); $('input[placeholder="メールアドレス(省略可)"]').val(DEFAULT_MAIL_ADDRESS); //$(eleget0('//p/input[@name="mail"]')).css("ime-mode", "inactive"); $("form>p>textarea").click(() => { floatKakikomi(); }); }, WAIT); // h抜きのURLをリンクにする function hNukiURLHokan() { document.querySelectorAll("div.post").forEach(function(obj) { var html = obj.innerHTML; if (obj.innerText.match(/^ttps?:\/\//gm)) { var newhtml = html.replace(/[^h](ttps?:\/\/\S+)/gm, "$1"); obj.innerHTML = newhtml; } }); } // 書き込み欄調整、クリックでフロート化 function floatKakikomi(str = "", addMode = 0) { resFloat = true; $(eleget0('//form/p/textarea[@name="MESSAGE"]')).css({ "z-index": "999", "position": "fixed", "right": "0em", "bottom": "3em" }); $(eleget0('//input[@class="submitbtn btn"]')).css({ "z-index": "9999999", "position": "fixed", "right": "0em", "bottom": "0em" }); str ? $("form>p>textarea").val((addMode ? $("form>p>textarea").val() + "\r\n" : "") + str) : 0; $(eleget0('//p/textarea[@name="MESSAGE" and @wrap="off"]')).css({ "width": "70%", "min-width": "900px" }).keyup(() => { kakikomiStretch(); }); $(eleget0('//form/p/textarea[@name="MESSAGE"]')).focus(); kakikomiStretch(); } function kakikomiStretch() { let target = eleget0('//p/textarea[@name="MESSAGE"]'); let lineHeight = Number(target.getAttribute("rows")); //document.title=target.scrollHeight+" / "+ target.offsetHeight while (target.scrollHeight >= target.offsetHeight) { lineHeight++; target.setAttribute("rows", lineHeight); } } // 画像と動画をインライン埋め込み function imageUmekomi() { sw("umegazo") // 画像埋め込み var i = 0; hoverZoomHttp(); // if (ALTERNATIVE_THUMBNAIL) { for (let a of elegeta('//a[not(contains(@href,"twimg"))]/div[@div="thumb5ch"]')) a.remove(); } if (ALTERNATIVE_THUMBNAIL) { for (let a of elegeta('//a/div[@div="thumb5ch"]')) a.remove(); } // for (let ele of elegeta(ALTERNATIVE_THUMBNAIL ? '//a[not(@imge)][not(contains(@href,"twimg"))]' : '//a[not(@imge) and not(contains(text(),"http"))][not(contains(@href,"twimg"))]').sort((a, b) => { for (let ele of elegeta(ALTERNATIVE_THUMBNAIL ? '//a[not(@imge)]' : '//a[not(@imge) and not(contains(text(),"http"))]').sort((a, b) => { return Math.abs(a.getBoundingClientRect().top - document.documentElement.clientHeight / 2) > Math.abs(b.getBoundingClientRect().top - document.documentElement.clientHeight / 2) ? 1 : -1 })) { if ((!isinscreen(ele))) continue; // 画面内に無い var url = $(ele).text(); url = url.replace(/^(ttps?:\/\/)/m, "h$1"); if (url.match(/.jpg$|.png$|.gif$|.bmp$/)) { $(ele).after($('
')); ele.setAttribute("imge", "imge"); if (enableHoverZoom) { setTimeout(() => { for (let a of elegeta('//a/img[not(@hz)]')) { if (a.parentNode) { a.parentNode.addEventListener("mouseover", function() { popupEle(a, a.parentNode) }, false); a.setAttribute("hz", "hz") } } }, 100) } if (++i >= NUMBER_IMAGE_EMBED_AT_ONCE) break; // 一度に設定枚数ずつしかやらない } } se("umegazo") }; function hoverZoomHttp() { if (enableHoverZoom) { setTimeout(() => { for (let a of elegeta('//img[@class="thumb_i"][not(@hz)]')) { if (a.parentNode) { a.parentNode.addEventListener("mouseover", function() { popupEle(a, a.parentNode, a.parentNode.parentNode.innerText); }, false); a.setAttribute("hz", "hz") } } }, 100) } } function videoUmekomi() { sw("umedouga") // ニコ動埋め込み(PrivacyBadger等は要Disable) for (let ele of elegeta('//a[contains(@href,"nicovideo")][not(@nde)]')) { if ((!isinscreen(ele))) continue; // 画面内に無い let url = ele.innerText.replace(/^ttp/i, "http"); ele.setAttribute("nde", "nde"); let nico = url.match(/h?ttps?:\/\/www.nicovideo.jp\/watch\/(.*)/i); if (!nico) continue $(ele).after('
'); break; // 一度に1つずつしかやらない } se("umedouga") sw("umeYT") // youtube埋め込み for (let ele of elegeta('//a[contains(@href,"youtube.com")][not(@yte)]|//a[contains(@href,"youtu.be")][not(@yte)]')) { if ((!isinscreen(ele))) continue; // 画面内に無い let url = ele.innerText; ele.setAttribute("yte", "yte"); var sm = (url.match(/h?ttps?:\/\/youtu\.be\/([^&?]+.*)\?t=(\d*).*$/i) || url.match(/h?ttps?:\/\/w?w?w?m?\.youtube\.com\/watch\?v=([^&]+.*)&t=(\d*).*$/i)) || url.match(/h?ttps?:\/\/youtu\.be\/([^&?]+)/i) || url.match(/h?ttps?:\/\/w?w?w?m?\.youtube\.com\/watch\?v=([^&]+)/i); if (!sm) continue $(ele).after('