// ==UserScript== // @name 画像のホバーで拡大 // @description  Shift+H:オンオフ切替 ホバー中の画像を(y:yandexで画像検索/d:名前を付けて保存(+Shift:suffixを指定)) a:twitterで画像をトリミングさせない // @version 0.1.19 // @run-at document-idle // @match *://*/* // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_download // @grant GM.setClipboard // @noframes // @namespace https://greasyfork.org/users/181558 // @downloadURL none // ==/UserScript== (function() { const USE_D_TO_SAVE = 0; // 1:dキーでホバー中の画像を「名前を付けて保存」する 0:無効 const USE_SAVE_AS = false; // true:「名前を付けて保存」画面を使う false:ファイル名は自動的に決定(推奨) const hoverTimeDefault = 2; // デフォルトの動作モード -1:オフ 1:オン(瞬間) 2~:画像に乗せてn/60秒静止したら const defaultDelay = 3; // Shift+H押下時に切り替わる動作モードの3つ目(3/60秒)のタイミングを指定 const KEY_SELECT_DELAY = "Shift+H"; // 動作モード切り替えキー const CONFIRM_FOR_Y = 1; // 1:yキーでyandex検索をする時URLの確認を求める const D_FILENAME_MAXLENGTH = 94; // 1-94:dキーで自動生成されるファイル名の最大長 const FUTABA_REPLACE_THUMBNAIL_AFTER_HOVER = 1; // 1:ふたばでホバー後にインラインのgif画像をオリジナルに置き換える 2:gif以外の画像も置き換える 0:無効 const VIDEO_FADEIN_MS = 500; const REDIRECT_RE = /^https?:\/\/jump\.5ch\.net\/\?|^https?:\/\/jbbs\.shitaraba\.net\/bbs\/link\.cgi\?url=|^https?:\/\/talk\.jp\/c\?|^https?:\/\/.*\.2chan\.net\/bin\/jump\.php\?/; const IMGURL = /^https?:\/\/(www\.|i\.)?imgur\.com\/\S*(\.png|\.jpg|\.jpeg|\.gif|\.bmp|\.webp|\.webm|\.mp4)$|^https?:\/\/pbs\.twimg\.com\/media\/\S*(\.png|\.jpg|\.jpeg|\.gif|\.bmp|\.webp|\.webm|\.mp4)$/i; const marginH = 100; // 元絵と拡大画像の横の余白px const ZMARGIN = 1; const POPUP_Z_INDEX = 2147483647 - ZMARGIN; // ポップアップ画像がどくらい手前に来るか const FORCE_USE_HALF_WIDTH = 0; // 1:ホバーズームで必ず画面の横半分のサイズ const enablePanel = 1; // 1:パネル表示を有効 const marginPe = 8; let verbose = 0; // 1:debug const is2chan = ld("2chan.net|ftbucket.info|kuzure.but.jp") ? true : false; const is2chanOrig = ld("2chan.net") ? true : false; 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] } // gフラグ不可 var GF = {} let addstyle = { added: [], add: function(str) { if (this.added.some(v => v[1] === str)) return; var S = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" // var S="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" var d = Date.now() var uid = Array.from(Array(12)).map(() => S[Math.floor((d + Math.random() * S.length) % S.length)]).join('') end(document.head, ``); this.added.push([uid, str]); return uid; }, remove: function(str) { // str:登録したCSSでもaddでreturnしたuidでも良い let uid = this.added.find(v => v[1] === str || v[0] === str)?.[0] if (uid) { eleget0(`#${uid}`)?.remove() this.added = this.added.filter(v => v[0] !== uid) } } } var domain = new URL(location.href).hostname; var hovertime = pref(domain + " : delay") || hoverTimeDefault; var minWidth var minHeight const getwinmin = () => { minWidth = location.href.match(/twitter/) ? 1000 : (window.innerWidth / 2) minHeight = location.href.match(/twitter/) ? 1000 : (window.innerHeight / 2) } getwinmin() window.addEventListener("focus", getwinmin) window.addEventListener("resize", getwinmin) var mousex = 0; var mousey = 0; var lastEle = ""; var hovertimer = 0; var poppedUrl = ""; var yandexUrl = ""; var userSuffix = "" var keyFunc = USE_D_TO_SAVE ? [{ key: /^d$|^Shift\+D$/, func: (e, key) => { var url = poppedUrl var ele = document.elementFromPoint(mousex, mousey); var pdf = !lh(/https:\/\/www\.google\.co\.jp\/search\?q=/) ? null : ele.closest('a[href$="pdf"]') || eleget0('span.pdffile', ele?.closest('div')) ? ele?.closest('a') : null var pdffn if (pdf) { url = pdf.href; pdffn = pdf.innerText; GM.setClipboard(`${ele?.textContent||""}\n${url}\n`) } //if (hovertime < 1) return if (!url) { if (!(ele?.tagName === "IMG" || (ele?.tagName === "VIDEO" && ld('2chan.net')))) return; url = pe(ele, 0) } if (url) { var caption = getParentHasText(ele) function getParentHasText(ele) { while (ele && ele != document.body) { if (ele?.innerText?.replace(/\d/gm, "")?.replace(/ |\s\s|\n/gm, " ")?.trim()?.length > 0) return ele?.innerText?.replace(/ |\s\s|\n/gm, " ")?.trim() ele = ele?.parentNode } return "" } let userSuffix = "" if (key === "Shift+D") { let sufin = prompt("ファイル名の最後に付ける文字列を設定できます", window?.getSelection()?.toString()?.trim()?.substr(0, D_FILENAME_MAXLENGTH / 2) || userSuffix || "") if (sufin === null) return; userSuffix = sufin || "" } var fn_maxlen = D_FILENAME_MAXLENGTH - userSuffix?.length var hrefName = url.match(/[^\/]+\.[^\/]+$/)?.[0]?.replace(/\?.+$/, "")?.slice(-15) || "" var ext = ((/https:\/\/pbs\.twimg\.com\//.test(url) && /format=[a-zA-Z0-9]+/.test(url))) ? `.${url.match(/format=([a-zA-Z0-9]+)/)?.[1]}` : ""; if (hrefName == "" && ext == "") ext = pdf ? ".pdf" : ".png" let fnlen = fn_maxlen - hrefName.length var fn = (pdffn?.substr(0, fnlen - 1) || `${signzen(document.title.slice(0,fnlen/2)+" "+caption+" "+location.href).substr(0, fnlen-1)} `) + `${userSuffix}${hrefName}`?.trim() + `${ext}` if (ld(/5ch\.net$/)) { var res = eleget0("section,.message", ele?.closest("table,article"))?.innerText?.trim(); res = res?.replace(/^\>.*$|^h?ttps?\:\/\/.*$/gm, "")?.trim() || res; res = res?.replace(/\s+|\n+/gm, " ")?.trim(); if (res) fn = `${signzen(document.title+" "+location.href).substr(0, fn_maxlen/2-hrefName.length-1)} ${signzen(res).substr(0, fn_maxlen/2-1)} ${userSuffix}${hrefName}`.trim() + `${ext}` } if (ld(/2chan\..net$/)) { //var res = eleget0("blockquote", ele?.closest("table"))?.innerText?.replace(/\s+|\n+/gm," ")?.trim() //res = res?.replace(/^\s*\>.*\s*$/gm, "")?.trim() || res; // ?.replace(/キタ━+\(゚\∀゚\)━+\!+/gm,"") var res = eleget0("blockquote", ele?.closest("table"))?.innerText?.trim(); res = res?.replace(/^\>.*$/gm, "")?.trim() || res; res = res?.replace(/\s+|\n+/gm, " ")?.trim(); if (res) fn = `${signzen(document.title+" "+location.href).substr(0, fn_maxlen/2-hrefName.length-1)} ${signzen(res).substr(0, fn_maxlen/2-1)} ${userSuffix}${hrefName}`.trim() + `${ext}` } if (ld(/twitter\.com$/)) { hrefName = url.match(/\/media\/([^\/\?\&]+)/)?.[1] || "" // 絵IDだけど名前順≠出現順だろうか? var res = eleget0('[data-testid="tweetText"]', ele?.closest('[data-testid="cellInnerDiv"]'))?.innerText?.replace(/\s+|\n+/gm, " ")?.trim() if (res) fn = `${signzen(document.title).substr(0, fn_maxlen/2-hrefName.length-1)} ${signzen(res).substr(0, fn_maxlen/2-1)} ${userSuffix}${hrefName}`.trim() + `${ext}` } if (lh("//nitter.cz")) { hrefName = url.match(/\/enc\/([a-zA-Z0-9=]+)/)?.[1] || "" // 絵IDだけど名前順≠出現順だろうか? var res = ele?.closest('div.quote.quote-big,div.timeline-item')?.innerText?.replace(/\s+|\n+/gm, " ")?.trim() || document.title || ""; if (res) fn = `${signzen(res).substr(0, fn_maxlen-1-hrefName.length)} ${userSuffix}${hrefName}`.trim() + `${ext}` } if (lh("//nitter.net")) { hrefName = url?.match(/[a-zA-Z0-9\-_]+/g)?.sort((a, b) => a?.length < b?.length ? 1 : -1)?.[0] || "" // urlの中から[a-zA-z0-9]+にマッチする一番長い部分をIDと見て拾う ext = url.match0(/\.[a-z0-9]+$/) || "" var res = ele?.closest('div.quote.quote-big,div.timeline-item')?.innerText?.replace(/\s+|\n+/gm, " ")?.trim() || document.title || ""; if (res) fn = `${signzen(res).substr(0, fn_maxlen-1-hrefName.length)} ${userSuffix}${hrefName}`.trim() + `${ext||".png"}` } function signzen(str) { return str.replace(/^\s+/, "").replace(/\\|\/|\:|\;|\,|\+|\&|\=|\*|\?|\"|\'|\>|\<|\./g, c => { return String.fromCharCode(c.charCodeAt(0) + 0xFEE0) }) } var arg = { url: url, name: fn, saveAs: USE_SAVE_AS, onerror: null, onload: null, onprogress: null, ontimeout: null, }; if (ld(".pixiv.")) arg.headers = { referer: location.protocol + '//' + location.host, origin: location.protocol + '//' + location.host, } GM_download(arg); return true; } // d::ホバー中の画像を名前を付けて保存 } }] : []; if (lh("//twitter.com/|//nitter.net/|//nitter.cz/")) { var zflag = pref("zflag") || 0; setTimeout(() => setsize(zflag), 1999) } function setsize(z) { addstyle.remove(GF?.twi); if (z == 1) GF.twi = addstyle.add(`div{ background-size:${['cover','contain'][z]} !important;}`) addstyle.remove(GF?.nit); if (z == 1) GF.nit = addstyle.add(`.still-image img:not([class*="quote"] img){ object-fit:contain !important; max-height:300px !important;}`) } //document.addEventListener("keypress", e => { document.addEventListener("keydown", e => { if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.isContentEditable || ((e.target.closest('#chat-messages,ytd-comments-header-renderer') || document.activeElement.closest('#chat-messages,ytd-comments-header-renderer')))) return; var key = (e.shiftKey ? "Shift+" : "") + (e.altKey ? "Alt+" : "") + (e.ctrlKey ? "Ctrl+" : "") + e.key; keyFunc.forEach(v => { if (v.key.test(key)) { if (v.func(e, key)) { e.stopPropagation(); e.preventDefault(); } } }) if (key === "y" && poppedUrl) { // y::ホバー中の画像をyandex画像検索で検索 yandexUrl = poppedUrl.match(/\;base64\,/i) ? null : "https://yandex.com/images/search?rpt=imageview&url=" + poppedUrl; if (!CONFIRM_FOR_Y || window.confirm(yandexUrl + "\n\nを開きます。よろしいですか?")) window.open(yandexUrl); return false; } if (key === "a" && lh("//twitter.com|//nitter.net/|//nitter.cz/")) { // a::twitterで画像をトリミングさせないオンオフ zflag = zflag ^ 1; setsize(zflag) pref("zflag", zflag) popup(`a:画像をフィット:${zflag}`) return false; } if (key === KEY_SELECT_DELAY) { // Shift+H::ホバーの設定 elegeta('//img[@class="ignoreMe hzP"]').forEach(e => e.remove()); // let ret = proInput(domain + `\n\nのホバーズームの設定をしてください\n\n-1:オフ\n0:デフォルト(${hoverTimeDefault})\n1:画像に乗った瞬間\n2-:画像に乗せて数値/60秒静止したら\n空欄:設定削除(${hoverTimeDefault})\n\n`, pref(domain + " : delay") || defaultDelay); let ret = hovertime == -1 ? 1 : hovertime == 1 ? defaultDelay : -1; if (ret == 1) verbose = 1 - verbose if (ret === "") { pref(domain + " : delay", ""); hovertime = hoverTimeDefault; } else { if (ret !== null) { hovertime = ret || hoverTimeDefault; pref(domain + " : delay", hovertime); popup(`${domain}
${KEY_SELECT_DELAY}:ホバーズームを${hovertime!=-1?"有効":"無効"}にしました${hovertime>0?"("+hovertime+"/60秒)":""}`, hovertime > 0 ? verbose ? "#362" : "#6080ff" : "#808080") } } } }, true); document.addEventListener("mousemove", function(e) { mousex = e.clientX; mousey = e.clientY; hovertimer = 0; }, false); document.addEventListener("wheel", function(e) { hovertimer = 0; }, false); hzOnInt() function hzOnInt() { if (!(hovertime < 1)) { hovertimer++; let ele = document.elementFromPoint(mousex, mousey); // if(logg)document.title=`${hovertime} ${hovertimer} ${mousex},${mousey} ${lastEle},${ele} ${poppedUrl} ${ele.clientWidth}<${minWidth} ${ele.clientHeight}<${ minHeight}` if (lastEle !== ele) { hovertimer = 0; if (poppedUrl) { document?.querySelectorAll('.ignoreMe.hzP')?.forEach(e => e.remove()); poppedUrl = ""; } } if (ele && hovertimer >= hovertime && poppedUrl == "" && (ele.clientWidth < minWidth || ele.clientHeight < minHeight)) { if ((ele.tagName === "IMG" || (ele.tagName === "VIDEO" && is2chan)) || (ele?.style?.backgroundImage?.match0(/^url\(/)) || //(ele?.matches(`a[href*="://i.imgur.com/"],a[href*="://pbs.twimg.com/media/"]`) && ele?.href?.match(/.png$|.jpg$|.jpeg$|.gif$|.bmp$|.webp$/i))) { (ele?.matches(`a[href*="imgur.com"],a[href*="pbs.twimg.com"]`) && ele?.href?.match(/.png$|.jpg$|.jpeg$|.gif$|.bmp$|.webp$/i))) { if (ele?.dataset?.zoomonhover != "disable") { poppedUrl = pe(ele, enablePanel); // yandexUrl = poppedUrl.match(/\;base64\,/i) ? null : "https://yandex.com/images/search?rpt=imageview&url=" + poppedUrl; } } } if (FUTABA_REPLACE_THUMBNAIL_AFTER_HOVER && is2chanOrig) { //if (ele && is2chan && ele.tagName === "IMG" && ele.matches('.thre>a>img[href*=".gif"]:not([data-gifloaded]),td.rtd>a[href*=".gif"]>img:not([data-gifloaded])')) { // 2chanならgifを動かす if (ele && ele.tagName === "IMG" && (FUTABA_REPLACE_THUMBNAIL_AFTER_HOVER >= 2 ? ele?.matches('.thre>a[href*=".gif"]>img:not([data-gifloaded]),.thre>a[href*=".png"]>img:not([data-gifloaded]),.thre>a[href*=".jpg"]>img:not([data-gifloaded]),.thre>a[href*=".webp"]>img:not([data-gifloaded]),td.rtd>a[href*=".gif"]>img:not([data-gifloaded]),td.rtd>a[href*=".png"]>img:not([data-gifloaded]),td.rtd>a[href*=".jpg"]>img:not([data-gifloaded]),td.rtd>a[href*=".webp"]>img:not([data-gifloaded])') : ele?.matches('.thre>a[href*=".gif"]>img:not([data-gifloaded]),td.rtd>a[href*=".gif"]>img:not([data-gifloaded])'))) { // 2chanならgifを動かす let a = ele?.closest("a") if (a && a?.href != ele?.src && eleget0('#contdisp')?.textContent?.indexOf("スレッドがありません") == -1) { //if (a && a?.href != ele?.src) { ele.dataset.gifloaded = 1 ele.src = a.href; } } } lastEle = ele; } setTimeout(hzOnInt, 17); } function pe(a, panel) { if (/magnifierLens/.test(a.id)) return null; // amazon拡大鏡の水色のドット let imgAspect = a.tagName == "VIDEO" ? a.videoWidth / a.videoHeight : a.naturalWidth / a.naturalHeight; // svg等だとNaN 要.onload let clientAspect = window.innerWidth / 2 / window.innerHeight; let ahref = (decodeURIComponent(a?.href)?.replace(REDIRECT_RE, "")) if (ahref && !(IMGURL.test(ahref))) ahref = ""; // 動画 if (a.tagName === "VIDEO") { var src = a?.src || eleget0('source', a)?.src if (src && !a?.paused) return src; if (src && panel) { var ve = document.createElement("video") var panel = document.createElement("span"); panel.referrerpolicy = "no-referrer"; // 無効 panel.className = "ignoreMe hzP"; panel.appendChild(ve) panel.src = src panel.animate([{ opacity: 0 }, { opacity: 1 }], { duration: VIDEO_FADEIN_MS, fill: 'forwards' }); ve.outerHTML = ``; panel.maxWidth = "18%"; } } else if (a?.dataset?.srcvideo || (a.parentNode.tagName == "A" && a.parentNode.href.match(/.mp4(\?.+)*$|.webm(\?.+)*$/i))) { var ext = (a?.dataset?.srcvideo || a.parentNode.href).match(/.mp4(\?.+)*$|.webm(\?.+)*$/i)[0]; //console.log(ext,a.parentNode.href) var src = a?.dataset?.srcvideo || a.parentNode.href; if (!src) return; // ? if (panel) { var ve = document.createElement("video") var panel = document.createElement("span"); panel.referrerpolicy = "no-referrer"; // 無効 panel.className = "ignoreMe hzP"; panel.appendChild(ve) panel.src = src panel.animate([{ opacity: 0 }, { opacity: 1 }], { duration: VIDEO_FADEIN_MS, fill: 'forwards' }); ve.outerHTML = ``; panel.maxWidth = "18%"; } } else if (a.style.backgroundImage?.match0(/^url\(/)) { // bacgground-image式の画像 var src = (a.style.backgroundImage.match0(/url\(\"([^"]+)/)) if (!src) return; // ? if (panel) { var panel = document.createElement("img"); panel.referrerpolicy = "no-referrer"; panel.className = "ignoreMe hzP"; panel.src = src } } else { // 画像 var src = a?.src || ahref; if (a.parentNode && a.parentNode.tagName == "A" && a.parentNode.href.match(/\/pic\/orig\/|.png$|.jpg$|.jpeg$|.gif$|.bmp$|.webp$/i) && /\.wikipedia\./.test(location.href) == false) { src = a.parentNode.href; } else if (a.parentNode && a.parentNode.parentNode && a.parentNode.parentNode.tagName == "A" && a.parentNode.parentNode.href.match(/.png$|.jpg$|.jpeg$|.gif$|.bmp$|.webp$/i)) { src = a.parentNode.parentNode.href; } if (a.srcset) { var srcWiki = a.srcset.match(/\/\/\S+/g); if (srcWiki.length) { srcWiki = srcWiki[srcWiki.length - 1].replace(/\/\d+px-.*$/i, "").replace(/\/thumb\//i, "/"); } // alert(srcWiki)} } if (ld(/5ch\.net$|shitaraba\.net$|^talk\.jp$/)) src = decodeURIComponent(src)?.replace(REDIRECT_RE, "") //if(src.match0(/.+?\.2chan\.net\/b\/thumb\/.+?s\..+?/)){src=src.replace(/(.+?\.2chan\.net\/b\/)(?:cat|thumb)(\/.+?)s(\..+?)/,"$1src$2$3")} // 拡張子が分からない //if(/https:\/\/i\.pximg\.net\/[^/]+\/[^/]+\/[^/]+\/img\/.+/.test(panel.src)) {panel.src=panel.src.replace(/(https:\/\/i\.pximg\.net\/)[^/]+\/[^/]+\/([^/]+\/img\/.+)/,"$1$2").replace(/custom-thumb/,"img-master").replace(/_custom/,"_master");} // pixiv if (/^https?:\/\/.+\.2chan\.net\/b\/cat\//.test(src)) { src = src.replace(/^(https?:\/\/.+\.2chan\.net\/b\/)cat\//, "$1thumb/"); } // futapo if (src.match0(/\?w=\d+\&h=\d+$/)) src = src.replace(/\?w=\d+\&h=\d+$/, ""); src = srcWiki || src.replace(/(https:\/\/pbs\.twimg\.com\/.*\?format=.+\&name=).+/, "$1orig"); if (!src) return; // ? if (panel) { var panel = document.createElement("img"); panel.referrerpolicy = "no-referrer"; panel.className = "ignoreMe hzP"; panel.src = src } } if (panel) { 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 aw = (mousex < document.documentElement.clientWidth / 2) ? (document.documentElement.clientWidth - (a.getBoundingClientRect()).right) : (a.getBoundingClientRect().left); let imgAspect2 = a.tagName == "VIDEO" ? a.videoHeight / a.videoWidth : a.naturalHeight / a.naturalWidth; // svg等だとNaN 要.onload let info1 = `${sani(src)} ${a.naturalWidth}×${a.naturalHeight} ar=1:${Math.round(imgAspect2*100)/100}` let info2 = ` → ${Math.floor(window.innerWidth*0.48-marginPe)}×${Math.floor(window.innerWidth*0.48/imgAspect-marginPe)}` let info2css = ` width:${Math.floor(window.innerWidth*0.48-marginPe)}px; height:${Math.floor(window.innerWidth*0.48/imgAspect-marginPe)}px;` if (imgAspect && (((aw - marginH) / imgAspect) <= window.innerHeight && (aw - marginH - marginPe) > a.width * 2.5)) { panel.setAttribute("style", `all:initial;float:none; width:${aw-marginH-marginPe}px; height:${(aw-marginH)/imgAspect-marginPe}px;${boxPos} z-index:${POPUP_Z_INDEX}; position:fixed; ${peStyle}`); // 元絵の左右にくっつき最大 if (verbose) popup(`${info1} くっつき`) } else if (imgAspect && ((window.innerHeight * imgAspect - marginPe) < aw - marginH)) { if (imgAspect < 0.46 && !ve) { // 48%+スクロール panel.setAttribute("style", `all:initial;float:none; ${info2css} ${boxPos} top:0px; z-index:${POPUP_Z_INDEX}; position:fixed; ${peStyle}`); // 48%+スクロール if (window.innerWidth * 0.48 / imgAspect - marginPe > window.innerHeight) setTimeout(() => { panel.animate([{ top: '0px' }, { top: `-${window.innerWidth*0.48/imgAspect-marginPe-window.innerHeight+marginPe}px` }], { duration: (window.innerWidth * 0.48 / imgAspect - marginPe - window.innerHeight + marginPe) * 4, fill: 'forwards' }); }, 1000) if (verbose) popup(`${info1} ${info2} 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 (verbose) popup(`${info1} 縦目いっぱい`) } } else if (!imgAspect || window.innerWidth * 0.48 / imgAspect - marginPe <= window.innerHeight) { panel.setAttribute("style", `all:initial;float:none; ${info2css} ${boxPos} z-index:${POPUP_Z_INDEX}; position:fixed; ${peStyle}`); // 横48% // panel.setAttribute("style", `all:initial;float:none; ${info2} ${boxPos} z-index:${POPUP_Z_INDEX}; position:fixed; ${peStyle}`); // 横48% if (verbose) popup(`${info1} ${info2} 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 (verbose) popup(`${info1} 縦目いっぱい2`) } if (a.clientWidth < document.documentElement.clientWidth * 0.48) document.body.appendChild(panel); if (ahref) panel.addEventListener('load', e => { setSizeImg(e.target, e.target, a); }); } // if (a?.matches(`a[href*="://i.imgur.com/"],a[href*="://pbs.twimg.com/media/"]`) && a?.href?.match(/.png$|.jpg$|.jpeg$|.gif$|.bmp$|.webp$/i)) panel.addEventListener('load', e => { setSizeImg(e.target, e.target, a); }); return src; } function setSizeImg(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 (verbose) 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 (verbose) 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 (verbose) 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 (verbose) popup(`余り左右:${amariWidth} naturalWidth:${a.naturalWidth} naturalHeight:${a.naturalHeight} a:${Math.round(imgAspect*100)/100} 縦目いっぱい2`) } } function elegeta(xpath, node = document) { if (!xpath || !node) return []; let flag if (!/^\.?\//.test(xpath)) return /:inscreen$/.test(xpath) ? [...node.querySelectorAll(xpath.replace(/:inscreen$/, ""))].filter(e => { var eler = e.getBoundingClientRect(); return (eler.top > 0 && eler.left > 0 && eler.left < document.documentElement.clientWidth && eler.top < document.documentElement.clientHeight) }) : /:visible$/.test(xpath) ? [...node.querySelectorAll(xpath.replace(/:visible$/, ""))].filter(e => e.offsetHeight) : [...node.querySelectorAll(xpath)] try { var array = []; var ele = document.evaluate("." + xpath.replace(/:visible$/, ""), 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 /:visible$/.test(xpath) ? array.filter(e => e.offsetHeight) : array; } catch (e) { popup3(e + "\n" + xpath + "\n" + JSON.stringify(node), 1); return []; } } function eleget0(xpath, node = document) { if (!xpath || !node) return null; if (!/^\.?\//.test(xpath)) return /:visible$/.test(xpath) ? [...node.querySelectorAll(xpath.replace(/:visible$/, ""))].filter(e => e.offsetHeight)[0] ?? null : node.querySelector(xpath.replace(/:visible$/, "")); try { var ele = document.evaluate("." + xpath.replace(/:visible$/, ""), 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 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 prefRestrict(name, type) { let data = pref(name); if (!data) return data; if (type === "array") { if (typeof data === "object") { return data; } else { alert("データベースの形式に誤りがある(配列/オブジェクトでない)ためクリアします\n"); pref(name, ""); return []; } } return data; } function proInput(prom, defaultval, min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER) { let ret = window.prompt(prom, defaultval) if (ret === null) return null; if (ret === "") return ""; return Math.min(Math.max(Number(ret.replace(/[A-Za-z0-9.-]/g, function(s) { return String.fromCharCode(s.charCodeAt(0) - 65248); }).replace(/[^-^0-9^\.]/g, "")), min), max); } function popup(text, color = "#6080ff") { text = String(text); var e = document.getElementById("hzbox"); if (e) { e.remove(); } var e = document.body.appendChild(document.createElement("span")); e.innerHTML = `/gm, "\\n") }\"; document.body.appendChild(a); a.select(); document.execCommand(\"copy\"); a.parentElement.removeChild(a);\'">${ text }`; setTimeout((function(e) { return function() { e.remove(); } })(e), 5000); } 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フラグ不可 function ld(re) { let tmp = location.hostname.match(re); if (!tmp) { return null } else if (tmp.length > 1) { return tmp[1] } else return tmp[0] } // gフラグ不可 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 sani(s) { return s?.replace(/&/g, "&")?.replace(/"/g, """)?.replace(/'/g, "'")?.replace(/`/g, '`')?.replace(//g, ">") || "" } })();