// ==UserScript==
// @name 5chサムネイル表示他
// @description r:ホバー中のレスへのアンカーを記入 c(文字列非選択時):ホバー中のレスを引用 c(文字列選択時):選択文字列を引用 .:そのレス以降を表示 Esc:書き込み欄にスクロール
// @version 0.1.2
// @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; // Math.random() > 0.5 ? 1 : 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") { // 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");
$('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 == ".") { // .::そのレス以降を表示
let num = getNearest('//span[@class="number"]');
if (num) {
$(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/") + num.innerText + "-";
}
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);
scrollKakikomi();
setTimeout(() => {
setInterval(videoUmekomi, WAIT_VIDEO_EMBED_INTERVAL);
setInterval(imageUmekomi, WAIT_IMAGE_EMBED_INTERVAL);
$(eleget0('//p/input[@name="mail"]')).css("ime-mode", "inactive");
// 細かい調整
$(".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 = "") {
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(str) : 0;
$(eleget0('//p/textarea[@name="MESSAGE" and @wrap="off"]')).css({ "width": "70%", "min-width": "900px" }).keyup(() => {
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);
}
});
$(eleget0('//form/p/textarea[@name="MESSAGE"]')).focus()
}
// 画像と動画をインライン埋め込み
function imageUmekomi() {
sw("umegazo")
// 画像埋め込み
var i = 0;
hoverZoomHttp();
//if (!ALTERNATIVE_THUMBNAIL) hoverZoomHttp();
//if (ALTERNATIVE_THUMBNAIL) { for (let a of elegeta('//div[@div="thumb5ch"]')) a.remove(); }
//for (let ele of elegeta(ALTERNATIVE_THUMBNAIL ? '//a[not(@imge)]' : '//a[not(@imge) and not(contains(text(),"http"))]').sort((a, b) => {
if (ALTERNATIVE_THUMBNAIL) { for (let a of elegeta('//a[not(contains(@href,"twimg"))]/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) => {
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).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('