// ==UserScript== // @name ヤフオクで非表示とメモ // @description q:非表示 w:アンドゥ b:NGワード Shift+Q:NG編集 12:メモを追加 34:自由メモ 56:定型文をメモ Shift+!:メモを編集 Shift+":自動メモのみ全削除 Shift+#:メモを一時非表示 Shift+56:定型文を設定 .:上限価格 t:半透明モード // @version 0.5.113 // @match *://auctions.yahoo.co.jp/search/* // @match *://page.auctions.yahoo.co.jp/jp/auction/* // @match *://auctions.yahoo.co.jp/seller/* // @match *://auctions.yahoo.co.jp/category/list/* // @match *://auctions.yahoo.co.jp/jp/auction/* // @match *://cpu.userbenchmark.com/* // @match *://webcomics.jp/* // @match *://www.amazon.co.jp/* // @exclude *://www.amazon.co.jp/*/cart/* // @exclude *://www.amazon.co.jp/*/buy/* // @exclude *://www.amazon.co.jp/*/huc/* // @exclude *://www.amazon.co.jp/*/css/* // @exclude *://www.amazon.co.jp/ap/* // @exclude *://www.amazon.co.jp/gp/* // @exclude *://www.amazon.co.jp/auto-deliveries* // @match *://booklive.jp/index/no-charge* // @match *://ebookjapan.yahoo.co.jp/free* // @match *://ebookjapan.yahoo.co.jp/ranking/free/* // @match *://ebookjapan.yahoo.co.jp/ranking/details/free/* // @match *://ebookjapan.yahoo.co.jp/viewer* // @match *://sokuyomi.jp/* // @match *://csbs.shogakukan.co.jp/free* // @match *://www.rtings.com/*/tools/table* // @match *://www.rtings.com/*/reviews/ * // @match *://www.nicovideo.jp/search/* // @match *://www.nicovideo.jp/tag/* // @match *://www.nicovideo.jp/user/* // @match *://www.nicovideo.jp/series/* // @match *://www.nicovideo.jp/my/* // @match *://www.nicovideo.jp/ranking* // @match *://www.nicovideo.jp/watch/* // @match *://*.5ch.net/* // @match *://www.ebay.com/sch/* // @match *://www.ebay.com/itm/* // @match *://jmty.jp/* // @match *://greasyfork.org/*/scripts* // @match *://*.aliexpress.com/af/* // @match *://*.aliexpress.com/item/* // @match *://*.aliexpress.com/wholesale* // @match *://www.cmoa.jp/freecontents* // @match *://piccoma.com/* // @match *://www.mangaz.com/* // @match *://www.sukima.me/* // @match *://*.userbenchmark.com/* // @match *://www.hellowork.mhlw.go.jp/kensaku/* // @match *://kakaku.com/* // @match *://manga.nicovideo.jp/* // @match *://tsugimanga.jp/* // @match *://www.yodobashi.com/* // @match *://www.youtube.com/* // @match *://*.iherb.com/* // @match *://www.suruga-ya.jp/* // @match *://twitter.com/* // @match *://www.nicovideo.me/* // @match *://www.nicochart.jp/* // @match *://pubmed.ncbi.nlm.nih.gov/?term=* // @match *://pubmed.ncbi.nlm.nih.gov/?linkname=* // @match *://pubmed.ncbi.nlm.nih.gov/* // @match *://a-timesale.com/* // @match *://rrws.info/* // @match *://commons.nicovideo.jp/* // @match *://scholar.google.tld/* // @match *://hibiki-radio.jp/* // @match *://www.onsen.ag/ // @match *://www.freem.ne.jp/* // @match *://shopping.yahoo.co.jp/search* // @match *://360life.shinyusha.co.jp/* // @match *://omocoro.jp/* // @match *://minsoku.net/* // @match https://refind2ch.org/search* // @match *://chiebukuro.yahoo.co.jp/* // @match *://www.msdmanuals.com/* // @match *://ff5ch.syoboi.jp/?q=* // @match *://todo-ran.com/* // @match *://booth.pm/* // @match *://sakura-checker.jp/category/* // @match https://chrome.google.com/webstore/search/* // @match https://chrome.google.com/webstore/detail/* // @match *://twicomi.com/* // @match *://twiman.net/* // @match *://free.arinco.org/* // @match *://workman.jp/shop/* // @match *://www.uniqlo.com/* // @match *://*.shitaraba.net/bbs/read.cgi/* // @match *://*.shitaraba.net/bbs/read_archive.cgi/* // @match *://*.ftbucket.info/* // @match *://kuzure.but.jp/* // @match *://*.2chan.net/* // @match *://anige.horigiri.net/* // @match *://futapo.futakuro.com/* // @match *://kako.futakuro.com/futa/* // @match https://kakaku.com/pc/note-pc/itemlist.aspx // @match https://kakaku.com/pc/desktop-pc/itemlist.aspx // @match *://btopc-minikan.com/* // @match *://pcfreebook.com/* // @match *://search.bilibili.com/* // @match *://www.mcdonalds.co.jp/menu/* // @match *://*.2chan.net/b/futaba.php* // @match *://nitter.cz/* // @match *://nitter.net/* // @match *://xcancel.com/* // @match *://www.uexpress.com/* // @match https://find.5ch.net/search // @match *://tsumanne.net/*/data/* // @match *://zzzsearch.com/* // @match *://review.kakaku.com/review/* // @match *://www.netoff.co.jp/* // @match *://jp.daisonet.com/* // @match *://search3.vector.co.jp/* // @match *://alternativeto.net/* // @match file:///*.html // @match *://www.google.co.jp/* // @match *://radiko.jp/* // @match *://mangahack.com/* // @match *://rookie.shonenjump.com/* // @match *://shonenjumpplus.com/series* // @match *://shonenjumpplus.com/episode* // @match *://kuragebunch.com/series* // @match *://www.sunday-webry.com/series* // @match *://duckduckgo.com/* // @match *://futafuta.site/thread/* // @match *://parupunte.net/logbox/detail.html* // @match *://www.eiyoukeisan.com/* // @noframes // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_addStyle // @grant GM.addStyle // @grant GM.setClipboard // @grant GM.openInTab // @run-at document-idle // @namespace https://greasyfork.org/users/181558 // @require https://code.jquery.com/jquery-3.7.1.min.js // @require https://code.jquery.com/ui/1.14.1/jquery-ui.min.js // @downloadURL https://update.greasyfork.icu/scripts/386645/%E3%83%A4%E3%83%95%E3%82%AA%E3%82%AF%E3%81%A7%E9%9D%9E%E8%A1%A8%E7%A4%BA%E3%81%A8%E3%83%A1%E3%83%A2.user.js // @updateURL https://update.greasyfork.icu/scripts/386645/%E3%83%A4%E3%83%95%E3%82%AA%E3%82%AF%E3%81%A7%E9%9D%9E%E8%A1%A8%E7%A4%BA%E3%81%A8%E3%83%A1%E3%83%A2.meta.js // ==/UserScript== (function() { const FUTABA_WEBP_LOSSY_QUALITY = 0.8; // ふたばでクリップボードの写真ぽいpng画像を非可逆webpにする場合の品質のデフォルト vキーで変更可 通常:0.8 const FUTAPO_CRAM_TITLE = 1; // 1:futapoでboxの横幅が狭いときでもタイトルを詰め込む 0:無効 const FUTAPO_89_NOTIFY = "notify"; // futapoで8/9登録がヒットした時にどう通知するか "notify"でnotification API(推奨)、"sound"で効果音。"notify sound"で両方、""で通知しない const FUTABA_SET_56MEMO_TO_ANCHORED = 3; // 1:ふたばで監視ワードヒットレスと自分のレスに自動的に5メモを付ける 2:1に加え5メモへのレスに6メモを付ける 3:1+2に加え遠くても5メモに連鎖するレスには6メモをつける(推奨) 4:1+2+3に加え遠くても6メモに連鎖するレスにも6メモをつける const FUTABA_NOTIFY_NEWRES_SOUND_MEMO_QUOTED = "5 6 m"; // ふたばで新着レスのNotification通知でメモのついたレスが引用された時に音で知らせるか "5"で○メモに鳴らす、"6"で×メモに鳴らす、"m"で監視ワードに鳴らす、""で鳴らさない、"5 6 m"などで複数設定可能 const FUTABA_Z_TO_COPY_TO_CLIPBOARD_AS_TEXT_TOO = "${num1} ${name1} ${name2} ${time} ${num2} ${soudane}\n${text}\n"; // ふたばでzキーかレス右上の□のクリック時についでに内容をテキストとしてクリップボードにコピーする、その書式 "":コピーしない const FUTABA_FLOAT_RELOAD_BUTTON = 1; // 1:ふたばでリロードボタンを浮遊させる 0:無効 const FUTABA_HOVER_POPUP_REPLACE = 1; // 1:ふたばで>引用ポップアップを置き換える 0:無効 const FUTABA_HOVER_POPUP_DELAY = 1; // ふたばで>引用の上に静止してn/60秒間後にポップアップを表示する -1:瞬間&低負荷(mousemove) 1:瞬間(interval) 2~:n/60秒(interval) 0:無効 const FUTABA_REPLACE_RES0 = 1; // 1:ふたばでスレ本文をレス番号0のように置き換える 引用ポップアップ等が働くために必要 0:無効 const FUTABA_QUOTE_LEAD_FOR_NUMBER_ONLY = 1; // 1:ふたばで>no.○○や>○○.jpgや>fu○○.jpgといった引用にも本文のバルーン引用を追加する(要「5chサムネイル表示他」併用) 0:無効 const FUTABA_AUTO_RELOAD_INTERVAL = 1; // 1-10:ふたばでnキーオン時の自動リロードする最短間隔(分) ※新着がなく無操作だと自動的に10分まで伸びる const FUTABA_ALTZ_FILENAME_SUFFIX = "●"; // ふたばでAlt+Z時にファイル名の末尾に付ける文字 const FUTABA_PLAY_GIF_INLINE = 1; // 1:ふたばのgifをインラインで動かす 0:無効 const FUTABA_POPUP_PADDING_RATE = 169; // ふたばの引用ポップアップの額縁の太さ(大きいほど細い) const FUTABA_REMOVE_REDIRECT_PAGE = 0; // 1:ふたばでリンクからクッションページを省略 const FUTABA_EXPERIMENTAL_REMOVE_METADATA_FROM_UPFILE = () => 1; // ()=>1:ふたばでD&DとCtrl+V時の添付ファイルからメタデータを除去する ()=>0:除去しない const FUTABA_YOUTUBE_PLAYALL_BUTTON = 1; // 1:ふたばでYouTubeのリンクの動画を小窓で連続再生するボタンを設置 2:1をuキーで実行 0:無効 const FUTABA_PICK_IMG_MIN_SIZE = 60; // ふたばでピックアップの最小画像サイズ const FILENAME_MAXLENGTH = 94; const FUTABA_FORWARD_LINK = 0; // 2:ふたばで若い方向の引用も>>2のホバーで表示する 2:1+ふたばで1<<形式のフォワードリンクも付ける 0:無効 const FUTABA_DEBUG = 0; // 1-3:ふたばで開発用情報を表示 1:リロードボタン 2:1+最下行ログ 3:1+2+タブタイトル 0:無効 const KEYCHANGE_DEBUG = "disable"; // 押す度にdebugを0~3に切り替えるキー "Shift+J","disable"等 const IN5CH_REMOVE_AKABAN = 1; // 1:5chで垢版ボタンをとりあえず隠す 0:そのまま const dniCancel = 1; // DNIがobserve(ms)以上連続して発火しても途切れるのを待ち続ける const DEBUG_CATCH = 0; // 1:catchしたエラーをalert 0:無効 const ENABLE_HELP = 1; // 1:操作ガイドを表示 0:無効 const ENABLE_AUTOMEMO = 7; // 0:自動メモを無効 1でオン 2~7:大きくするほど一時的に表示が崩れる代わりに確実 const REPLACE_LINK_IN_YOUTUBE = 1; // 1:YouTubeで投稿者のチャンネルのホームタブへのリンクをチャンネルの動画タブへのリンクに置き換える const ENABLE_EXCEPT_YAJ = 1; // 1:ヤフオク以外でも有効 0:ヤフオクでのみ動作 var debug = 0; // 1~だとデバッグモード V&&dc(text) 1:非対応ページでその旨表示など/コンソールにverbose表示 2:verboseをポップアップ(速度測定して表示 debug&&sw("項目"))&非表示にした原因に枠追加 3:Q/1/2等の対象要素を目立たせる const ENABLE_MEASURE_TIME_SPENT = 0; // 1で速度測定して表示 debug&&sw("項目") const CUSTOMAUTOMEMORE = [].concat([/(Windows.*bit)/gmi, /((?:HDD|HDD|SSD|SSD|ハードディスク|ストレージ).*?(?:GB|TB|GB|TB|無し|なし|無|欠品|欠))/mi, /((?:10|テン)(?:key|キー))/mi, /(768.{1,3}576)|(800.{1,3}600)|(832.{1,3}624)|(1024.{1,3}768)|(1152.{1,3}864)|(1,?280.{1,3}960)|(1400.{1,3}1050)|(1440.{1,3}1080)|(1600.{1,3}1200)|(2048.{1,3}1536)|(2304.{1,3}1728)|(3200.{1,3}2400)|(854.{1,3}480)|(1024.{1,3}576)|(1136.{1,3}640)|(1280.{1,3}720)|(1,?36\d?.{1,3}768)|(1,?920.{1,3}1,?080)|(2048.{1,3}1152)|(2,?560.{1,3}1,?440)|(3200.{1,3}1800)|(3,?840.{1,3}2,?160)|(7680.{1,3}4320)|(640.{1,3}400)|(1280.{1,3}800)|(1,?440.{1,3}900)|(1680.{1,3}1050)|(1,?920.{1,3}1,?200)|(2560.{1,3}1600)|(2880.{1,3}1800)|(3840.{1,3}2400)|(480.{1,3}320)|(960.{1,3}640)|(176.{1,3}144)|(400.{1,3}240)|(352.{1,3}288)|(640.{1,3}350)|(720.{1,3}480)|(800.{1,3}480)|(864.{1,3}480)|(1024.{1,3}480)|(1024.{1,3}600)|(1280.{1,3}600)|(1120.{1,3}750)|(1280.{1,3}768)|(1152.{1,3}870)|(1280.{1,3}1024)|(1,?600.{1,3}900)|(1,?600.{1,3}1024)|(2048.{1,3}1080)|(4096.{1,3}2160)|(8192.{1,3}4320)/m, /(非光沢|ノングレア|ノーグレア|アンチグレア|光沢|グレア)/m, /((?:30|40)\s?(?:pin|ピン))/mi, /(LVDS.*$|eDP)/mi, /^(.*リカバリ.*)$/mi, /(仕事率[^0-90-9\n]*[0-9,0-9,]*\s*[wW])/mi, /(\[?\s?関東[^0-9,0-9,\n]*[0-9,0-9,]*\s*円[-~]?)/m, /(\[?\s?本州[^0-9,0-9,\n]*[0-9,0-9,]*\s*円[-~]?)/m, /(HDMI.{0,10}入力.{2,3}|入力.{0,20}HDMI.{2,3})/mi, /(電源入り.{2,10})/mi, /(\d\d鍵)/m]); const fasttest = 1; // 1:高速モードを試用 const FUTABA_BGC = 0 ? "#f0e0d6" : "#ffffee" const ISCHROME = window.navigator.userAgent.toLowerCase().indexOf('chrome') != -1 var futabapopupscale; const futabapicksizeDefault = 90 var futabapicksizeL = futabapicksizeDefault; var futabapicksize = 100; var hovertimer = 0; var swb = new Date(); var prefCache = [] GM_addStyle("span.yhmMyMemo{all:initial; word-wrap:break-word;cursor:pointer; font-size:14px; font-weight:bold; margin:0px 1px; text-align:center; padding:0px 6px 0px 6px; border-radius:12px; color:white;font-family:sans-serif;}") debug && sw("reset") 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フラグ不可 String.prototype.match1 = function(re) { return this?.match(re)?.slice(1)?.find(v => v) } // this./a(bc)|d(ef)/ 等の()でキャプチャした最初の1つを返す gフラグ不可 //String.prototype.nfd = function() { return SITE.nfd ? this?.normalize("NFD") : String(this) } // 文字列をNFDエンコードにまとめる String.prototype.sanit = function() { return this.replace(//g, ">").replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/`/g, '`') } const waitFrame = () => new Promise(resolve => requestAnimationFrame(resolve)); function adja(place = document.body, pos, html) { return place ? (place.insertAdjacentHTML(pos, html), place) : null; } 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('') document.head.insertAdjacentHTML("beforeend", ``); 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 SITE = {} let inYOUTUBE = location.hostname.match0(/^www\.youtube\.com|^youtu\.be/); $.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 (window != parent) return; const WAIT = performance.now(); // ページ開始後のウエイト用 delayAutoWeightingをかける const OLD_COLOR1 = "#6080ff", OLD_COLOR2 = "#c03020", OLD_COLOR3 = "#808080", OLD_COLOR5 = "#6080ff", OLD_COLOR6 = "#c03020", OLD_COLORVIDEOTIME = "#204020", OLD_COLORCPUSCORE = "#a08000", COLOR1 = "#57f", //"#6080ff", COLOR2 = "#c32", //"#c03020", COLOR3 = "#888", //"#808080", COLOR5 = "#57f", //"#6080ff", COLOR6 = "#c32", //"#c03020", COLORVIDEOTIME = "#242", //"#204020", COLORCPUSCORE = "#970", //"#a08000", COLOR_ALERT_WORD = "#882", //"#a08000", KEYHIDE = "q", KEYUNDO = "w", KEYBW = "b", KEYEDIT = "Shift+Q", // KEYEDIT2 = "Shift+B", KEYMAXP = ".", KEYMEMO1 = "1", KEYMEMO2 = "2", KEYMEMO1S = "3", KEYMEMO2S = "4", KEYMEMO5 = "5", KEYMEMO6 = "6", KEYMEMO5EDIT = "Shift+%", KEYMEMO6EDIT = "Shift+&", KEYTOGGLEtranslucent = "t", KEYRESETMEMO = "Shift+!", KEYRESETMEMOAUTO = "Shift+\""; var MEMO5WORD = "", // 5キーの定型文初期値 ""なら現在年月日 MEMO6WORD = ""; // 6キーの定型文初期値 ""なら現在年月日 var pauseAll = 0; let kaisuuU = 0; var GF = {} GF.yhmSortType = 0 GF.isNico = location.host == "www.nicovideo.jp" var memofast = false; // 上が優先 const SITEINFO = [ // @match *://www.eiyoukeisan.com/* { id: 'www.eiyoukeisan.com', urlRE: '//www.eiyoukeisan.com/', //title: 'td', // box: 'tr', }, { id: 'duckduckgo.com', urlRE: '//duckduckgo.com/', title: 'span.tile--img__title', box: 'div.tile.tile--img.has-detail', }, { id: 'WCA', urlRE: '//www.sunday-webry.com/series', title: 'h4.test-series-title.series-title , p.author', box: 'li.webry-series-item.test-series', }, { id: 'WCA', urlRE: '//kuragebunch.com/series', title: 'li.page-series-list-item > div > div > a.series-data-container > h4 , a.series-data-container > h5', box: 'li.page-series-list-item', }, { id: 'WCA', urlRE: '//shonenjumpplus.com/series|//shonenjumpplus.com/episode', title: 'h2.series-list-title , h1.episode-header-title , a > h3.series-list-author', box: 'li.series-list-item , div.episode-header-container', redoWhenRefocused: 1, memoFunc: e => e, forceTranslucentFunc: e => lh("/episode"), }, { id: 'rookie.shonenjump.com', urlRE: () => lh('//rookie.shonenjump.com/') && !eleget0('a.page-navigation-backward.js-slide-backward'), title: 'h1.series-title', box: 'section.series-contents', }, { id: 'mangahack.com', urlRE: '//mangahack.com/', title: 'div.episodeArea:nth-child(2 of div.episodeArea) > h1 , p[class*="title"] > span > a , div.comicTitle_toppage > h1', box: 'div.oneColumn.pb00.pl00.pr00.pt00 , div#stored.comicList_box.cf , div.mainContents', observe: 500, funcOnlyFirst: () => setInterval(() => eleget0('i.fa.fa-angle-down:visible:inscreen')?.click(), 1000), }, { id: 'radiko.jp', urlRE: '//radiko.jp/', title: 'p.img-list__title.ellipsis', box: 'li.img-list__item', observe: 2222, }, { id: 'www.google.co.jp', urlRE: /\/\/www\.google\.co\.jp\/.*\&tbm\=shop/, title: '.tAxDx , div > div.aULzUe', box: '.sh-dgr__grid-result', }, { id: 'alternativeto.net', urlRE: '//alternativeto.net/', title: 'h1[class*="Heading_h1___"] , h2[class*="Heading_h2___"] > a , div > a[class*="AppItemBox_appName__"]', box: 'main#mainContent > section , div[class*="AppItem_shared_itemBox__"] , div[class*="PageIntroWrapper_noClouds__"] > div:last-of-type', }, { id: 'vector.co.jp', urlRE: '//search3.vector.co.jp/', title: 'h2 > a.url', box: 'div[class*="hreview"]', wholeHelp: [() => 1, " A:ソート"], keyFunc: [{ key: 'a', // a:: func: () => { var sorttype = GF.yhmSortType || 0 let menu = [ { t: "新しい", f: () => { sortdom(elegeta(SITE.box), v => eleget0('p.footer > span:nth-of-type(1)', v)?.textContent + eleget0('.board_title span', v)?.textContent, 1) } }, { t: "古い", f: () => { sortdom(elegeta(SITE.box), v => eleget0('p.footer > span:nth-of-type(1)', v)?.textContent + eleget0('.board_title span', v)?.textContent) } }, { t: "評価", f: () => { sortdom(elegeta(SITE.box), v => eleget0('span[class*="rating"]', v)?.className || 0, 1) } }, { t: "タイトル", f: () => { sortdom(elegeta(SITE.box), v => eleget0(SITE.title, v)?.textContent) } }, ] popup2("A:ソート\n" + (menu.map((c, i) => " " + c.t + (i == sorttype ? " ←\n" : "\n")).join("")), 6, `min-width:${menu.reduce((p,c)=>Math.max(p,c.t.length+3),0)}em;`); menu[sorttype].f() GF.yhmSortType = (++sorttype) % menu.length return } }], }, { id: "DAISONET", urlRE: '//jp.daisonet.com/', title: 'h1.product-meta__title.heading.h1.text--strong , a.product-item__title', box: 'div.product-block-list , div.product-item--vertical', forceTranslucentFunc: e => lh('daisonet.com/products/'), redoWhenRefocused: 1, }, { id: `NETOFF`, urlRE: '//www.netoff.co.jp/', box: 'li.clearfix', title: 'p > a.fw', wholeHelp: [() => 1, " A:ソート"], keyFunc: [{ key: 'a', // a:: func: () => { var sorttype = GF.yhmSortType || 0 let menu = [ { t: "安い→新しい", f: () => { sortdom(elegeta('li.clearfix'), v => +eleget0('.price', v)?.textContent?.replace(/,/g, "")?.match0(/\d+/) * 1000000000 + (1000000 - +(eleget0('.subinfo', v)?.textContent?.replace(/(\d+).(\d+).*/, "$1$2")))) } }, ] popup2("A:ソート\n" + (menu.map((c, i) => " " + c.t + (i == sorttype ? " ←\n" : "\n")).join("")), 6, `min-width:${menu.reduce((p,c)=>Math.max(p,c.t.length+3),0)}em;`); menu[sorttype].f() GF.yhmSortType = (++sorttype) % menu.length } }] }, { id: 'KAKAKU', urlRE: 'https://review.kakaku.com/review/', title: 'h2[itemprop="name"]', box: 'div#main', forceTranslucentFunc: e => 1, memoFunc: e => e?.parentNode?.parentNode, }, { id: 'zzzsearch.com', urlRE: '//zzzsearch.com/', title: '//div[@class="gs-title"]/a[@dir="ltr"]', box: 'div.gsc-webResult.gsc-result', observe: 333, keyFunc: [{ key: 'Shift+F', // Shift+F::refind2chでキーワード検索 func: () => { searchWithHistory("find5ch,掲示板横断検索,re.find2ch,ff5ch", "find5ch,掲示板横断検索,re.find2ch,ff5ch", [`https://zzzsearch.com/bbs/#gsc.q=%22***%22&gsc.sort=date`, `https://refind2ch.org/search?q=***&sort=rate`, 'https://find.5ch.net/search?q=***', `https://ff5ch.syoboi.jp/?q=***`], " OR ") }, }], }, { id: 'uexpress', urlRE: /\/\/www\.uexpress\.com\//, keyFunc: [{ key: 'Shift+F', // Shift+F::キーワード検索 func: () => { searchWithHistory("uexpress", "UExpress", 'https://www.google.co.jp/search?q=***%20site:www.uexpress.com/life/miss-manners', "|") }, }], }, { id: 'nitter', urlRE: /\/\/nitter\.cz\/|\/\/nitter.\.net\/|\/\/xcancel.com\//, title: '.username', box: '.timeline-item', observe: 500, }, { id: 'futaba_catalog', urlRE: /https?:\/\/[^\.]+\.2chan\.net\/[^/]+\/futaba\.php\?mode=cat/, title: '.tdDiv small', box: '#cattable tbody tr td', isHidePartialMatch: 1, titleSubstr: true, hideSelectedWord: 1, disableKeyB: 0, listTitleXPIgnoreNotExist: 1, funcOnlyFirst: () => { $('#cattable td').wrapInner('
') // レス数とvボタンを下の帯に固定 elegeta('.pdmc').forEach(e => e.closest('td')?.appendChild(e)) addstyle.add(`.pdmc { margin-top:auto; margin-bottom:auto; right: 0em; bottom:0.2em; position:absolute; z-index:99; opacity:1; padding:0.15em; background-color:#ffffee; }`) GF.imgw = Math.max(eleget0('#cattable img')?.offsetWidth, eleget0('#cattable img')?.offsetHeight) || 40 GF.boxw = eleget0('#cattable td')?.offsetWidth || 40 setSlider(eleget0('//body/span[1]'), 0, 320, 0, "画像サイズ:***px", "futaba_catalog_imagesize", (val) => { eleget0('#imagesizecss')?.remove() $("#boxs").remove(); GF.imgw = val if (val) end(document.head, ``) if (val) { end(document.head, ``) } }, 0, '') setSlider(eleget0('#setSliderfutaba_catalog_imagesize'), 0, 300, 0, "box高さ:***px", "futaba_catalog_boxheight", (val) => { eleget0('#futaba_catalog_imagesize')?.remove() if (val) end(document.head, ``) // レス数とvボタンを下の帯に固定 if (val) addstyle.add(`#cattable img{border:black solid 1px;}`); //$("#cattable img").wrap('
') eleget0('#futaba_catalog_imagesize')?.remove() }, 0, '') setSlider(eleget0('#setSliderfutaba_catalog_imagesize'), 0, 300, 0, "box横幅:***px", "futaba_catalog_boxwidth", (val) => { eleget0('#boxwidthcss')?.remove() $("#boxs").remove(); $('#cattable td a+br').remove() GF.boxw = val - 5 if (val) end(document.head, ``) if (val) { end(document.head, ``) } }, 0, '') setSlider(eleget0('#setSliderfutaba_catalog_imagesize'), 0, 24 * 3, 0, "文字サイズ:***/3px", "futaba_catalog_fontsize", (val) => { eleget0('#textsizecss')?.remove() if (val) end(document.head, ``) }, 0, '') }, }, { id: 'search.bilibili.com', urlRE: '//search.bilibili.com/', listTitleXP: '//h3[@class="bili-video-card__info--tit"]', listTitleSearchXP: '//h3[@class="bili-video-card__info--tit"][**title**]/ancestor::div[contains(@class,"video-list-item")]', listTitleMemoSearchXP: '//h3[@class="bili-video-card__info--tit"][**title**]', listGen: 7, observe: 999, useText: 1, }, { id: 'mcdonaldsmenu', urlRE: '//www.mcdonalds.co.jp/menu/', keyFunc: [{ key: 'a', // a:: func: () => { var sorttype = GF.yhmSortType || 0 let menu = [ { t: "安い順", f: () => { sortdom(elegeta('//span[@class="product-list-card-price-number text-2xl font-extrabold"]/../../..'), v => eleget0('//span[@class="product-list-card-price-number text-2xl font-extrabold"]', v)?.textContent) } }, { t: "高い順", f: () => { sortdom(elegeta('//span[@class="product-list-card-price-number text-2xl font-extrabold"]/../../..'), v => eleget0('//span[@class="product-list-card-price-number text-2xl font-extrabold"]', v)?.textContent, 1) } }, { t: "タイトル", f: () => { sortdom(elegeta('//span[@class="product-list-card-price-number text-2xl font-extrabold"]/../../..'), v => eleget0('.product-list-card-name', v)?.textContent) } }, ] popup2("A:ソート\n" + (menu.map((c, i) => " " + c.t + (i == sorttype ? " ←\n" : "\n")).join("")), 6, `min-width:${menu.reduce((p,c)=>Math.max(p,c.t.length+3),0)}em;`); menu[sorttype].f() GF.yhmSortType = (++sorttype) % menu.length return } }], func: () => popup3("A:ソート", 5), }, { id: 'pcfreebook.com', urlRE: '//pcfreebook.com/', listTitleXP: '//td[1]/a[@target="_blank" and @rel="noopener nofollow noreferrer"]', listTitleSearchXP: '//td[1]/a[@target="_blank" and @rel="noopener nofollow noreferrer"][+++]/ancestor::tr', listTitleMemoSearchXP: '//td[1]/a[@target="_blank" and @rel="noopener nofollow noreferrer"][+++]', listGen: 3, listTitleMemoSearchXPSameGen: 1, listTitleXPIgnoreNotExist: 1, observe: 2500, }, { id: 'btopc-minikan.com', urlRE: '//btopc-minikan.com/', titleProcessFunc: (title) => { return title.replace(/\n|\s/gmi, " ").trim() }, listTitleXP: '//span[@class="itemname"]', listTitleSearchXP: '//span[@class="itemname"][++title++]/ancestor::tr', listTitleMemoSearchXP: '//span[@class="itemname"][++title++]', listTitleMemoSearchXPSameGen: 1, useTitle: 1, listGen: 4, func: () => { elegeta('//td[1]/div[@class="notranslate"]|.//td[@class="CPUgrease-GraphCell-Maker"]').forEach(e => e.insertAdjacentHTML("beforeend", ` `)) }, listTitleXPIgnoreNotExist: 1, observe: 2500, }, { urlRE: /^https:\/\/kakaku.com\/pc\/note-pc\/itemlist\.aspx|https:\/\/kakaku\.com\/pc\/desktop-pc\/itemlist.aspx|\/\/kakaku.com\/pc\/gaming-pc\/itemlist.aspx|\/\/kakaku.com\/pc\/gaming-note\/itemlist.aspx|\/\/kakaku.com\/pc\/stick-pc\/itemlist.aspx/, WhateverFirstAndEveryAPFunc: () => { if (!elegeta('//th[@class="sub thHeader" and contains(text(),"CPUスコア(PassMark) ")]')[0]) return; popup3("A:CPUスコア単価で絞り込み", 4) //$(".cps").remove() // let arr = lh("gaming") ? elegeta('//table/tbody/tr[@class="tr-border"]/td[11]') : elegeta('//table/tbody/tr[@class="tr-border"]/td[10]') let arr = lh("gaming") ? elegeta('table tbody tr.tr-border td:nth-child(11):not([data-scorecost])') : elegeta('table tbody tr.tr-border td:nth-child(10):not([data-scorecost])') arr.forEach(e => { e.dataset.scorecost = 1 // let p = elegeta('//td[2]/ul/li[@class="pryen"]/a', e?.closest("tr"))[0] let p = elegeta('td:nth-child(2) ul li.pryen a', e?.closest("tr"))[0] let b = e.parentNode let cpu = parseInt(e.innerText.replace(/\D/g, "")) let price = parseInt(p.innerText.replace(/\D/g, "")) if (e.textContent.match(/\d/)) { e.insertAdjacentHTML("beforeend", `
価格/スコア
${((price/cpu).toFixed(2))}
`) } else { end(e, '') } }) }, keyFunc: [{ key: 'a', // a:: func: () => { if (!elegeta('//th[@class="sub thHeader" and contains(text(),"CPUスコア(PassMark) ")]')[0]) return; GF.base = proInput("CPUスコア単価上限を入力してください", $('#CPS').data("cps") || 10) || 0; autoPagerizedD("cpsAP", () => { $('#CPS').remove(); $(document.body).append(``) $(elegeta('table tbody tr.tr-border')).show() if (GF.base <= 0) return elegeta('.cputanka').forEach(e => { let cps = Number(e?.innerText || 0) let b = e.closest("tr.tr-border") if (!cps || cps > GF.base) { $([b, b.previousSibling, b.previousSibling.previousSibling]).hide() } }) }) } }, ], }, { urlRE: /\/\/www.ftbucket.info\/scrapshot\/ftb\/index\.php|\/\/www.ftbucket.info\/scrapshot\/ftb\/?$|\/\/www.ftbucket.info\/scrapshot\/ftb\/\?favo=/, id: 'FUTACHAN_CATALOG', listTitleXP: '//div[@class="tdDiv"]/span/div', listTitleSearchXP: '//div[@class="tdDiv"]/span/div[***]/../..', listTitleMemoSearchXP: '//td/div[@class="tdDiv"]/span/div[***]', listGen: 5, listTitleXPIgnoreNotExist: 1, listGen: 4, WhateverFirstAndEveryAPFunc: () => { popup3("Shift+F:FTBucket検索", 8, 5000) }, // WhateverFirstAndEveryAPFunc: () => { SITEINFO[SITEINFO.findIndex(c => c.id == "FUTACHAN")].WhateverFirstAndEveryAPFunc() }, keyFunc: [{ key: 'Shift+F', // Shift+F::FTBucketでキーワード検索 func: () => { searchWithHistory("FUTACHAN", "FTBucketとふたば★さーち", ['https://www.ftbucket.info/scrapshot/ftb/index.php?mode=c&favo=0&ord=1&s=***', `https://zzzsearch.com/futaba/#gsc.q=***&gsc.sort=date`], "|") }, //https://zzzsearch.com/futaba/#gsc.q=%E8%87%AA%E7%82%8A&gsc.sort=date // func: () => { SITEINFO[SITEINFO.findIndex(c => c.id == "FUTACHAN")].keyFunc[0].func() }, }], // memoStyle:'display:inline-block;', //listTitleMemoSearchXPSameGen: 1, delay: 333, funcOnlyFirst: () => { //if (!lh("mode=c")) return; eleget0('//html/body/table[2]')?.setAttribute("id", "cattable") $('#cattable td').wrapInner('
') begin(document.body, `




`) $('#cattable td>a>img').css({ "border": "black solid 1px" }).wrap('
') // $('#cattable td').wrapInner('
') setSlider(eleget0('#sliders4'), 0, 300, 0, "box高さ:***px", "futabucket_catalog_boxheight", (val) => { eleget0('#boxheightcss')?.remove() if (val) end(document.head, ``) }, 0, '') setSlider(eleget0('#sliders3'), 0, 500, 0, "box横幅:***px", "futabucket_catalog_boxwidth", (val) => { eleget0('#boxwidthcss')?.remove() $('#cattable td a+br').remove() if (val) end(document.head, ``) if (val) elegeta('td div span[title] div').forEach(e => e.textContent = e.parentNode.title) }, 0, '') //setSlider(eleget0('#sliders'), 1, 24 * 3, 12 * 3, "文字サイズ:***/3px", "futabucket_catalog_fontsize", (val) => { // eleget0('#textsizecss')?.remove() // end(document.head, ``) //}, 0, '') setSlider(eleget0('#sliders2'), 0, 320, 0, "画像サイズ:***px", "futabucket_catalog_imagesize", (val) => { eleget0('#imagesizecss')?.remove() if (val) end(document.head, ``) }, 0, '') setSlider(eleget0('#sliders1'), 0, 20, 0, "タイトル行数:***行", "futabucket_catalog_titlerow", (val) => { eleget0('#catalogrowscss')?.remove() if (val) end(document.head, ``) if (val) elegeta('td div span[title] div').forEach(e => e.textContent = e.parentNode.title) }, 0, '') /*elegeta('//div/input[@type="search" and @id="searchword"]').forEach(e => e.style.width = "calc(100% - 4em)") // 検索フォームを大きく if (location.href.match0("//www.ftbucket.info/scrapshot/ftb/")) { setSlider(eleget0('//html/body/div[2]/a/img/..'), 4, 255, 16, "タイトル表示:***文字", "FTBTitleLength", (val) => elegeta('//td/span[@title]/div').forEach(e => e.textContent = e.parentNode.title.substr(0, val))) setSlider(eleget0('//html/body/div[2]/a/img/..'), 4, 30, 8, "セル幅:***em", "FTBcallWidth", (val) => elegeta('//td/span[@title]/div/../..').forEach(e => e.style.width = `${val}em`)) }*/ }, }, { urlRE: "\/\/futapo.futakuro.com", // futapo:: id: 'FUTACHAN_CATALOG', id2: "futapo-catalog", listTitleXP: '//div[@class="thread-text"]', listTitleSearchXP: '//div[@class="thread-text"][***]/../..|.//span[@class="emph"][***]/../../..', listTitleMemoSearchXP: '//div[@class="thread-text"][***]|.//span[@class="emph"][***]/..', listGen: 5, Bsyntax2: 1, memoStyle: 'word-break:break-all; font-size:10px !important; line-height:130%; padding:0px 4px 0px 4px; line-break:anywhere;', preventMemo: m => ["★", "◎", "○"].includes(m), QRule: "\n\n非表示化に関しては正規表現は使えませんが\nこのサイトではここで登録した内容で8/9メモの追加動作の抑制ができ、\nその抑制に関してだけは正規表現が使え、全角/半角、大文字/小文字も区別しません\n", listTitleXPIgnoreNotExist: 1, observe: 666, observeClass: ["box", "box ", "yhmMyMemo"], //2022年07月12日 WhateverFirstAndEveryAPFunc: () => { popup3("Shift+F:FTBucket検索\n7:左上優先配置タイトルを設定\n8:左上優先配置(+通知)タイトルを設定\n9:左上優先配置(+開く)タイトルを設定\nShift+789:独自構文使用版789\n0:789メモ一括削除画面\nE:更新 D:下まで読み込む\nG:画像でNG H:画像でピックアップ", 8, 5000) // ↻ }, listTitleMemoSearchXPSameGen: 1, funcOnlyFirst: () => { hoverHelp(e => { if (e.closest('a.box img.thumbnail')) return "G:この画像でNG
H:この画像でピックアップ"; if (e.closest('a.box')) return "Q:非表示 W:アンドゥ" }) GF.latestReload = Date.now() - 3000 GF.FUTAPO_AUTO_RELOAD_DEFAULT = pref("FUTAPO_AUTO_RELOAD_DEFAULT") || 0; GM_addStyle('.graybutton{ cursor:pointer; position: fixed; z-index:100; opacity:1; font-size:12px; margin:0px 1px; padding:1px 6px 1px 6px; word-break: break-all !important; border-radius:12px; border:solid 1px #888; background-color:#fff; color:#888;}') GM_addStyle('.popuptext,.popuptext-back{z-index:1000000000000;}') GM_addStyle(".boxpri1{animation: pulse1 5s 1; } @keyframes pulse1 { 0% { outline:5px solid #0000ffff; } 40% { outline:5px solid #0000ffff; } 100% { outline:5px solid #0000ff00; } }") document.querySelector(`head`).insertAdjacentHTML('beforeend', ``) if (FUTAPO_CRAM_TITLE) GM_addStyle("div.thread-contents { padding-left: 0px !important;} div.thumbnailContainer { min-width:3em; position:inherit !important; margin-left: 0px !important;}") // boxの横幅が狭いときでもタイトルを詰め込む // フィルタフォームでEscで削除して抜ける document.addEventListener("keydown", e => { if (document.activeElement.matches("input#cat_text_search") && e.key == "Escape" && !e.isComposing) { $("input#cat_text_search").val("").blur() } }, true) var lastope = Date.now() var cdID //if (eleget0('#reload') && !cdID) { function waitAndDo(checkFunc, func) { // checkFuncがtrueになったらfuncを実行 if (!checkFunc()) setTimeout(waitAndDo, 500, checkFunc, func); else func(); } waitAndDo(() => eleget0('#reload'), () => { document.body.insertAdjacentHTML("beforeend", `⏻無操作
自動更新
`) $(document).on("mousedown", `#cdsw`, (e) => { if (e?.button != 0) return; cdon() }) function cdon() { pref("FUTAPO_AUTO_RELOAD_DEFAULT", 1) $('#cdsw').fadeOut(222); var lastope = Date.now() $(document).on("mousemove mousedown keydown", () => { lastope = Date.now() }) clearInterval(GF?.cdID); GF.cdID = setInterval(() => { if (document.visibilityState == "visible") { $('#cd,#cdsw').remove(); document.body.insertAdjacentHTML("beforeend", `${Math.max(0,~~(60.9-(Date.now()-lastope)/1000))}`) //⏽ } if (Date.now() - lastope > 60 * 1000 && !eleget0('.blinkingOL')) { // 開き待ちのが1つでもあれば押さない lastope = Date.now(); eleget0('//li[1]/a[@id="reload"]')?.click() } }, 1000) } $(document).on("mousedown", `#cd`, (e) => { if (e?.button != 0) return; cdoff() }) function cdoff() { pref("FUTAPO_AUTO_RELOAD_DEFAULT", "") clearInterval(GF?.cdID); $('#cd,#cdsw').remove() //text("停止")//.hide(999); document.body.insertAdjacentHTML("beforeend", `⏻無操作
自動更新
`) } if (GF.FUTAPO_AUTO_RELOAD_DEFAULT >= 1) cdon() //$("#cdsw").click() if (GF.FUTAPO_AUTO_RELOAD_DEFAULT >= 2) scr(0) }) setTimeout(() => { function scr(i) { let last = eleget0('#kako-search') window.scroll({ left: 0, top: last ? 0 : 99999, behavior: i % 2 == 1 ? "instant" : "smooth" }) if (!last && i < 99) setTimeout(() => { scr(++i) }, 100) } // setTimeout(() => { elegeta("#reload,#logo,#cdsw").forEach(e => e.title = e?.title + "\nE:更新\nD/右クリック:更新+下まで読み込み") // ↻ $(document).on("contextmenu", '#reload,#logo,#cdsw,#cd', () => { // d:: let r = eleget0('//a[@id="reload"]'); if (r) { if (!GF.latestReload || new Date().getTime() - GF.latestReload > 5000) { r.click(); scr(0) GF.latestReload = new Date().getTime(); } // 要5秒インターバル } return false }) $('a#server.pulldown').attr("title", ($("a#server.pulldown").attr("title") || "") + "\n右クリック:may←→img") // ↻ $('a#server.pulldown').on("contextmenu", () => { let url = location.href == 'https://futapo.futakuro.com/?server=img_b' ? 'https://futapo.futakuro.com/?server=may_b' : 'https://futapo.futakuro.com/?server=img_b' location.href = url; return false }) $('a#mode.pulldown').on("contextmenu", () => { let url = lh(/\&mode=6_0/) ? location.href.replace("&mode=6_0", "&mode=4_0") : lh(/\&mode=4_0/) ? location.href.replace("&mode=4_0", "&mode=6_0") : `https://futapo.futakuro.com/?server=may_b&mode=4_0&search=&searchMode=0&kako=0` location.href = url; return false }) /* if (FUTAPO_AUTO_RELOAD_DEFAULT >= 1) $("#cdsw").click() if (FUTAPO_AUTO_RELOAD_DEFAULT >= 2) scr(0) */ }, 500) GM_addStyle("div.thread-text{display:inline;line-break: anywhere;} .thread-text{word-break:break-all;}") // html{line-height:1.2;} setTimeout(() => { setSlider(eleget0('//div[@id="boxArea"]'), 54, 300, 54, "box高さ:***", "boxheight", (val) => { eleget0('#boxheightcss')?.remove() end(document.head, ``) }, 0, 'style="width:7em;height:0.5em;"') setSlider(eleget0('//div[@id="boxArea"]'), 0, 10, 5, "タイトル行間:***", "lineheight", (val) => { eleget0('#boxlineheightcss')?.remove() end(document.head, ``) }, 0, 'style="width:7em;height:0.5em;"') }, 999) }, funcMemo: () => { SITE.funcFinally() }, funcFinally: (disableHide, numberOfHidden) => { setTimeout(() => { let found = 0; // 7::8::9:: メモが付いたものを左上に優先整列 // ここの7/8/9動作は正規表現対応や大文字小文字全角半角非区別のために標準のメモが付く動作(正規表現非対応)や非表示動作(正規表現非対応)とは一致しない点に注意 try { // メモをクリックで消す&ESC/パネル外をクリックでパネルを消す動作を登録 if (!GF.yhmMemoDeleteButton) { GF.yhmMemoDeleteButton = 1 $(document).on("keydown", e => { if (e.key === "Escape") $('#yhmMemoDeletePanel').hide(200).queue(function() { $(this).remove(); run("observed"); }) }) $(document).on("mousedown", e => { if (!e?.target?.closest("#yhmMemoDeletePanel") || e?.target?.matches("#yhmMemoDeletePanelClose")) $('#yhmMemoDeletePanel').hide(200).queue(function() { $(this).remove(); run("observed"); }) }) $(document).on("click", ".yhmMemoDeleteButton,.yhmNoSelect", e => { // クリックで削除 let ele = e.target?.closest(".yhmMemoDeleteButton") if (!ele) return; // 非表示 if (ele?.dataset?.qword) { var str = pref(SITE.id + ' : SearchHideTitle') || []; let qword = str.find(v => escape(v) === ele?.dataset?.qword) if (qword) { $(ele).hide(200).queue(function() { $(this).remove() }); // これは削除パネル用の動作 //elegeta('.yhmMyMemo').filter(e => e === escape(qword)).forEach(e => { $(e).hide(200).queue(function() { $(this).remove() }) }) //adja(document.body, "afterbegin", ""); //eleget0('yhmmymemoremoved')?.remove(); // 学園祭用に消去を告知 GM.setClipboard(qword) showByTitle(qword) pref(SITE.id + ' : SearchHideTitle', (pref(SITE.id + ' : SearchHideTitle') || []).filter(n => n != qword)) } e.preventDefault() return false } else { // メモ var str = pref(SITE.id + ' : SearchMyMemo') || []; let memo = str.find(v => escape(v.t + v.m + v.c) === ele.id) if (memo) { $(ele).hide(200).queue(function() { $(this).remove() }); // これは削除パネル用の動作 elegeta('.yhmMyMemo').filter(e => e.id === escape(memo.t + memo.m + memo.c)).forEach(e => { $(e).hide(200).queue(function() { $(this).remove() }) }) //adja(document.body, "afterbegin", ""); //eleget0('yhmmymemoremoved')?.remove(); // 学園祭用に消去を告知 document.body.dispatchEvent(new Event('yhmMyMemoRemoved')) GM.setClipboard(memo.t) pref(SITE.id + ' : SearchMyMemo', (pref(SITE.id + ' : SearchMyMemo') || []).filter(n => JS(n) !== JS(memo))) } e.preventDefault() return false } }) document.addEventListener("scroll", e => { let my = eleget0('#yhmMemoDeletePanel')?.getBoundingClientRect()?.bottom + window.scrollY - clientHeight() + 15 if (window.scrollY > my) window.scrollTo({ left: 0, behavior: "smooth", top: my }) }) } let memoa = pref(SITE.id + ' : SearchMyMemo') || []; memoa = memoa.filter(v => SITE?.preventMemo(v.m)).map(v => { v.than = han(v.t); if (isValidRE(v.t)) v.re = new RegExp(v.t, "mi"); return v }) // 重いので計算を最内周でせず1回で済ます let hide = disableHide ? [] : pref(SITE.id + ' : SearchHideTitle') || []; hide = hide.map(v => { return { word: v, re: isValidRE(v) ? new RegExp(v, "mi") : null } }); // 重いので計算を最内周でせず1回で済ます var hit = new Set() for (let e of elegeta('.box:not([relocatedByMemo])')) { let tt = eleget0('.thread-text', e)?.textContent for (let v of memoa) { if (v.re && v.re.test(han(tt)) || tt?.indexOf(v.t) !== -1) { // u/U/jメモ登録ワードが正規表現として有効なら正規表現としてチェック、また単純文字列としてヒットしてもヒットとする hit.add(e) e.dataset.vip = Math.max("○◎★".indexOf(v.m), e.dataset?.vip || 0) // 9>8>7の順で強い結果を残す e.title = `${e.title?e.title+" ":""}${v.m}${v.t}` e.dataset.hitwords = `${e.dataset.hitwords?e.dataset.hitwords+" ":""}${v.m}${v.t}`; if (v?.t?.length > 3) { if (!eleget0(`*[id="${escape(v.t+v.m+v.c)}"]`, e)) after(eleget0('.thread-text', e), `${sani(v.m)}${sani(v.t)?.substr(0,25)+(v?.t?.length>25?"…":"")}`) } else { if (!eleget0(`*[id="${escape(v.t+v.m+v.c)}"]`, e)) after(eleget0('.thread-text', e), `${sani(v.m)}${sani(v.t)?.substr(0,25)+(v?.t?.length>25?"…":"")}`) } } } eleget0('.thumbnail', e)?.setAttribute("title", `${tt}\n${e.title}`) } GF.opened = GF.opened || new Set() GF.opened1 = GF.opened1 || new Set() GF.opened2 = GF.opened2 || new Set() while (1) { var b = elegeta('.box:not([relocatedByMemo])') var e = b.find(v => hit.has(v)) if (!e) break; //GF.opened.push(e.href) let vip = Number(e.dataset.vip) e.setAttribute("relocatedByMemo", "") let memoEle = e let firstBox = elegeta('.box:not(.boxpri,.boxpri2)').filter(e => e.style.backgroundColor !== "rgb(255, 255, 0)")[0] if (vip == 1) { firstBox = elegeta('.box:not(.boxpri2,.boxpri3)').filter(e => e.style.backgroundColor !== "rgb(255, 255, 0)")[0] eleget0('.thread-text', memoEle).style.color = "#12e"; memoEle.style.boxShadow = "4px 4px 4px 0px #0006"; memoEle.style.zIndex = 2; memoEle.style.fontWeight = "bold" memoEle.classList.add("boxpri2") } else if (vip == 2) { firstBox = elegeta('.box:not(.boxpri3)').filter(e => e.style.backgroundColor !== "rgb(255, 255, 0)")[0] eleget0('.thread-text', memoEle).style.color = "#12e"; memoEle.style.boxShadow = "4px 4px 4px 0px #0006"; memoEle.style.zIndex = 3; memoEle.style.fontWeight = "bold" memoEle.classList.add("boxpri3") } else { memoEle.style.boxShadow = "4px 4px 4px #0006"; memoEle.style.zIndex = 1; } memoEle.classList.add("boxpri") firstBox.parentNode.insertBefore(memoEle, firstBox) // Qの非表示にヒットしたものは除外、正規表現対応・全角半角非区別・大文字小文字非区別 let tt = eleget0('.thread-text', e)?.textContent; let hitPreventWord = hide.find(q => { return q.re && q.re.test(han(tt)) || tt?.indexOf(q.word) !== -1 }) // Q非表示登録ワードが正規表現として有効なら正規表現としてもチェック、また単純文字列としてヒットしてもヒットとする if (!(disableHide || !hitPreventWord)) { e.title += `\n\nブロックワード『${hitPreventWord?.word}』で抑制` } else { if (vip == 0 && e.offsetHeight && !GF.opened.has(e.href)) { GF.opened.add(e.href); //GF.opened = GF.opened.slice(0, 1000) } if (vip == 1 && e.offsetHeight && !GF.opened1.has(e.href)) { GF.opened1.add(e.href); // GF.opened1 = GF.opened1.slice(0, 1000) found = memoEle.cloneNode(true); if (FUTAPO_89_NOTIFY.split(" ").includes("notify")) notifyMe(eleget0(".thread-text", found)?.innerText, e.dataset.hitwords, e => { e.preventDefault(); //`${"\n\n"+eleget0('//a[@id="server"]')?.innerText||""}` window.open(memoEle.href, "", "noreferrer"); }, eleget0('img', found)?.src) } if (vip == 2 && e.offsetHeight && !GF.opened2.has(e.href)) { GF.opened2.add(e.href); //GF.opened2 = GF.opened2.slice(0, 1000) // GF.open = GF.open || [] found = memoEle.cloneNode(true); if (FUTAPO_89_NOTIFY.split(" ").includes("notify")) notifyMe(eleget0(".thread-text", found)?.innerText, e.dataset.hitwords, e => { e.preventDefault(); window.open(memoEle.href, "", "noreferrer"); }, eleget0('img', found)?.src) memoEle.classList.add('blinkingOL') addstyle.add('@keyframes outline { 0% { outline: 4px solid #f0f; } 100% { outline: 4px solid #c8f; } } .blinkingOL{ animation: outline 1s ease infinite alternate; }') function opentab(href) { if (Date.now() - (GF.latest || 0) > (ISCHROME ? 7000 : 5000)) { GF.latest = Date.now() GM.openInTab(href, true) memoEle.classList.remove('blinkingOL') } else { setTimeout(() => { opentab(href) }, 333) } } opentab(memoEle.href) //} } } } if (found) { //if (FUTAPO_SOUND_NOTIFY) sound("sine", 0.1); if (FUTAPO_89_NOTIFY.split(" ").includes("sound")) sound("sine", 0.1); } } catch (e) { if (DEBUG_CATCH) alert(e) } }, 200) if (numberOfHidden) window.dispatchEvent(new CustomEvent('scroll')) // 非表示をしてスレアイテム数が画面に入り切るようになるとスクロールができなくて続きがあっても読み込めなくなるのでスクロールイベントだけ起こす }, keyFunc: [{ key: /^g$|^h$/, // g::画像でNG h::画像でピックアップ func: (k) => { e = eleget0('img.thumbnail:hover') if (e) { eleget0('div.cat-menu', e?.closest(".box"))?.click() eleget0(k == "h" ? 'a#th_img_pickup' : 'a#th_img_ng')?.click() /* waitdo(() => eleget0('select[name="img_match"]'), e => e.value = "100") waitdo(() => eleget0('input#all_board'), e => e?.click()) waitdo(() => eleget0('input#save.submit.option-button'), e => e?.focus()) */ waitdo(() => eleget0('select[name="img_match"]'), e => e.value = pref("FUTAPO_IMAGE_NG_PERCENT") || "100") waitdo(() => eleget0('input#all_board'), e => e?.click()) waitdo(() => eleget0('input#save.submit.option-button'), e => e?.focus()) waitdo(() => eleget0('select[name="img_match"]'), () => { eleget0('select[name="img_match"]')?.addEventListener("change", e => { pref("FUTAPO_IMAGE_NG_PERCENT", e.target.value) }) }) } } }, { key: 'd', // d:: func: (e) => { let r = eleget0('//a[@id="reload"]'); if (r) { let scr = (i) => { let last = eleget0('#kako-search') window.scroll({ left: 0, top: last ? 0 : 99999, behavior: i % 2 == 1 ? "instant" : "smooth" }) if (!last && i < 100) setTimeout(() => { scr(++i) }, 100) // 10秒まで //if (!last && i < 150) setTimeout(() => { scr(++i) }, 100) // 15秒まで } scr(0) //} // 要5秒インターバル } }, }, { key: 'e', // e::リロード func: () => { let r = eleget0('//a[@id="reload"]'); if (r) { if (!GF.latestReload || new Date().getTime() - GF.latestReload > 4000) { r.click(); GF.latestReload = new Date().getTime(); } // 要4秒インターバル } }, }, { key: 'Shift+F', // Shift+F::FTBucketでキーワード検索 func: () => { searchWithHistory("FUTACHAN", "FTBucketとふたば★さーち", ['https://www.ftbucket.info/scrapshot/ftb/index.php?mode=c&favo=0&ord=1&s=***', `https://zzzsearch.com/futaba/#gsc.q=***&gsc.sort=date`], "|") }, }, { key: /^7$|^8$|^9$|^Shift\+\'$|^Shift\+\($|^Shift\+\)$/, // 7::8::9::Shift+789 func: (e, opt, site) => { let shift = e.match0("Shift+"); var str = pref(site.id + ' : SearchMyMemo') || []; let memostr = (e == "9" || e == "Shift+)") ? "★" : (e == "7" || e == "Shift+'") ? "○" : "◎" var newstr = str.filter(e => e.m === memostr).map(e => e.t) GF.sorttype = ((GF.sorttype || 0) % 3 + 1) var [order, finstrfunc] = [ ["登録順", a => a.reverse().join(" ")], ["abc順", a => a.reverse().sort(new Intl.Collator("ja", { numeric: true, sensitivity: 'base' }).compare).join(" ")], ["長さ→abc順", a => a.reverse().sort((a, b) => a.length === b.length ? (new Intl.Collator("ja", { numeric: true, sensitivity: 'base' }).compare)(a, b) : a.length > b.length ? 1 : -1).join(" ")] ][GF.sorttype - 1] let tips = shift ? "独自構文Tips:\n「ABCやDEFを含まず、GHIかJKLを含み、かつMNOとPQRも含む」\n!ABC|DEF GHI|JKL MNO PQR" : "複数の単語をスペースで区切って入力するとまとめて登録できます"; var target = (window.getSelection() && window.getSelection().toString().trim()) || (prompt(`futapoで${memostr}メモを付けるキーワードを入力してください\n\nfutapoではここで設定した項目、メモが付く項目を左上に優先配置します\n(部分一致、正規表現使用可)\n\n${"7:○メモではヒットした項目を左上に優先配置します\n8:◎メモではさらに音声とNotificationで通知します\n9:★メモでは更に自動的に新しいタブで開きます"}\n\nすでに登録されている文字列を入力するとそれを削除します\n\n${tips}\n\n現在登録済み(${newstr.length}): (${order})\n${finstrfunc(newstr)}\n\n`) || "").trim(); if (!target) return; let targets = shift ? [target.replace(/^S$/, "??").replace(/^S([\s \||])/, "??$1").replace(/([\s \||!!])S([\s \||])/, "$1??$2").replace(/([\s \||!!])S$/, "$1??").replace(/|/gm, "|").replace(/^[\!|!](\S*)/, "^(?!.*($1)).*").replace(/(\S*)[  ](\S*)/gm, "^(?=.*($1))(?=.*\($2\))").replace(/\s| /gm, ".*")] // 独自構文を正規表現に変換 : target.split(/\s| /) var dele = 0; targets.forEach(targetc => { targetc = targetc.trim() var str = pref(site.id + ' : SearchMyMemo') || []; var str2 = str.filter(e => { return e.t != targetc }) if (str.length != str2.length) { if (confirm(`『${targetc}』(${str.find(e=>e.t==targetc)?.m})は既に存在します\n削除しますか?`)) { if (debug) V && dc(`『${targetc}』をメモから削除しました`) pref(site.id + ' : SearchMyMemo', JSON.stringify(str2)); dele = 1; elegeta('[relocatedByMemo]').forEach(v => v.removeAttribute("relocatedByMemo")) $(".yhmMyMemo").remove() run("returned") } } else { storeMemo(targetc.trim(), memostr, COLOR1, null, null, site) elegeta('[relocatedByMemo]').forEach(v => v.removeAttribute("relocatedByMemo")) run("returned") } }) if (dele) run(document.body, "returned") }, }, { key: /^0$/, // 0::メモ一覧一括削除画面 func: (e, opt, site) => { var str = pref(site.id + ' : SearchMyMemo') || []; var newstr = str GF.sorttype = eleget0("#yhmMemoDeletePanel") ? ((GF.sorttype) + 1) : 1 var [order, finstrfunc] = [ ["登録順", a => a.reverse()], ["abc順→種別", a => a.sort((a, b) => a.m == b.m ? 0 : a.m < b.m ? 1 : -1).sort((a, b) => (new Intl.Collator("ja", { numeric: true, sensitivity: 'base' }).compare)(a.t, b.t))], ["長さ→abc順→種別", a => a.sort((a, b) => a.m == b.m ? 0 : a.m < b.m ? 1 : -1).sort((a, b) => a.t.length === b.t.length ? (new Intl.Collator("ja", { numeric: true, sensitivity: 'base' }).compare)(a.t, b.t) : a.t.length > b.t.length ? 1 : -1)], ["種別→登録順", a => a.reverse().sort((a, b) => a.m == b.m ? 0 : a.m < b.m ? 1 : -1)], ["種別→abc順", a => a.sort((a, b) => (new Intl.Collator("ja", { numeric: true, sensitivity: 'base' }).compare)(a.t, b.t)).sort((a, b) => a.m == b.m ? 0 : a.m < b.m ? 1 : -1)], ["種別→長さ→abc順", a => a.sort((a, b) => a.t.length === b.t.length ? (new Intl.Collator("ja", { numeric: true, sensitivity: 'base' }).compare)(a.t, b.t) : a.t.length > b.t.length ? 1 : -1).sort((a, b) => a.m == b.m ? 0 : a.m < b.m ? 1 : -1)] ][(GF.sorttype - 1) % 6] var words = finstrfunc(newstr) $("#yhmMemoDeletePanel").remove() end(document.body, `
×(Esc)0:メモ&非表示一括削除
メモ(${words.length})(${order})をクリックすると削除してクリップボードにコピーします
`) let dup = [] // 重複しているものは暗い色にする let list = "" words.forEach(w => { // list += `${sani(w.m)}${sani(w.t)} ` list += `${sani(w.m)}${sani(w.t)} ` dup.push(w.t) }) var [order, finstrfunc] = [ ["登録順", a => a.reverse()], ["abc順", a => a.sort((a, b) => (new Intl.Collator("ja", { numeric: true, sensitivity: 'base' }).compare)(a, b))], ["長さ→abc順", a => a.sort((a, b) => a.length === b.length ? (new Intl.Collator("ja", { numeric: true, sensitivity: 'base' }).compare)(a, b) : a.length > b.length ? 1 : -1)], ][(GF.sorttype - 1) % 3] var words = pref(site.id + ' : SearchHideTitle') || []; words = finstrfunc(words) list += `

非表示登録(${words.length})(${order})をクリックすると削除してクリップボードにコピーします
`; words.forEach(w => { list += `${sani(w)} `; }) end(eleget0("#yhmMemoDeletePanel"), list) }, }, { key: 'a', // a::ソート func: () => { var sorttype = Number($('.yhmSortType')?.attr("id") || 0); $('.yhmSortType').remove(), $(document.body).append(``) popup2("A:ソート\n" + (["レス数", "勢い", "タイトル", "元"].map((c, i) => " " + c + (i + 1 == sorttype ? " ←\n" : "\n")).join("")), 6, "min-width:6em;") sorttype == 1 && sortdom(elegeta('.box:not(#kako-search)'), v => Number(eleget0('.rescnt', v)?.textContent), 1) sorttype == 2 && sortdom(elegeta('.box:not(#kako-search)'), v => Number(0 + eleget0('.ikioi-icon', v)?.textContent) + Number((eleget0('span.red', v)?.textContent + 0 || 0)) * 10, 1) sorttype == 3 && sortdom(elegeta('.box:not(#kako-search)'), v => (eleget0('.thread-text', v)?.textContent)) sorttype == 4 && sortdom(elegeta('.box:not(#kako-search)'), v => v?.dataset?.idx) } }, ], wholeHelp: [() => 1, " A:ソート"], hideSelectedWord: 1, selectedHelp: { help: [KEYHIDE + ":NGワードに追加", "7/8/9:左上に優先配置(8:+通知/9:+開く)"] }, //, multi: "複数行に渡る文字列は NG に入れられません" }, }, { id: 'FUTACHAN_CATALOG', urlRE: /\/\/anige\.horigiri\.net\/?$|\/\/anige\.horigiri\.net\/\?cat=|\/\/anige\.horigiri\.net\/\?paged=/, listTitleXP: '//div/h3[@class="entry-title"]/a', listTitleSearchXP: '//div/h3[@class="entry-title"]/a[+++]/../../../..', hideSelectedWord: 1, listTitleMemoSearchXP: '//div/h3[@class="entry-title"]/a[+++]', listGen: 5, delat: 500, listTitleMemoSearchXPSameGen: 1, WhateverFirstAndEveryAPFunc: () => { SITEINFO[SITEINFO.findIndex(c => c.id == "FUTACHAN_CATALOG")].WhateverFirstAndEveryAPFunc() }, keyFunc: [{ key: 'Shift+F', // Shift+F::FTBucketでキーワード検索 func: () => { searchWithHistory("FUTACHAN", "FTBucketとふたば★さーち", ['https://www.ftbucket.info/scrapshot/ftb/index.php?mode=c&favo=0&ord=1&s=***', `https://zzzsearch.com/futaba/#gsc.q=***&gsc.sort=date`], "|") }, }], }, { id: 'FUTACHAN', // futaba:: urlRE: () => (location.protocol == "file:" && eleget0('tbody > tr > th[bgcolor="#e04000"] > font[color="#FFFFFF"]:text*=レス送信モード')) || /\/\/.*.ftbucket.info\/|\/\/kuzure\.but\.jp\/f\/b\/|\/\/[^.]+.2chan\.net\/|\/\/anige\.horigiri\.net|\/\/kako\.futakuro\.com\/futa\/|https?:\/\/tsumanne\.net\/.*\/data\/|\/\/futafuta\.site\/thread\/|\/\/parupunte\.net\/logbox\/detail\.html\?no\=\d+/.test(location.href), title: '.thre table .cno', box: '.thre table', funcQ: e => { let h = elegeta('a img', e?.closest(".rtd") || null).map(e => `IdH:${img2dhash(e)}`).forEach(v => v != "IdH:null" && addNG(v, "dispall")); // Qで画像も非表示に入れる 2025.03 //e?.closest('.rtd')?.textContent?.match(/\.(?:jpe?g|png|webp|avif|gif|mp4|mkv)\-\(\d+\sB\)/)?.forEach(v => addNG(v, "dispall")) }, // Qで画像も非表示に入れる 2025.03 }, funcHidden: (e, com) => SITE.hideDhash(), hideDhash: e => { // 画像もdHsahで覚えて非表示(縮小ぼかし) 2025.03 (function hideDH(evt) { //console.log("onload",evt?.target,Date.now()); after(evt?.target?.parentNode,`${(new Date()).toLocaleString("ja-JP", { timeZone: "Asia/Tokyo" })}`); var qs = JP(JS(pref(SITE.id + ' : SearchHideTitle')).normalize("NFC")) || []; qs = qs.map(v => v.match0(/^IdH:(.+)$/)).filter(v => v); (evt ? [evt?.target] : elegeta('.rtd img')).forEach(img => { img?.removeEventListener("load", hideDH); if (img.complete) { if (qs.includes(img2dhash(img))) { img.style.filter = `blur(1em) saturate(33%)`; img.title = `非表示登録『IdH:${qs.find(v=>v==img2dhash(img))}』にヒット`; !img.closest("#pickbox") && img.animate([{}, { width: "auto", maxHeight: `5em` }], { duration: 100, easing: "ease", fill: "both" }) } else { if (img?.style?.filter == `blur(1em) saturate(33%)`) img.animate([{ filter: `blur(1.1em) saturate(33%)` }, {}], { duration: 8000, easing: "ease-out" }) img.style.filter = null; img.removeAttribute("title"); !img.closest("#pickbox") && img.animate([{}, { width: null, maxHeight: `250px` }], { duration: 100, easing: "ease", fill: "both" }) } } else img.addEventListener("load", hideDH); }) })(); }, disableKeyB: 0, redoWhenRefocused: 1, funcMemo: () => GF.resChained = [], // isMemoPartialMatch:1, isHidePartialMatch: 1, memoFunc: titleEle => eleget0('.memo', titleEle.closest('tr')), memoPosition: "afterbegin", listHelpJQS: '.thre table', delay: 111, memoStyle: 'display:table !important; margin-bottom:2px;', //memoStyle: 'display:block !important; width:fit-content; margin-bottom:2px;', detailURLRE: /$^/, detailTitleXP: '', hideSelectedWord: 1, selectedHelp: { help: [KEYHIDE + ":NGワードに追加", "y:YouTubeで検索"], multi: "y:YouTubeで検索" }, detailTitleSearchXP: '', listTitleSearchFunc: (title) => { // q::レス中キーワードNG // todo:リロードで追加されたレスにも非表示を適用 let resHit = []; if (typeof title === "string" && !/^No\.\d+$/gmi.test(title)) { // textContentでサーチする for (let res of elegeta('.thre table:not(.ftbpu table,#respopup_area table) , #searchResult table')) { // レス全体(ID:~も対象) if (res.textContent.indexOf(title) !== -1) resHit.push(res?.closest('table')); } } return resHit; }, WhateverFirstAndEveryAPFunc: () => { if (!eleget0('span.ignoreMe.wcspu3bottom.pu3line7s')) if (!eleget0('input[value="スレッドを立てる"]')) popup3(`Shift+F:FTBucket検索\na:画像順/そうだね順/引用順でソート${!GF?.isFile && location.href.match0(/^https?:\/\/[^.]+\.2chan\.net\//)?"\ne:新着チェック\nd:新着チェック+新着に移動\nc:ホバー下にそうだね\nn:自動リロード&新着通知\nm:監視ワード設定":"\nz/x:動画再生位置を-+\nShift+Z/Shift+X:動画再生速度-+"}`, 7, 5000) }, /* setTimeout(()=>{ let tf=eleget0(':is(input[type="file"][name="upfile"],input[type="file"][id="upup"] , input[type="file"]#up2input)')?`\nCtrl+V:クリップボードの画像/動画を添付`:``; popup3(`Shift+F:FTBucket検索\na:画像順/そうだね順/引用順でソート${location.href.match0(/^https?:\/\/[^.]+\.2chan\.net\//)?"\ne:新着チェック\nd:新着チェック+新着に移動\nc:ホバー下にそうだね\nn:自動リロード&新着通知\nm:監視ワード設定"+tf:""}`, 7, 5000) },2100)*/ funcD: () => { // d:: let r = location.protocol != "file:" && eleget0("#contres>a,#fvw_loading,a#akahuku_reload_button"); if (r) { if (!GF.latestReload || new Date().getTime() - GF.latestReload > 4000) { location.protocol != "file:" && r.click(); GF.latestReload = new Date().getTime(); } // 要4秒インターバル setTimeout(() => { eleget0('.reloadline')?.scrollIntoView({ behavior: "smooth", block: "center", inline: "center" }) }, 200); } }, keyFunc: [{ key: ' ', // Space::すべての画像を一定時間ぼかす // func: (e,evt) => { elegeta('img').forEach(e=>e.animate([{filter:`blur(1em)`,transform:`scale(0.33)`},{}],{duration:8000})); evt.preventDefault() + evt.stopPropagation(); return false;},//,easing:`ease-out` func: (e, evt) => { elegeta('.rtd :is(img,video)').forEach(e => e.animate([{ filter: `blur(1.1em) saturate(33%)` }, {}], { duration: 8000, easing: "ease-out" })); evt.preventDefault() + evt.stopPropagation(); return false; }, //,easing:`ease-out` }, { key: /^Shift\+X$|^Shift\+Z$/, func: (key, evt) => { if (GF?.isFile && !eleget0('div.vsc-controller') && eleget0('img:hover , video:hover')) { let pl = /Shift\+X/.test(key); let pbs = elegeta('video').reduce((a, b) => Math.max(a, b?.playbackRate || 1), 1) + (pl ? 0.1 : -0.1); elegeta('video').find(e => !e.paused) && elegeta('video').forEach(e => { e.playbackRate = pbs; }) popup3(`${pl?"Shift+X":"Shift+Z"}:動画再生速度${pl?"↑":"↓"} ×${Math.round(pbs*10)/10}`, 7) } } }, { key: /^x$|^z$/, func: (key, evt) => { let pl = /x/.test(key); GF?.isFile && !eleget0('div.vsc-controller') && eleget0('img:hover , video:hover') && elegeta('video').filter(e => !e.paused).forEach(e => { e.currentTime = e?.currentTime + (pl ? 1 : -1); popup3(`${pl?"x":"z"}:動画再生位置${pl?"+":"-"}1秒 (${Math.round(e?.currentTime)}/${Math.round(e?.duration)})`, 7) }) } }, { // key: /^7$|^8$|^9$|^Shift\+\'$|^Shift\+\($|^Shift\+\)$|^0$/, // 7::8::9::Shift+789 0:: key: /^7$|^8$|^9$|^Shift\+\'$|^Shift\+\($|^Shift\+\)$/, // 7::8::9::Shift+789 0:: func: (e) => { keyFuncDispatch(e, null, SITEINFO?.find(v => v?.id2 == "futapo-catalog")); } }, { key: "y", // y:: func: e => { let str = window.getSelection()?.toString()?.trim() if (!str) return; // let strs = str.split("\n").map(v => v.trim())?.slice(0, 6) let strs = str.split("\n").map(v => v?.trim()?.replace(/^…\s+\d+.*$/, "")?.trim())?.filter(v => v > "")?.slice(0, 6) let urls = strs.map(str => `https://www.youtube.com/results?search_query=${(str)}`) let urls2 = strs.map(str => `https://www.youtube.com/results?search_query=${encodeURI(str)}`) if (confirm(`${strs.join("\n")}\nをYouTube検索します(安全のため一度に最大6行にしています)\n\nまたついでに下記をクリップボードにコピーします\nよろしいですか?\n\n${urls.join("\n")}\n`)) { GM.setClipboard(strs.map((v, i) => `${v} - YouTube\n${urls2[i]}\n`).join("")) urls2.forEach((v, i) => { setTimeout(() => { GM.openInTab(v, true) }, i * 5000) }) } } }, { key: /^(?:Shift\+)?(?:Alt\+)?(?:z|Z)$/, // z::ポップアップを画像として「名前を付けて保存」 Shift+で高画質、Alt+でメモを反映 Alt+だとクリップボードには○メモが付いたものだけコピーする id: "z", func: (e) => { elegeta('.relpost , .relpost2').forEach(e => { e.classList.remove("relpost"); e.classList.remove("relpost2") }) if (document.elementFromPoint(mousex, mousey)?.matches("video,img") || eleget0('.youtubePLViewer:hover')) return; let isZ = e.indexOf("Shift+") != -1; let target = eleget0('.ftbpu') ? ".ftbpu" : eleget0('#pickbox') ? "#presentPick" : "" let orgtarget = eleget0('.ftbpu') ? ".ftbpu" : eleget0('#pickbox') ? "#pickbox" : "" if (!target || !orgtarget) return; if (target == "#presentPick") { let p = end(document.body, `
`) if (GF.presentPick) GF.presentPick.forEach(v => p.append(v.cloneNode(true))) sortdom(elegeta('#presentPick table[data-rsc]'), v => v.getAttribute("data-rsc")) } else { let p = eleget0(target).cloneNode(true); p.id = "presentPick"; p.className = "ignoreMe" target = "#presentPick" document.body.appendChild(p) // end(document.body, `
`) } if (elegeta(`${target} img:not(.quoteSpeechBalloonImg)`).length) { // 画像が1つでもあったら if (1 || !eleget0("#contdisp font:text*=スレッドがありません")) { let imgs = ld('kuzure.but.jp') ? elegeta(`${target} a>img[src*="s."]:not([data-rep-lar])`).filter(e => e.src.match(/s\./)) : elegeta(`${target} a>img[src*="thumb/"]:not([data-rep-lar])`).filter(e => e.src.match(/thumb\/\d+s\./)) imgs.forEach(img => { let orgHref = img?.parentNode?.href if (orgHref?.match(/\.(jpe?g|png|bmp|gif|webp)/)) { cldt("head"); img.dataset.repLar = 2; $.ajax({ url: orgHref, type: 'HEAD', error: (function(img) { return function() { img.dataset.repLar = 0; cldt(`404`); } })(img), success: (function(img) { return function() { img.src = orgHref; img.dataset.repLar = 1 cldt(`replace`); } })(img), }); } }) } let zwait = () => { cldt("compele?"); if (!eleget0(`${target} table a>img[data-rep-lar="2"]`) && elegeta(`${target} table a>img[data-rep-lar="1"]`).every(e => e.complete)) { z(e, isZ ? 3 : 2, isZ ? 3 : 2) } else { setTimeout(zwait, 50) } } setTimeout(zwait, 100 + elegeta(`${target} img:not(.quoteSpeechBalloonImg)`)?.length * 10) } else { z(e, isZ ? 2 : 1.5, isZ ? 2 : 1.5) } if (FUTABA_Z_TO_COPY_TO_CLIPBOARD_AS_TEXT_TOO) { // 文字としてもクリップボードにコピー let txt = elegeta('table[data-rsc]', eleget0(orgtarget)).filter(v => e.indexOf("Alt+") == -1 || eleget0('.yhmMyMemoO', v)).map(t => FUTABA_Z_TO_COPY_TO_CLIPBOARD_AS_TEXT_TOO.replace("${num1}", eleget0('.rsc', t)?.innerText || "").replace("${name1}", eleget0('.csb', t)?.innerText || "").replace("${name2}", eleget0('.cnm', t)?.innerText || "").replace("${time}", eleget0('.cnw', t)?.innerText || "").replace("${num2}", eleget0('.cno', t)?.innerText || "").replace("${soudane}", eleget0('.sod', t)?.innerText || "").replace("${text}", eleget0('blockquote', t)?.innerText || "")) GM.setClipboard(txt.join("\n")) popup3(elegeta('table[data-rsc]', eleget0(orgtarget)).map(t => `${eleget0('.rsc',t)?.innerText}`).join(","), 0, 5000, "top") } return; function z(key, SCALE_MIN = 1, SCALE_MAX = 2) { var D_FILENAME_MAXLENGTH = 83 // ファイル名の最大長 if (!eleget0(target)) return; let filenameSuffix = (key.indexOf("Alt+") != -1) ? FUTABA_ALTZ_FILENAME_SUFFIX : "" GF?.zFunc && GF?.zFunc() let honbun = elegeta(`${target} blockquote`).map(e => e.innerText)?.join(" ")?.replace(/\s+|\n+/gm, " ")?.trim() honbun = honbun?.replace(/\s*\>+[^\s]+/gm, "")?.trim() || res honbun = honbun?.slice(0, 100); // ?.replace(/キタ━+\(゚\∀゚\)━+\!+/gm,""); if (key.indexOf("Alt+") == -1) { $(`${target} .yhmMyMemo,${target} .relallArea,${target} .adddel`).remove() $(`${target} .sod`).css({ "font-size": "100%" }).removeClass("sodmypush") //,"float":"right" }) } $(`${target} .relallArea`).remove() $(`${target}`).css({ "box-shadow": "none", "transform": "scale(1)" }) if (orgtarget == "#pickbox") { $(`${target} table`).css({ "margin-right": "auto", "margin-left": "0" }) $(`${target} a img`).css({ "max-height": "" }) // pickの画像縦圧縮を解放する } $(`${target} .revQuote`).after("
"); //css({ "display": "inline-block" }) addstyle.add(`.yendotsaveElement .GM_FRRS_Counter,.yendotsaveElement .GM_FRRS_own_res{display:none}`) elegeta(`${target} .rsc`).forEach(e => e.style = "") var hrefName = "" // var fn = `${signzen(document.title?.replace(/\s+|\n+/gm," ")?.trim()+" "+location.href).substr(0, D_FILENAME_MAXLENGTH-hrefName.length-1)} ${hrefName}`?.trim() var fn = `${signzen(document.title?.replace(/\s+|\n+/gm," ")?.trim()+" "+location.href).substr(0, D_FILENAME_MAXLENGTH-hrefName.length)}`?.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() if (res) fn = `${signzen(document.title+" "+location.href).substr(0, D_FILENAME_MAXLENGTH/2-hrefName.length)} ${signzen(res).substr(0, D_FILENAME_MAXLENGTH/2)}`.trim() //if(filenameSuffix&&fn.length>D_FILENAME_MAXLENGTH)fn=fn.slice(-1,1) fn += filenameSuffix function signzen(str) { return str.replace(/^\s+/, "").replace(/\\|\/|\:|\;|\,|\+|\&|\=|\*|\?|\"|\'|\>|\<|\./g, c => { return String.fromCharCode(c.charCodeAt(0) + 0xFEE0) }) } let puele = eleget0(`${target}`) puele.classList.add('yendotsaveElement') let scale = Math.max(1, Math.min(SCALE_MAX, SCALE_MIN + (elegeta('img:not(.quoteSpeechBalloonImg)', puele).length * 0.5))) popup3(`z:ポップアップ(ピックアップ)を保存\n(Shift+で高画質、Alt+でメモを維持)\nScale = ${scale}`, 12) // document.dispatchEvent(new CustomEvent('saveDOMAsImage', { detail: { element: puele, filename: fn, scale: scale, hd: (key.indexOf("Shift+") != -1) ? 1 : 0, eleToFlash: target == ".ftbpu" ? eleget0(".ftbpu") : eleget0("#pickbox") } })) document.dispatchEvent(new CustomEvent('saveDOMAsImage', { detail: { element: puele, filename: fn, scale: scale, hd: (key.indexOf("Shift+") != -1) ? 1 : 0, eleToFlash: orgtarget == ".ftbpu" ? eleget0(".ftbpu") : eleget0("#pickbox") } })) $('#presentPick').remove() //if (orgtarget == "#pickbox") setTimeout(() => window.dispatchEvent(new Event('resize')), 1000) } }, }, { key: 'e', // e::リロード func: () => { let r = location.protocol != "file:" && eleget0("#contres>a,#fvw_loading,a#akahuku_reload_button"); if (r) { if (!GF.latestReload || new Date().getTime() - GF.latestReload > 4000) { location.protocol != "file:" && r.click(); GF.latestReload = new Date().getTime(); } // 要4秒インターバル } } }, { key: 'd', // d:: func: (e) => { if (eleget0("a:hover,video:hover,img:hover")) return SITE.funcD() }, }, { key: 'Shift+F', // Shift+F::FTBucketでキーワード検索 func: () => { searchWithHistory("FUTACHAN", "FTBucketとふたば★さーち", ['https://www.ftbucket.info/scrapshot/ftb/index.php?mode=c&favo=0&ord=1&s=***', `https://zzzsearch.com/futaba/#gsc.q=***&gsc.sort=date`], "|") }, }, { key: 'n', // n::Notificationで新着レスを通知on/off func: (key, evt, options = []) => { let [opt, changeto] = [options?.[0], options?.[1]] ////[opt[0],opt[1]]; if (opt == "automation") { GF.reloadAndNotifyNewArrival = Number(changeto); } else { GF.reloadAndNotifyNewArrival = ((GF.reloadAndNotifyNewArrival || 0) + 1) % 5; pref("FUTABA_RELOAD_AND_NOTIFY_NEWRES_DEFAULT", GF.reloadAndNotifyNewArrival) } $('#notiApiOn').remove(); let mess = `N:自動リロードと新着通知=${GF.reloadAndNotifyNewArrival}\n${["オフ","自動リロード","自動リロード+新着レスをNotificationで通知(メモ付きのみ)","自動リロード+新着レスをNotificationで通知(画像かメモ付きのみ)","自動リロード+新着レスをNotificationで通知(全て)"][GF.reloadAndNotifyNewArrival]}\n\n推奨:Firefoxではalerts.useSystemBackend false` //if (opt != "automation") popup2(`${mess}`, 8); if (opt != "automation") popup2(`N:自動リロードと新着通知\n${["オフ","自動リロード","自動リロード+新着レスをNotificationで通知(メモ付きのみ)","自動リロード+新着レスをNotificationで通知(画像かメモ付きのみ)","自動リロード+新着レスをNotificationで通知(全て)"].map((c, i) => " " + c + (i == GF.reloadAndNotifyNewArrival ? " ←\n" : "\n")).join("")}`, 9, "min-width:31em;") if (GF.reloadAndNotifyNewArrival && ld("2chan.net")) { $(`${["オフ","更新","メモ","画像","全て"][GF.reloadAndNotifyNewArrival]}`).click(e => keyFuncDispatch("n")).appendTo('body'); } }, }, { key: 'a', // a:: func: () => { GF.stopmoq = 1 let isftchan = /^https?:\/\/kuzure\.but\.jp\/f\/b\//.test(location.href); let isanigeaki = /\/\/anige\.horigiri\.net\/\?p/.test(location.href); if (isanigeaki || isftchan) $(elegeta('.thre .rtd').filter(e => !e.closest("#pickbox,.ftbpu"))[0]).closest("table").before($('
')) $('.reloadline').remove(); GF.sort = (GF?.sort || 0) % 4 + 1 //var sorttype = GF.sort||0//Number($('.yhmSortType')?.attr("id") || 0); let sorttype = GF.sort if (lh(/\/futaba\.php\?guid\=on/)) { popup2("A:ソート\n" + (["fu+YouTube+ニコ動", "YouTube", "ニコ動", "fu"].map((c, i) => " " + c + (i + 1 == sorttype ? " ←\n" : "\n")).join("")), 6, "min-width:6em;") sorttype == 1 && sortdom(elegeta('div.catsr'), v => { return (v.textContent?.match(/(?+[a-z0-9\/\.\-\:]+)(fu?\d+\.|youtube\.com\/watch|youtu\.be\/|nicovideo\.jp\/watch|\/\/nico\.ms\/)/g)?.length || 0) }, 1) sorttype == 2 && sortdom(elegeta('div.catsr'), v => { return (v.textContent?.match(/(?+[a-z0-9\/\.\-\:]+)(\/\/www\.youtube\.com\/|youtu\.be\/)/g)?.length || 0) }, 1) sorttype == 3 && sortdom(elegeta('div.catsr'), v => { return (v.textContent?.match(/(?+[a-z0-9\/\.\-\:]+)(nicovideo\.jp\/watch|\/\/nico\.ms\/)/g)?.length || 0) }, 1) sorttype == 4 && sortdom(elegeta('div.catsr'), v => { return (v.textContent?.match(/(?+[a-z0-9\/\.\-\:]+)(fu?\d+\.)/g)?.length || 0) }, 1) } else { popup2("A:ソート\n" + (["画像", "そうだね", "引用", "古い順"].map((c, i) => " " + c + (i + 1 == sorttype ? " ←\n" : "\n")).join("")), 6, "min-width:6em;") sorttype == 1 && sortdom(elegeta('table:not([data-reszero]) .rtd:not(#pickbox .rtd,.ftbpu .rtd)').map(v => v?.closest('table')), (v) => { return (((v.querySelector('table:not([data-reszero]) .sod') || v).innerText?.match0(/そうだねx(\d+)/) || 0) * 1) + (v.querySelectorAll("table:not([data-reszero]) .revQuote").length * 1) + (((v.textContent.match(/\.(gif|webm|mp4).*\d\d\d\sB\)|youtube\.|youtu\.be|nicovideo|(?)fu?\d+\.(gif|webm|mp4)/gmi) ? 1 : 0) * 1000000000) || ((v.textContent.match(/\d\d\d\sB\)|(?)fu?\d+\.(jpg|jpeg|png|bmp|webp)/gmi) ? 1 : 0) * 1000000) || ((v.textContent.match(/ttps?\:\/\//gmi) ? 1 : 0) * 1000)) }, 1) // リンクも加点 sorttype == 2 && sortdom(elegeta('table:not([data-reszero]) .rtd:not(#pickbox .rtd,.ftbpu .rtd)').map(v => v?.closest('table')), (v) => { return (((v.querySelector('table:not([data-reszero]) .sod') || v).innerText?.match0(/そうだねx(\d+)/) || 0) * 1000000000) + (v.querySelectorAll("table:not([data-reszero]) .revQuote").length * 1000000) + (((v.textContent.match(/\d\d\d\sB\)|youtube\.|youtu\.be|nicovideo|(?)fu?\d+\.(jpg|jpeg|png|gif|bmp|webp|webm|mp4)/gmi) || []).length * 1000) || ((v.textContent.match(/ttps?\:\/\//gmi) || []).length * 1)) }, 1) // リンクも加点 sorttype == 3 && sortdom(elegeta('table:not([data-reszero]) .rtd:not(#pickbox .rtd,.ftbpu .rtd)').map(v => v?.closest('table')), (v) => { return (((v.querySelector('table:not([data-reszero]) .sod') || v).innerText?.match0(/そうだねx(\d+)/) || 0) * 1000000) + (v.querySelectorAll("table:not([data-reszero]) .revQuote").length * 1000000000) + (((v.textContent.match(/\d\d\d\sB\)|youtube\.|youtu\.be|nicovideo|(?)fu?\d+\.(jpg|jpeg|png|gif|bmp|webp|webm|mp4)/gmi) || []).length * 1000) || ((v.textContent.match(/ttps?\:\/\//gmi) || []).length * 1)) }, 1) // リンクも加点 sorttype == 4 && sortdom(elegeta('table:not([data-reszero]) .rtd:not(#pickbox .rtd,.ftbpu .rtd)').map(v => v?.closest('table')), v => v.getAttribute("rsc")) //, } setTimeout(() => { GF.stopmoq = 0 }, 500) }, }], funcOnlyFirst: () => { ///xxx: GF.dhash = new WeakMap(); hoverHelp(e => e?.closest('table[border="0"]') ? "Q:非表示 W:アンドゥ 1,5:○メモ 2,6:×メモ" : "") document.body.addEventListener("dblclick", e => elegeta('.rtd :is(img,video)').forEach(e => e.animate([{ filter: `blur(1.1em) saturate(33%)` }, {}], { duration: 8000, easing: "ease-out" })), true); // 何もない背景をダブルクリック::すべての画像を一定時間ぼかす GF.latestReload = Date.now() - 3000 GF.newarticle = 1000 GF.latestInterval = FUTABA_AUTO_RELOAD_INTERVAL * 60 * 1000 GF.isFile = location.protocol == "file:" && eleget0('tbody > tr > th[bgcolor="#e04000"] > font[color="#FFFFFF"]:text*=レス送信モード') let is2chan = /^https?:\/\/[^.]+\.2chan\.net\//.test(location.href) || GF.isFile; let isftb = /^https?:\/\/[^\.]+\.ftbucket\.info\/|\/\/futafuta\.site\/thread\/|\/\/parupunte\.net\/logbox\/detail\.html\?no\=\d+/.test(location.href) || GF.isFile; // let isftb = /^https?:\/\/www\.ftbucket\.info\//.test(location.href); let istsumanne = ld("tsumanne.net"); // 添付ファイル関係 keyFuncAdd([{ key: 'v', // v:: func: () => { GF.webpQ = proInput(`v:\nWebP品質を1-10で指定してください(現在値:${GF?.webpQ||"未指定"})\n\n推奨:5-8\nロスレス圧縮:10\n10 eleget0('input[type="file"][name="upfile"] , input[type="file"][id="upup"] , input[type="file"]#up2input'), () => { if (!lh(/\/res\/|\/futaba\.htm$/)) return; fileSel = eleget0('input[type="file"][name="upfile"]') || eleget0('input[type="file"][id="upup"]') || eleget0('input[type="file"]#up2input') if (fileSel) { let tf = eleget0(':is(input[type="file"][name="upfile"],input[type="file"][id="upup"] , input[type="file"]#up2input)') ? `\nCtrl+v:クリップボードの画像/動画を添付` : ``; if (!eleget0('input[value="スレッドを立てる"]')) popup3(`Shift+F:FTBucket検索\na:画像順/そうだね順/引用順でソート${location.href.match0(/^https?:\/\/[^.]+\.2chan\.net\//)?"\ne:新着チェック\nd:新着チェック+新着に移動\nc:ホバー下にそうだね\nn:自動リロード&新着通知\nm:監視ワード設定"+tf:""}`, 7, 5000) if (fileSel?.files?.length) { $(fileSel).addClass("upfilein"); $("textarea#ftxa").addClass("upfilein") } setTimeout(() => elegeta('input[type="file"][name="upfile"] , input[type="file"][id="upup"] , input[type="file"]#up2input').forEach(e => e.setAttribute(`title`, `ここにドラッグ&ドロップでファイルを落とすかクリップボードに画像か動画がある時にここにホバーしてペースト(Ctrl+V)しても添付ファイルを選択できます\nファイル名はランダムな数字にし、メタデータを除去します。vキーでWebP再圧縮率の設定をします\n\nここ以外の場所(ページ内の何もない背景など)でファイルをCtrl+Vすると添付File→あぷにアップ→futaba-add-uploaderの順で探して最初に見つかったものにファイルを選択します`)), 2100) fileSel.setAttribute(`ondragover`, `event.preventDefault();event.dataTransfer.dropEffect='copy';event.target.classList.add('dragdrop-over')`) fileSel.setAttribute(`ondragenter`, `event.target.classList.add('dragdrop-over')`) fileSel.setAttribute(`ondragleave`, `event.target.classList.remove('dragdrop-over')`) addstyle.add(`:is(input[type="file"][name="upfile"],input[type="file"][id="upup"] , input[type="file"]#up2input){transition:box-shadow 0.33s;} .dragdrop-over { -webkit-appearance:none; position:relative; z-index:1000; outline: 5px solid #2196F3 !important; background-color: #e3f2fd !important; box-shadow:0 0 0 9999px #000000aa; transition:all 0.5s; animation:upfilein2 1s linear infinite;} .upfilein { -webkit-appearance:none; animation:upfilein 1s linear infinite; } @keyframes upfilein { 0% { outline:4px solid #1133ff; background-color: #e0e8ff; } 50% { outline:4px solid #def; background-color: #e0e8ff; } 100% { outline:4px solid #1133ff; background-color: #e0e8ff; } } @keyframes upfilein2 { 0% { outline:3px solid #2299ff; background-color: #fff; } 50% { outline:3px solid #eef; background-color: #bdf; } 100% { outline:3px solid #2299ff; background-color: #fff; } } .pvholder {animation:1s linear infinite upfilein; transform:scale( calc(23 / 300) ); bottom:0em; right:6.5em !important; transform-origin:bottom right; top:auto !important;} `) setTimeout(() => { document.querySelectorAll(':is(input[type="file"][name="upfile"],input[type="file"][id="upup"] , input[type="file"]#up2input)').forEach(input => { let gomi = after(input, `🗑`); gomi.addEventListener("click", e => { e.target.previousElementSibling.value = ''; fileSelectOff(); e.stopPropagation() e.preventDefault() return false; }) }) }, 2100) // あぷにアップよりあとにする fileSel.addEventListener('drop', async e => { e.preventDefault(); fileSel = eleget0(':is(input[type="file"][name="upfile"],input[type="file"][id="upup"] , input[type="file"]#up2input):hover') || eleget0('input[type="file"][name="upfile"]') || eleget0('input[type="file"][id="upup"]') || eleget0('input[type="file"]#up2input') if (await fileset(e?.dataTransfer?.files?.[0], `ドラッグアンドドロップ`, fileSel) == 0) popup2('D&Dに対応するのは「画像か動画」かつ「1ファイルだけ」です') }) document.addEventListener('paste', async (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; e.preventDefault(); fileSel = eleget0(':is(input[type="file"][name="upfile"],input[type="file"][id="upup"] , input[type="file"]#up2input):hover') || eleget0('input[type="file"][name="upfile"]') || eleget0('input[type="file"][id="upup"]') || eleget0('input[type="file"]#up2input') let file = firstFile(e?.clipboardData?.items) function firstFile(cbd) { for (let i = 0; i < cbd?.length; i++) { if (/^image\/|^video\//.test(cbd[i]?.getAsFile()?.type)) return cbd[i]?.getAsFile(); } } if (file) await fileset(file, `クリップボード`, fileSel) }); async function fileset(srcfile, from, fileSel) { if (!srcfile || !/^image\/|^video\//.test(srcfile?.type)) { fileSel?.classList?.remove('dragdrop-over'); return 0; } // 添付ファイル選択にCtrl+VでOSからペースト addstyle.add(`.pastePreviewContainer { font-size:85%; z-index:2000000021; position: fixed;top: 1em; right: 1em; background-color: #fff; padding: 10px; border: 4px solid #29f; border-radius: 5px; box-shadow: 0 0 2em #00000040; max-width: 300px; min-width:300px; word-wrap: break-word; word-break: break-all; transition:all 0.5s; } .pastePreviewContainer:hover {transform:scale(1) !important; transition:all 0.2s; } .pastePreview-container { display: flex; flex-direction: column; align-items: center; margin-top: 10px; } .pastePreview-container :is(img,video) { max-width: 300px; max-height: 300px; width: 100%; height: 100%; object-fit: contain; }`) end(document.body, `
添付ファイルプレビュー準備中…
`); const fileName = srcfile.name || ''; const currentDate = new Date(); currentDate.setHours(0, 0, 0, 0); let file = new File([FUTABA_EXPERIMENTAL_REMOVE_METADATA_FROM_UPFILE() ? await removeMetadataFromFile(srcfile) : srcfile], `${new Array(~~(Math.random()*6)+1).fill(0).map(v=>~~(Math.random()*10)).join("")}.${fileName.split('.').pop().toLowerCase()}`, { type: srcfile.type, lastModified: currentDate.getTime(), webkitRelativePath: '' }); let deleteMeta = srcfile.size > file.size; let toWebp = 0, colors, psnr; [file, toWebp, colors, psnr] = await pngToWebp(srcfile, file); file = new File([file], file.name, { type: file.type, lastModified: currentDate.getTime(), webkitRelativePath: '' }); let xyreso = await getResolution(file) || ""; $('.pastePreviewContainer').remove() async function getResolution(file) { return new Promise((resolve) => { try { const objectUrl = URL.createObjectURL(file); if (file.type.startsWith('image/')) { const img = new Image(); img.onload = () => { const { naturalWidth: w, naturalHeight: h } = img; URL.revokeObjectURL(objectUrl); img.src = ''; resolve(`original: ${w} x ${h}`); }; img.onerror = () => resolve(""); img.src = objectUrl; } else if (file.type.startsWith('video/')) { const video = document.createElement('video'); video.onloadedmetadata = () => { const { videoWidth: w, videoHeight: h, duration } = video; URL.revokeObjectURL(objectUrl); video.src = ''; resolve(`original: ${w} x ${h} - ${new Date(duration * 1000).toISOString().slice(11, 19)}
`); }; video.onerror = () => resolve(""); video.src = objectUrl; } else resolve(""); } catch { resolve(""); } }); } let srcPreview = (clientHeight() > 999 || debug) ? `
<${/^image\//.test(srcfile.type) ? 'img' : 'video'} id="upfilemedia" src="${URL.createObjectURL(srcfile)}" ${/^image\//.test(srcfile.type) ? '' : 'controls'}>
` : ""; let pv = end(document.body, `
🗑添付ファイルプレビュー

${from}:
name: "${sani(srcfile.name)}"
size: ${sani(srcfile.size.toLocaleString())} bytes
type: "${sani(srcfile.type)}"
${srcPreview}
→添付ファイル: ${psnr}
name: "${sani(file.name)}"
size: ${sani(file.size.toLocaleString())} bytes${deleteMeta?" (メタデータ除去)":""}${toWebp}
type: "${sani(file.type)}"
lastModified: ${sani(new Date(file.lastModified).toLocaleString())}

${xyreso}${colors}
<${/^image\//.test(file.type) ? 'img' : 'video'} id="upfilemedia" src="${URL.createObjectURL(file)}" ${/^image\//.test(file.type) ? '' : 'controls'}>
`); setTimeout(pv => $(document).one("click wheel mousemove keydown", "body", () => $(pv).addClass("pvholder")), 1000, pv) eleget0('#filegomibako')?.addEventListener("click", e => { document.querySelectorAll(':is(input[type="file"][name="upfile"],input[type="file"][id="upup"]) , input[type="file"]#up2input').forEach(e => e.value = '') fileSelectOff(); return false; }) const dataTransfer = new DataTransfer(); dataTransfer.items.add(file); fileSel.files = dataTransfer.files; fileSel.scrollIntoView({ behavior: "smooth", block: "center", inline: "center" }); clearTimeout(GF?.modal); GF.modal = setTimeout(() => fileSel?.classList?.remove('dragdrop-over'), 1000); fileSel?.classList?.add('dragdrop-over'); eleget0('textarea#ftxa')?.click(); if (fileSel.matches("#upup")) { // あぷにアップの処理が終わった頃ULしたにせよキャンセルしたにせよ点滅を消す setTimeout(fileSel => { fileSel?.dispatchEvent(new Event("change")) $(".pastePreviewContainer").remove() fileSel?.classList?.remove('dragdrop-over'); }, 600, fileSel) } else { $(fileSel).addClass("upfilein") $("textarea#ftxa").addClass("upfilein") } return 1; } } }, 9999) setTimeout(() => { elegeta('input#up2input').forEach(e => { new MutationObserver(e => { refreshUpfilein() }).observe(e, { attributes: true, childList: true, subtree: true }); }) }, 2100) document.addEventListener("click", e => { if (e?.target?.matches(`span.clearFileInput , input[type="file"] , input[type="submit"] , button#up2submit , span#filegomibako`)) refreshUpfilein(); }, true) document.addEventListener("yhm2chanAllDone", refreshUpfilein) function refreshUpfilein() { setTimeout(() => { elegeta('input[type="file"].upfilein')?.filter(e => !e?.files?.length)?.forEach(e => e?.classList?.remove('upfilein')); //$(eleins)?.classList?.remove('dragdrop-over'); if (elegeta('input[type="file"].upfilein').every(e => !e?.files?.length)) { $('.upfilein').removeClass("upfilein"); //fileSel?.classList?.remove('dragdrop-over'); //$(':is(input[type="file"][name="upfile"],input[type="file"][id="upup"] , input[type="file"]#up2input):hover').removeClass('dragdrop-over') $('.pastePreviewContainer').remove() } }, 34) } function fileSelectOff(com = 0) { // $('.upfilein').removeClass("upfilein") elegeta('.upfilein')?.filter(e => e?.files?.length == 0)?.forEach(e => e?.classList?.remove('upfilein')); if (elegeta('.upfilein').every(e => !e?.files?.length)) $('.upfilein').removeClass("upfilein"); //fileSel?.classList?.remove('dragdrop-over'); $(com == "all" ? 'input[type="file"]' : fileSel)?.classList?.remove('dragdrop-over'); //$(':is(input[type="file"][name="upfile"],input[type="file"][id="upup"] , input[type="file"]#up2input):hover').removeClass('dragdrop-over') $('.pastePreviewContainer').remove() } async function pngToWebp(srcFile, inputFile) { if (GF?.webpQ && GF?.webpQ > 10) return [inputFile, debug ? `(指定WebP品質${GF?.webpQ}>10により無変換)` : "", "", ""] if ((!(GF?.webpQ && inputFile.type == 'image/webp') && inputFile.type != 'image/avif' && inputFile.type != 'image/png' && inputFile.type != 'image/jpeg') || GF?.canvasNA == 1) return [inputFile, debug ? "(WebP化対象外)" : "", "", ""]; const canvas = document.createElement('canvas'); if (!canvas.getContext) return [inputFile, debug ? "(canvas使用不可)" : "", ""]; const img = await new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = e => { const image = new Image(); image.onload = () => resolve(image); image.onerror = reject; image.src = e.target.result; }; reader.onerror = reject; reader.readAsDataURL(inputFile); }); canvas.width = img.width; canvas.height = img.height; canvas.getContext('2d').drawImage(img, 0, 0); async function countUniqueColors(file) { return new Promise((resolve, reject) => { const img = new Image(); img.crossOrigin = "Anonymous"; img.src = URL.createObjectURL(file); img.onload = () => { const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); canvas.width = img.width; canvas.height = img.height; context.drawImage(img, 0, 0); const data = context.getImageData(0, 0, canvas.width, canvas.height).data; const colors = new Set(); let count = 0; const startTime = Date.now(); const maxDuration = 333; for (let i = 0; i < data.length && Date.now() - startTime < maxDuration; i += 4) { if (data[i + 3] > 0) colors.add(`${data[i]},${data[i + 1]},${data[i + 2]}`); count++; } resolve(colors.size); }; img.onerror = error => { reject(error); URL.revokeObjectURL(img.src); }; }); } let colors = await countUniqueColors(inputFile) let debugcolors = (1 || debug) && ` (${colors} colors)` || "" let validBlobs; if (inputFile.type == "image/avif") { // avifならまだ対応してないので強制的にwebpに変換して確定 validBlobs = [ //new File([await canvasToBlob(canvas, 'image/webp', 1)], inputFile.name.replace(/\.\w+$/, `.webp`), { type: "image/webp" }), new File([await canvasToBlob(canvas, 'image/webp', GF?.webpQ ? (GF?.webpQ / 10) : FUTABA_WEBP_LOSSY_QUALITY)], inputFile.name.replace(/\.\w+$/, `.webp`), { type: "image/webp" }) ]; inputFile = new File([await canvasToBlob(canvas, 'image/webp', 1)], inputFile.name.replace(/\.\w+$/, `.webp`), { type: "image/webp" }); } else { const blobPromises = (srcFile.name == "image.png" && inputFile.type == "image/png") ? [canvasToBlob(canvas, 'image/webp', GF?.webpQ ? (GF?.webpQ / 10) : colors <= 10000 ? 1 : FUTABA_WEBP_LOSSY_QUALITY), canvasToBlob(canvas, 'image/webp', 1)] : GF?.webpQ ? [canvasToBlob(canvas, 'image/webp', (GF?.webpQ / 10)), canvasToBlob(canvas, 'image/webp', 1)] : [canvasToBlob(canvas, 'image/webp', 1)]; const blobs = await Promise.all(blobPromises.map(p => p.catch(() => null))); validBlobs = [inputFile, ...blobs.filter(blob => blob !== null)]; } let bestBlob = validBlobs.reduce((prev, curr) => curr.size < prev.size ? curr : prev, inputFile); let psnr = `(PSNR: ${Math.floor(await getPSNR(inputFile,bestBlob))}dB)`; //psnr += ` / SSIM: ${Math.round(await getSSIM(inputFile,bestBlob)*1000)/1000}` async function getPSNR(file1, file2) { const [pixels1, pixels2] = await Promise.all([loadImagePixels(file1), loadImagePixels(file2)]); if (pixels1.width !== pixels2.width || pixels1.height !== pixels2.height) return 0; const pixelCount = pixels1.width * pixels1.height; let mse = 0; for (let i = 0; i < pixelCount; i++) for (let c = 0; c < 3; c++) mse += (pixels1.data[i * 4 + c] - pixels2.data[i * 4 + c]) ** 2; mse /= pixelCount * 3; return mse === 0 ? 50 : 10 * Math.log10((255 ** 2) / mse); } function loadImagePixels(file) { return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => { const canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); const { data } = ctx.getImageData(0, 0, img.width, img.height); resolve({ data, width: img.width, height: img.height }); }; img.onerror = reject; img.src = URL.createObjectURL(file); }); } if (bestBlob.size < inputFile.size / 1000) { GF.canvasNA = 1; return [inputFile, "(おそらくcanvas無効)", debugcolors]; } return bestBlob.size < inputFile.size ? [new File([bestBlob], inputFile.name.replace(/\.\w+$/, `.${bestBlob.type.split('/').pop()}`), { type: bestBlob.type }), `(${inputFile.name.match0(/\.(\w+)$/)}→${bestBlob.type.split('/').pop()})`, debugcolors, psnr] : [inputFile, debug ? "(元ファイルが最小)" : "", debugcolors, psnr]; } function canvasToBlob(canvas, type, quality) { return new Promise((resolve, reject) => canvas.toBlob(blob => blob ? resolve(blob) : reject(new Error('')), type, quality) ); } async function removeMetadataFromFile(file) { try { let newfile = (file.type.startsWith('image/png')) ? await cleanPNG(file) : (file.type.startsWith('image/jpeg')) ? await cleanJPEG(file) : (file.type.startsWith('image/webp')) ? await cleanWEBP(file) : (file.type.startsWith('image/gif')) ? await cleanGIF(file) : file; return newfile } catch { return file; } } async function cleanGIF(file) { const buffer = await file.arrayBuffer(), view = new DataView(buffer); const header = String.fromCharCode(...new Uint8Array(buffer, 0, 6)); if (header !== 'GIF87a' && header !== 'GIF89a') return file; const newGIF = [buffer.slice(0, 13)]; (view.getUint8(10) & 0x80) && newGIF.push(buffer.slice(13, 13 + 3 * (1 << ((view.getUint8(10) & 0x07) + 1)))); for (let i = newGIF.reduce((sum, arr) => sum + arr.byteLength, 0); i < buffer.byteLength;) { const block = view.getUint8(i); if (block === 0x21) { const extType = view.getUint8(i + 1); extType === 0xFE ? (i = skipBlock(view, i + 2)) : (newGIF.push(buffer.slice(i, skipBlock(view, i + 2))), i = skipBlock(view, i + 2)); } else if (block === 0x2C) { const imageStart = i; i += 10; (view.getUint8(i - 1) & 0x80) && (i += 3 * (1 << ((view.getUint8(i - 1) & 0x07) + 1))); newGIF.push(buffer.slice(imageStart, i = skipBlock(view, i + 1))); } else if (block === 0x3B) { newGIF.push(new Uint8Array([0x3B])); break; } else i++; } return new File(newGIF, file.name, { type: 'image/gif' }); const skipBlock = (view, start) => { let i = start; while (view.getUint8(i) !== 0) i += view.getUint8(i) + 1; return i + 1; }; }; async function cleanWEBP(file) { // 不十分、要修正 const view = new DataView(await file.arrayBuffer()); if (String.fromCharCode(...new Uint8Array(view.buffer.slice(0, 4))) !== 'RIFF') return file; const chunks = []; for (let offset = 12; offset < view.byteLength;) { const chunkFourCC = String.fromCharCode(...new Uint8Array(view.buffer.slice(offset, offset + 4))); const chunkSize = view.getUint32(offset + 4, true); const chunkData = view.buffer.slice(offset, offset + 8 + chunkSize); if (['VP8 ', 'VP8L', 'VP8X', 'ALPH', 'ANIM'].includes(chunkFourCC)) chunks.push(chunkData); offset += 8 + chunkSize + (chunkSize & 1); } const totalSize = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0) + 12; const newWebP = new Uint8Array(totalSize); newWebP.set(new Uint8Array(view.buffer.slice(0, 4)), 0); new DataView(newWebP.buffer).setUint32(4, totalSize - 8, true); newWebP.set(new Uint8Array(view.buffer.slice(8, 12)), 8); chunks.reduce((offset, chunk) => { newWebP.set(new Uint8Array(chunk), offset); return offset + chunk.byteLength; }, 12); const newfile = new File([newWebP], file.name, { type: 'image/webp' }) if (file.size - 1 == newfile.size || newfile.size == 44) return file; return newfile; }; async function cleanJPEG(file) { const buffer = await file.arrayBuffer(); const view = new DataView(buffer); if (view.getUint16(0) !== 0xFFD8) return file; const imageData = [buffer.slice(0, 2)]; let offset = 2; while (offset < view.byteLength) { const marker = view.getUint16(offset); if ((marker & 0xFF00) !== 0xFF00) return file; const segmentLength = view.getUint16(offset + 2); const segmentType = marker & 0xFF; if (segmentType === 0xDA) { imageData.push(buffer.slice(offset)); break; } else if ([0xC0, 0xC1, 0xC2, 0xC4, 0xDB, 0xDC, 0xDD].includes(segmentType)) { imageData.push(buffer.slice(offset, offset + 2 + segmentLength)); } offset += 2 + segmentLength; } return new File(imageData, file.name, { type: 'image/jpeg' }); } async function cleanPNG(file) { const view = new DataView(await file.arrayBuffer()); const signature = [137, 80, 78, 71, 13, 10, 26, 10]; if (signature.some((byte, i) => view.getUint8(i) !== byte)) return file; const chunks = []; for (let offset = 8; offset < view.byteLength;) { const length = view.getUint32(offset); const type = String.fromCharCode(...Array.from({ length: 4 }, (_, i) => view.getUint8(offset + 4 + i))); if (['IHDR', 'PLTE', 'IDAT', 'IEND'].includes(type)) chunks.push(view.buffer.slice(offset, offset + 12 + length)); offset += 12 + length; } return new File([new Uint8Array(signature), ...chunks], file.name, { type: 'image/png' }); }; // u::youtubeリンクを小窓で全て再生 if (FUTABA_YOUTUBE_PLAYALL_BUTTON >= 2) keyFuncAdd([{ key: 'u', // u:: func: () => { let isON = eleget0('.youtubePLViewer') isON ? isON?.remove() : youtubePLV("y"); youtubePLVsetButton(); } }]); youtubePLVsetButton(); function youtubePLVsetButton() { if (!FUTABA_YOUTUBE_PLAYALL_BUTTON) return; let inp = [...elegeta('a:visible').map(e => e.href.replace("//yewtu.be/watch?v=", "//www.youtube.com/watch?v=").replace("//yewtu.be/shorts/", "//www.youtube.com/shorts/").replace(/\/\/yewtu\.be\/([a-zA-Z0-9_\-,]{11})(.*)/, "//youtu.be/$1$2").replace("//yewtu.be/", "//www.youtube.com/")).filter(v => /youtube\.com|youtu\.be/.test(v)), ...document?.body?.innerText?.split(/\n|\s/)?.filter(v => /youtube\.com|youtu\.be/.test(v)), ...elegeta('iframe[src*="youtube"]').map(e => e?.src)].join(" ").split(/\s/).map(v => { return [...v?.matchAll(/^(?:h?t?tps?:\/\/)?(?:youtu\.be\/|(?:m\.|www\.|)?youtube\.com\/(?:shorts\/|watch\S*?[\?\&]v=|embed\/|live\/))([a-zA-Z0-9_\-]{11})(?![a-zA-Z0-9_\-]{1})|^(?:h?t?tps?:\/\/)?www\.youtube\.com\/(?:watch_videos\?video_ids=|embed\/\?playlist=)([a-zA-Z0-9_\-,]{11,600})/gmi)]?.map(c => c.slice(1, 999)) })?.flat()?.flat()?.map(v => v?.split(","))?.flat()?.filter(c => /^[a-zA-Z0-9_\-]{11}$/.test(c)) if (!inp?.length) return; //if (!elegeta('blockquote a[href*="youtu"]').some(e => /^(?:h?t?tps?:\/\/)?(?:youtu\.be\/|(?:m\.|www\.|)?youtube\.com\/(?:shorts\/|watch\?v=|embed\/|live\/))([a-zA-Z0-9_\-]{11})(?![a-zA-Z0-9_\-]{1})|^(?:h?t?tps?:\/\/)?www\.youtube\.com\/(?:watch_videos\?video_ids=|embed\/\?playlist=)([a-zA-Z0-9_\-,]{11,600})/gmi.test(e))) return; let isON = elegeta('.youtubePLViewer'); $('#youtubePLVButton').remove(); end(document.body, `
${isON?.length?"×":GF?.PLVed?"◀":"▶"}
`) eleget0('#youtubePLVButton')?.addEventListener("click", e => { if (isON?.length) { isON.forEach(e => e.remove()) } else { youtubePLV(e.button == 0 ? "" : "Shift+Ctrl+Y"); GF.PLVed = 1; } youtubePLVsetButton(); }, true) eleget0('#youtubePLVButton')?.addEventListener("contextmenu", e => { e.preventDefault(); e.stopPropagation(); isON?.length ? isON.forEach(e => e.remove()) : youtubePLV(e.button == 0 ? "" : "Shift+Ctrl+Y"); youtubePLVsetButton(); return false; }, true) } function youtubePLV(key) { $('.youtubePLViewer').remove(); const IPURL = `https://www.youtube.com/watch_videos?video_ids=`; let option = key == "Shift+Ctrl+Y" ? "shuffle" : ""; let inp = [...elegeta('a:visible').map(e => e.href.replace("//yewtu.be/watch?v=", "//www.youtube.com/watch?v=").replace("//yewtu.be/shorts/", "//www.youtube.com/shorts/").replace(/\/\/yewtu\.be\/([a-zA-Z0-9_\-,]{11})(.*)/, "//youtu.be/$1$2").replace("//yewtu.be/", "//www.youtube.com/")).filter(v => /youtube\.com|youtu\.be/.test(v)), ...document?.body?.innerText?.split(/\n|\s/)?.filter(v => /youtube\.com|youtu\.be/.test(v)), ...elegeta('iframe[src*="youtube"]').map(e => e?.src)].join(" "); if (inp || 1) { var urlcap = inp.split(/\s/).map(v => { return [...v?.matchAll(/^(?:h?t?tps?:\/\/)?(?:youtu\.be\/|(?:m\.|www\.|)?youtube\.com\/(?:shorts\/|watch\?v=|embed\/|live\/))([a-zA-Z0-9_\-]{11})(?![a-zA-Z0-9_\-]{1})|^(?:h?t?tps?:\/\/)?www\.youtube\.com\/(?:watch_videos\?video_ids=|embed\/\?playlist=)([a-zA-Z0-9_\-,]{11,600})/gmi)]?.map(c => c.slice(1, 999)) })?.flat()?.flat()?.map(v => v?.split(","))?.flat()?.filter(c => /^[a-zA-Z0-9_\-]{11}$/.test(c)) // 書式が混在していても登場順に収納する inp = null; if (urlcap?.length || 1) { let urla = urlcap //urlcap.join(",").split(",").filter(c => /^[a-zA-Z0-9_\-]{11}$/.test(c)); // 動画IDは11桁 let urllen = urla.length; let urla2 = [...new Set(urla)]; // 重複削除 if (GF?.PLVed) urla2 = urla2?.reverse(); else GF.PLVed = 1; if (option == "shuffle") urla2 = shuffle(urla2); // シャッフル let urllen2 = urla2.length; let urla3 = [...urla2].slice(0, 50); // 50件まで let urlenum = urla3.join(",") let url = `${IPURL}${urla2.join(",")}` var enumUrl = [] for (let u = 0; u < urla2.length / 50; u++) { enumUrl.push(`https://www.youtube.com/embed/${urla2[u*50]}?playlist=${ (urla2.slice(u*50, u*50+50).join(",") ) }`) } if (enumUrl?.length) { enumUrl.forEach((u, i) => { let plv = end(document.body, `
` + `

` + `
`) new MutationObserver(m => { // 小窓をリサイズしたら中のyoutube iframeも連動リサイズ let ytif = eleget0('#ytplayer', plv) ytif.width = plv.offsetWidth - 16 * 2; ytif.height = plv.offsetHeight - 16 * 2; }).observe(plv, { attributes: true, childList: true, subtree: false }); plv && dragElement(plv, "*", "iframe", ".youtubePLViewer") plv?.addEventListener("dblclick", e => e?.target?.remove()) }) youtubePLVsetButton() } } function shuffle(array) { return array.map(a => ({ rnd: Math.random(), val: a })).sort((a, b) => a.rnd - b.rnd).map(a => a.val); } } } // 以降スレッド内のみ if (!GF.isFile && (is2chan && !location.href.match0(/\.2chan\..+\/res\/|\.2chan\.net\/[^\/]*\/futaba\.php\?guid\=on$|\/\/futafuta\.site\/thread\/|\/\/parupunte\.net\/logbox\/detail\.html\?no\=\d+/))) return; // if (!GF.isFile && (is2chan && !location.href.match0(/\.2chan\..+\/res\/|\.2chan\.net\/b\/futaba\.htm$|\.2chan\.net\/[^\/]*\/futaba\.php\?guid\=on$/))) return; let iskurokako = /\/\/kako\.futakuro\.com\/futa\//.test(location.href); let isftchan = /^https?:\/\/kuzure\.but\.jp\/f\/b\//.test(location.href); let isanigeaki = /\/\/anige\.horigiri\.net\/\?p/.test(location.href); let isfvw = eleget0('#fvw_menu') // futakuro if (isanigeaki) GM_addStyle('table, th, td{border:0;}'); if (isftchan || isanigeaki) { document.body.innerHTML = "
" + document.body.innerHTML + "
" } if (isftchan) { GM_addStyle("body{min-width:95%} .thre table{margin-right:0} #v0z{display:none;}") } if (is2chan || isftchan) GM_addStyle(".rtd{vertical-align:top} #pdm{z-index:2000000021}") GM_addStyle("#pickbox .yhmMyMemo{font-size:85%; white-space: nowrap;} .quo{vertical-align:top} a:visited{color:#800080;} .ftbpu .quo{max-width:25vw;}") GM_addStyle(".quoteSpeechBalloon{padding:0 0.68em; margin-left:0.68em; font-size:14px; 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(".revQuote{cursor:pointer;color:#789922; margin:0.19em; }") addstyle.add('.waiting{ display: inline-block; vertical-align: middle; font-size:85%; color: #666; line-height: 1; width: 1em; height: 1em; border: 0.12em solid currentColor; border-top-color: rgba(102, 102, 102, 0.3); border-radius: 50%; box-sizing: border-box; -webkit-animation: rotate 1s linear infinite; animation: rotate 1s linear infinite; } @-webkit-keyframes rotate { 0% { transform: rotate(0); } 100% { transform: rotate(360deg); } } @keyframes rotate { 0% { transform: rotate(0); } 100% { transform: rotate(360deg); } }') addstyle.add('.relallArea{user-select:none; text-align:center; display:inline-block; line-height:1.5em; color:#789922c0; min-width:1.5em; height:1.5em; margin:1px 0 1px 3px; margin-top:0.3em; } .memo:empty+.backlink:empty+.relallArea{margin-top:3px; } .backlink:empty+.relallArea{margin-top:7px; } .backlink{word-break:break-word;}') //background-color:#eed; addstyle.add('.relallAreaon{outline:2px dotted #789922f0; cursor:pointer;}') //background-color:#eed; addstyle.add('.sodmypush{color:#f00;}') if (location.href.match0("anige.horigiri")) GM_addStyle(`blockquote{color:#800000;line-height:1.2em;font-style:normal;`) addstyle.add('.GM_FRRS_Counter{display:none !important;}') addstyle.add('#pickbox blockquote { word-wrap: anywhere; }') addstyle.add(`#pickbox .rts {cursor:pointer;}`) addstyle.add(`.thre table blockquote a { word-break: break-all; }`) addstyle.add(`#pickbox{background-color:#ffffeebb; opacity:0.9; transition:background-color 0.1s, opacity 0.1s;} #pickbox:hover{background-color:#ffffeeff; opacity:1; transition:background-color 0.1s, opacity 0.1s;}`) //addstyle.add(`#pickbox{background-color:#ffffeebb; filter:opacity(0.9); transition:background-color 0.1s, opacity 0.1s;} #pickbox:hover{background-color:#ffffeeff; filter:opacity(1); transition:background-color 0.1s, opacity 0.1s;}`) GF.originalDocTitle = eleget0('//div[@class="thre"]/blockquote')?.textContent || document.title; GF.originalDocTitle0 = document.title; GF.anchor = new Array(1000).fill("") //let favi=eleget0('.thre>a>img')?.src; if(favi)end(document.head,``) // ファビコンを最初の画像にする if (ld("futafuta\.site"))[...document.body.getElementsByTagName('*')].forEach(el => [...el.childNodes].filter(node => node.nodeType === Node.TEXT_NODE).forEach(node => node.textContent = node.textContent.replace(/\n\s{5}([\s\S]+?)\n\s{4}/g, '$1').trim())); //if(ld("futafuta\.site"))autoPagerized(()=>{ [...document.body.getElementsByTagName('*')].forEach(el => [...el.childNodes].filter(node => node.nodeType === Node.TEXT_NODE).forEach(node => node.textContent = node.textContent.replace(/\n\s{5}([\s\S]+?)\n\s{4}/g, '$1').trim()));}) // フォーカスが戻った時に0を表示 //$(document).on("click","div.thre",e=>{e.preventDefault();if(e?.target?.matches("div.thre"))leadFocus();}) let Rimg = eleget0('//div[@class="thre"]/a/img')?.cloneNode(true) let Rdesc = elegeta('//div[@class="thre"]/span[1]|//div[@class="thre"]/span[2]|//div[@class="thre"]/span[3]|//div[@class="thre"]/span[4]|//div[@class="thre"]/span[5]|//div[@class="thre"]/a[@class="sod"]')?.map(e => e.cloneNode(true)) let Rhonbun = eleget0('//div[@class="thre"]/blockquote')?.cloneNode(true) // if (!img || !honbun) return; if (Rimg && Rhonbun) { document.addEventListener("focus", e => { if (!elegeta('.thre :is(video , img):not(img.quoteSpeechBalloonImg):inscreen:visible').length) leadFocus() }) document.addEventListener("blur", e => { $(".leadFocus").remove() }) Rhonbun.style.minWidth = "800px" function leadFocus(e) { if (window.scrollY + clientHeight() < eleget0('.thre img , .thre video:visible')?.getBoundingClientRect().top + window.scrollY) return; // if (!lh("/res/") || eleget0('//div[@class="thre"]/a/img|//div[@class="thre"]/a[2]/img:inscreen:visible') || eleget0('//div[@class="thre"]/blockquote:inscreen:visible') || eleget0('#pickbox,.leadFocus,.yhmMyMemo')) return; if (!lh("/res/") || eleget0('//div[@class="thre"]/a/img|//div[@class="thre"]/a[2]/img:inscreen:visible') || eleget0('//div[@class="thre"]/blockquote:inscreen:visible') || eleget0('.leadFocus , #pickbox')) return; let Rlead = end(document.body, `
`) Rdesc.forEach(e => eleget0(".rtd", Rlead)?.appendChild(e)) eleget0(".rtd", Rlead)?.appendChild(Rimg) eleget0(".rtd", Rlead)?.appendChild(Rhonbun) $(Rlead).animate2({ "transform": "translate(0,0)" }, 333) //, (function(e) { return function() { setTimeout(() => { $(e).animate({"transform":"translate(0,-100%)"},333, () => $(e).remove()) }, 999) } })(lead)) setTimeout(Rlead => { $(document).one("click wheel mousemove keydown", "body", (function(Rlead) { return function() { $(Rlead).animate2({ "transform": "translate(0,-150%)" }, 333); setTimeout(Rlead => Rlead.remove(), 333, Rlead); } }(Rlead))) }, 1333, Rlead) } } // res0:: レス本文をレス0かのように加工 if (FUTABA_REPLACE_RES0 && ld(/\.2chan\./) && !isfvw && !eleget0('[data-reszero]')) { let img = eleget0('div.thre a img')?.cloneNode(true) let imgfn = eleget0('div.thre a')?.cloneNode(true) let imgfn2 = eleget0('div.thre a')?.cloneNode(true) let desc = elegeta('div.thre>span.rsc,div.thre>span.csb,div.thre>span.cnm,div.thre>span.cnw,div.thre>span.cno')?.slice(0, 6)?.map(e => e.cloneNode(true)) let honbun = eleget0('div.thre blockquote')?.cloneNode(true) if (imgfn && imgfn2 && img && honbun) { addstyle.add(`.thre table[data-reszero] blockquote:not(.ftbpu table blockquote,#pickbox table blockquote){min-width:800px;}`) //addstyle.add(`.thre table[data-reszero] blockquote:not(.ftbpu table blockquote,#pickbox table blockquote){min-width:45em;}`) let lead = before(eleget0(`table[border="0"]`), `
0
`) lead = !lead ? after(eleget0(`.maxres`), `
0
`) : lead //let lead = before(eleget0(`table[border="0"]`), `
0
`) desc.forEach(e => eleget0(".rtd", lead).appendChild(e)) end(eleget0(".rtd", lead), `
  `) eleget0(".rtd", lead).appendChild(imgfn) //end(imgfn, `B)
`) after(imgfn, `
`) imgfn2.innerText = "" eleget0(".rtd", lead).appendChild(imgfn2) imgfn2.appendChild(img) eleget0(".rtd", lead).appendChild(honbun) elegeta('div.thre>a>img,div.thre>blockquote').forEach(e => { e.dataset.hiddenzero = 1; e.style.display = "none" }) elegeta('div.thre>span.rsc,div.thre>span.csb,div.thre>span.cnm,div.thre>span.cnw,div.thre>span.cno').forEach(e => { e.dataset.hiddenzero = 1; e.style.display = "none" }) // レス0のダブルクリックでfutapoのカタログでどの9メモにヒットして開いたスレかを照会 eleget0('.thre td.rtd')?.addEventListener("dblclick", e => { let memoa = pref('FUTACHAN_CATALOG : SearchMyMemo') || []; let r0t = honbun.innerText; let res0hits = memoa.filter(v => ["★"].includes(v?.m)).filter(v => { let re = (isValidRE(v.t)) ? new RegExp(v.t, "mi") : null; if ((re && re.test(r0t)) || han(v.t) == r0t) { return 1 } }).map(v => "★" + v.t); let res0hits8 = memoa.filter(v => ["◎"].includes(v?.m)).filter(v => { let re = (isValidRE(v.t)) ? new RegExp(v.t, "mi") : null; if ((re && re.test(r0t)) || han(v.t) == r0t) { return 1 } }).map(v => "◎" + v.t); if (res0hits?.length || res0hits8?.length) popupCenter(`ふたぽカタログの9メモにヒット:\n${res0hits.join("\n")}\n\nふたぽカタログの8メモにヒット:\n${res0hits8.join("\n")}`); e.preventDefault(); return false; }, true) } $(document).on("keydown", e => { e.key == "c" && !e.shiftKey && !e.altKey && !e.ctrlKey && eleget0('div.thre>table[data-rsc="0"]:hover') && $(eleget0('div.thre>a.sod:not(.sodmypush)')).click().effect("highlight") }) } function popupCenter(text, bgcolor = text == "解除" ? "#888" : "#35a") { if (!text) return text = String(text).replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/`/g, '`').replace(//g, ">").replace(/\n/gm, "
") let e = end(document.body, `
${text}
`) $(e).on("click", e => { // GM.setClipboard(v.target.innerText); // v.target.innerText = `「${v.target.innerText}」をクリップボードにコピーしました` removePopup(1) }) removePopup() function removePopup(stime = Date.now()) { clearTimeout(this?.TO) if (Date.now() > 5000 + stime && !eleget0('.yhmpuc:hover')) $("#yhmpucBG").fadeOut(155, e => $("#yhmpucBG").remove()); else this.TO = setTimeout(removePopup, 111, stime) } } // 引用ポップアップを置き換える let replaceMainPopup = (FUTABA_HOVER_POPUP_REPLACE) || !is2chan if ((is2chan || ld('kako.futakuro.com')) && replaceMainPopup) { GM_addStyle("#slp{z-index:1001} .qtd{display:none !important;} .fvw_respop,#respopup_area{display:none;opacity:0}"); } $("#contres>a,#fvw_loading").attr("title", ($("#contres>a,#fvw_loading").attr("title") || "新着レスを読み込みます") + "\nE:リロード\nD/右クリック:リロード+新着にスクロール").on("contextmenu", (e) => { SITE.funcD(); return false; }) // m::監視ワード設定 document.addEventListener("focus", () => GF.alertWord = pref("alertWord")?.replace(/^([\s\S]*)<\/string>$/, "$1") || "") keyFuncAdd([{ key: 'm', func: () => { const tips = "Tips:\n「ABCやDEFを含まず、GHIかJKLを含み、かつMNOとPQRも含む」\n!ABC|DEF GHI|JKL MNO PQR\n"; let a = prompt(`m:\n監視ワードを正規表現+独自構文で設定できます\nこれにヒットしたレスはNotificationで通知されます\n\n${tips}\n\n現在の設定値:\n${GF?.alertWord||"なし"}\n\n推奨:Firefoxではalerts.useSystemBackend false`, GF?.alertWord || "") if (a === null) return; try { GF.alertWordRE = new RegExp(a.replace(/^S$/, "??").replace(/^S([\s \||])/, "??$1").replace(/([\s \||!!])S([\s \||])/, "$1??$2").replace(/([\s \||!!])S$/, "$1??").replace(/|/gm, "|").replace(/^[\!|!](\S*)/, "^(?!.*($1)).*").replace(/(\S*)[  ](\S*)/gm, "^(?=.*($1))(?=.*\($2\))").replace(/\s| /gm, ".*"), "mi"); //alert(searRE); // 独自構文を正規表現に変換 } catch (e) { alert(`${a}\n\nは正規表現としてエラーが出てしまうため取り消します\n\n参考:\nhttps://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Regular_expressions\n`); a = ""; } GF.alertWord = a; pref("alertWord", a ? `${a}` || "" : "") } }]) if (FUTABA_FLOAT_RELOAD_BUTTON) { $('#contres').attr("floated", ""); GM_addStyle(`#contres{z-index:999; position:fixed; right:18em; left:auto; bottom:3px; height:auto; padding:0.1em 0.2em; border:2px solid #dddddd; background-color:#ffffff;}`) } pick2title(1) GF.myRes = new Set(); eleget0('input[value="返信する"]')?.addEventListener("click", e => { let ysend = window.scrollY; // 返信した時点の縦スクロール位置を記憶して更新後それを復元する //GF.myRes.add(eleget0("#ftxa")?.value?.replace(/\s+$/gm, "\n")?.replace(/^\s+/gm, "")?.trim()) // 自分の返信を記憶 GF.myRes.add(eleget0("#ftxa")?.value?.replace(/^[ \t]+/gm, "")?.replace(/[ \t]+$/gm, "")?.trim()) // 自分の返信を記憶 restoreScrollY(1000); function restoreScrollY(i) { if (Math.abs(window.scrollY - ysend) < 11 && i--) requestAnimationFrame(() => restoreScrollY(i)); else { if (fileSel) fileSelectOff(); window.scrollTo(0, ysend); requestAnimationFrame(() => window.scrollTo(0, ysend)); } } }, true) $(document).on("click", ".relallAreaon", e => SITE?.keyFunc?.find(v => v.id === "z")?.func(e.ctrlKey ? "Shift+Z" : "z")) // 自動リロード:: if (1 || FUTABA_AUTO_RELOAD_INTERVAL >= 1) { var musousa = { last: Date.now(), elapsed: () => { return Date.now() - this.last }, init: () => { this.last = Date.now(); $('body').on('keydown mousedown mousemove', () => this.last = Date.now()); }, } musousa.init() GF.reloadHis = []; //n:: eleget0('span#contres')?.addEventListener("mousemove", e => { if (FUTABA_DEBUG >= 1 || debug) e.target.setAttribute("title", sani(`E:リロード\nD/右クリック:リロード+新着にスクロール\n${GF?.reloadHis?.join("\n")||""}\n前回ロード後経過:${Math.floor((Date.now() - (GF?.latestReload||0))/1000)}秒`)); }) eleget0('span#contres')?.addEventListener("mouseout", e => { e.target.title = `E:リロード\nD/右クリック:リロード+新着にスクロール`; }); [...new Set(elegeta('input[value="返信する"] , #contres>a , #fvw_loading , a#akahuku_reload_button'))].forEach(e => { e?.addEventListener("click", e => { // let inter = 60*1000; GF.reloadHis.push(`update:${gettime("hh:mm:ss")} / 新着:${GF?.newarticle} / 前回ロード後経過秒:${~~((Date.now()-GF.latestReload)/1000)} / 無操作秒:${~~(musousa.elapsed()/1000)} / 前回:${GF.latestInterval/1000}`); GF.reloadHis = GF.reloadHis.slice(-30) GF.latestReload = Date.now(); //GF.latestInterval = inter; //bcc.setBusy() }, true) }); let bcc = { channel: null, lastReload: { time: Date.now(), src: "" }, init() { if (!this.channel) this.channel = new BroadcastChannel('YHM_REL'); this.channel.onmessage = (event) => { if (event.data.type == 'reload') this.lastReload = { time: event.data.time, src: event.data.src }; } }, setBusy(e) { this.channel.postMessage({ type: 'reload', time: Date.now(), src: GF?.originalDocTitle?.slice(0, 9) || "?" }); this.lastReload = { type: 'reload', time: Date.now(), src: GF?.originalDocTitle?.slice(0, 9) || "?" }; }, isBusy(interval = 2000, f = null) { if (Date.now() - this.lastReload.time < interval) { f?.(interval, this); return this.lastReload; } } } bcc.init() GF.nReloadSI = setInterval(() => { //if (GF?.stopThre) return; // if (document.body.textContent.match("スレッドがありません|上限\d+レスに達しました") && !GF?.stopThre) { if (document.body.textContent.match("スレッドがありません|上限\d+レスに達しました")) { clearInterval(GF?.nReloadSI) //GF.stopThre = 1 if (!GF?.isFile) threendAnten() if (document.visibilityState == "hidden") { document.title = `🐾${document.title}` } return; } if (!GF?.reloadAndNotifyNewArrival) return; // if ((musousa.elapsed() < 30000 && document.activeElement.tagName.match(/textarea|input/i)) || eleget0('//span[@id="thread_down"]') || GF?.stopThre) return; // if ((musousa.elapsed() < 30000 && document.activeElement.tagName.match(/textarea|input/i)) || eleget0('#thread_down') || GF?.stopThre) return; if ((musousa.elapsed() < 30000 && (document.visibilityState == "visible" && document.activeElement.tagName.match(/textarea|input/i))) || eleget0('#thread_down')) return; let r = location.protocol != "file:" && eleget0('#contres>a,#fvw_loading') if (!r) return let inter = Math.max(60 * 1000, Math.min(Math.max(600000, (FUTABA_AUTO_RELOAD_INTERVAL * 60 * 1000)), (FUTABA_AUTO_RELOAD_INTERVAL * 60 * 1000) + (musousa.elapsed() >= 59000 && GF?.newarticle == 0 && GF.latestInterval / 1))) // 1分以上無操作の時リロードして新着がないと更新間隔を広げていく、最低1分最大10分 if (!GF.latestReload || Date.now() - GF.latestReload >= inter) { if (bcc.isBusy(5000, (interval, bcc) => { GF.reloadHis.push(`skip:${gettime("hh:mm:ss")} ※(${sani(bcc?.lastReload?.src)})の更新(${gettime("hh:mm:ss", new Date(bcc?.lastReload?.time))})が${~~(Date.now()/1000)- ~~(bcc.lastReload.time/1000)}秒前`); //GF.reloadHis.push(` 延期:${gettime("hh:mm:ss")} <${gettime("hh:mm:ss", new Date(bcc?.lastReload?.time))}(${sani(bcc?.lastReload?.src)})+${interval/1000}秒`); GF.latestReload += 5000 + Math.random() * 999; })) { return; } //GF.reloadHis.push(`n)自動リロード : ${gettime("YYYY/MM/DD hh:mm:ss")} / 現在更新間隔:${~~(inter/1000)} / 新着:${GF?.newarticle} / リロード後経過秒:${~~((Date.now()-GF.latestReload)/1000)} / 無操作秒:${~~(musousa.elapsed()/1000)} / 前回:${GF.latestInterval/1000}`); if (FUTABA_DEBUG >= 2) end(document.body, `
update : ${gettime()} / 現在更新間隔:${~~(inter/1000)} / 前回新着:${GF?.newarticle} / リロード後経過秒:${~~((Date.now()-GF.latestReload)/1000)} / 無操作秒:${~~(musousa.elapsed()/1000)} / 前回:${GF.latestInterval/1000}
`) r?.click() //GF.latestReload = Date.now() GF.latestInterval = inter bcc.setBusy() } if (FUTABA_DEBUG >= 3) document.title = `${~~(inter/1000)}/${GF?.newarticle}/${~~((Date.now()-GF.latestReload)/1000)}/${~~(musousa.elapsed()/1000)} 現在更新間隔:${~~(inter/1000)} / 新着:${GF?.newarticle} / リロード後経過秒:${~~((Date.now()-GF.latestReload)/1000)} / 無操作秒:${~~(musousa.elapsed()/1000)} / 前回:${GF.latestInterval/1000}` }, 1000) } // 画面最下段で下ホイール/下キーでリロード window.addEventListener('wheel', (e) => { if (e.deltaY > 0 && document.body.clientHeight - window.innerHeight <= window.pageYOffset && Date.now() - GF.latestReload >= 5000) { if (location.protocol != "file:") { $('#contres>a').effect("highlight"); eleget0('#contres>a,#fvw_loading,a#akahuku_reload_button')?.click() } GF.latestReload = Date.now(); } }) document.addEventListener('keydown', e => { if (e?.target?.tagName === 'TEXTAREA' || e?.target?.isContentEditable) return; if (["PageDown", "End", "ArrowDown", "ArrowRight"].includes(e.key) && document.body.clientHeight - window.innerHeight <= window.pageYOffset && Date.now() - GF.latestReload >= 5000) { location.protocol != "file:" && $('#contres>a').click().effect("highlight"); GF.latestReload = Date.now(); } }, true) // c::ホバー下にそうだね var xTarget = [] var cnoPushed = new Set() var soddone = new Set() var sodTarget = [] var lastc = 0 document.addEventListener('keypress', 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; var ele = document.elementFromPoint(mousex, mousey); var cno = eleget0('table:not([data-reszero]) .cno', ele?.closest('table'))?.textContent?.replace(/\D/g, "") // if (cno && key == "c") $(ele?.closest('td')).effect("highlight") if (cno && key == "c" && cno && !cnoPushed.has(cno)) { $(ele?.closest('td')).effect("highlight") cnoPushed.add(cno) xTarget.push({ key: key, cno: cno }); xTarget = (Array.from(new Set(xTarget.map(v => JSON.stringify(v))))).map(v => JSON.parse(v)) // uniq:オブジェクトの配列→JSON文字列配列→uniq→オブジェクトの配列 dosou() //end(eleget0('.sod', ele?.closest('td')), `?`) // くるくるを表示 //elegeta(`table .cno:text*=${cno}`).forEach(e => end(eleget0(".sod", e?.closest('table')), `?`)) // くるくるを表示 } }) function dosou() { if (Date.now() - lastc < 4000) return; var a = xTarget.shift() if (!a) return let [key, cno] = [a.key, a.cno] let t = eleget0(`table .cno:text*=${cno}`)?.closest('table') if (!t) return; if (key == "c" && !(soddone.has(t))) { //$('.waiting', t).remove() //elegeta(`table .cno:text*=${cno}`).forEach(e => eleget0(".waiting", e?.closest('table'))?.remove()) eleget0('.sod', t)?.click() $('.sod', t).effect("highlight") // eleget0('//div[@class="thre"]/a[@class="sod"]') lastc = Date.now() soddone.add(t) //t.dataset.soddone = 1; } setTimeout(dosou, 4100) } dosou() //setInterval(dosou, 4100) // そうだねを改行しないようにする //document.body.addEventListener('copy', (event) => { $("#pickbox .sod:contains('x')").css({ "font-size": "125%", "float": "none" }); setTimeout(() => { $("#pickbox .sod:contains('x')").css({ "font-size": "125%", "float": "right" }) }, 1) }); var sdset = () => { if (is2chan) elegeta('#pickbox .sod:not([onclick]),.ftbpu .sod:not([onclick])').forEach(e => { e.setAttribute("onclick", `sd(${e?.id.replace(/\D/g,"")});return(false);`) }) } // $(document).on("click", "a.sod", (e) => { e.target.style.color = "#f00" }) //$(document).on("click","a.sod",(e)=>{e.target.style.textDecoration="underline"}) $(document).on("click", "a.sod", (e) => { e.target.classList.add("sodmypush") }) //$(document).on("click","a.sod",(e)=>{e.target.style.textDecoration="underline"}) location.protocol != "file:" && $("#contres>a,#fvw_loading").on("click", (e => { GF.newarticle = 0 let latestEle = eleget0('//*[@class="thre"]/table[last()]'); //setTimeout(() => { document.body.dispatchEvent(new Event('2chanReloaded')) }, 1500) document.body.dispatchEvent(new Event('2chanReloadedNodelay')) })); // リロード再実行 moq(eleget0('.thre:not(#pickbox .thre)'), `table[border="0"] , div:has(table[border="0"]):not(#pickbox , .qtd)`, v => { // ,divはfutakuro共存用 //moq(eleget0('.thre:not(#pickbox .thre'), "table,div", v => { // ,divはfutakuro共存用 if (!GF.stopmoq) { GF.newarticle = elegeta('.thre table .rsc:not([data-basec])').length || 0; //console.log(v.length, GF.newarticle) GF.newarticle && setTimeout(() => { document.body.dispatchEvent(new Event('2chanReloaded')) }, 1) } }) window.addEventListener("focus", () => { // firefox/chrome document.title = document.title.replace(/^🐾/g, ""); if (GF?.arrival) { document.title = document.title.replace(/^[🐾🔴🔵⚠️■]+/g, ""); GF.arrival = 0 } // let threEnd=eleget0("span#contres:text*=スレッドがありません|上限\d+レスに達しました");console.log(threEnd) let threEnd = eleget0("span#contres:text*=スレッドがありません|上限\\d+レスに達しました"); threEnd?.classList?.remove("threend") if (threEnd && lh(/^https:\/\/[^\.]+\.2chan\.net\//)) threendAnten() }, true) function threendAnten() { let threEnd = eleget0("span#contres:text*=スレッドがありません|上限\\d+レスに達しました"); threEnd.animate([{ boxShadow: "0 0 0em 99em #00000018", outline: "6px solid #f00", offset: 0 }, { zIndex: "999999", boxShadow: "0 0 0em 99em #00000018", outline: "6px solid #f00", offset: 0.8 }, { boxShadow: "0 0 0em 99em #0000", outline: "6px solid #f000", offset: 1 }], { fill: "both", duration: 300 }) } function moq(observeNode, targetCSSSelector, cb) { new MutationObserver((m) => { let eles = [...m.filter(v => v.addedNodes).map(v => [...v.addedNodes]).filter(v => v.length)].flat().find(v => v.nodeType === 1 && v?.matches(targetCSSSelector)); if (eles) cb(eles) })?.observe(observeNode || document.body, { attributes: false, childList: true, subtree: false }); } document.body.addEventListener('2chanReloadedNodelay', function() { const place = elegeta('.thre table:not(#pickbox table,.ftbpu table,#respopup_area table,[floated] table)')?.pop() after(place, `
`) //$(`
`).fadeIn("slow", function() { $(this).hide(0).fadeIn("slow") }).insertAfter($(elegeta('.thre table:not(#pickbox table,.ftbpu table,#respopup_area table,[floated] table)').pop())) }, false); $(document).on("scroll", () => { if (elegeta('.reloadline:not([found])').filter(e => isinscreen(e)).length) { // $(elegeta('.reloadline')).attr("found", 1).delay(2000).hide("slow").delay(3000, function() { $(this).remove(); }) $('.reloadline').attr("found", 1).delay(2000).hide("slow").delay(3000, function() { $(this).remove(); }) } }) /* $(elegeta('//table[@class="deleted"]|//tr/td[@class="rtd"]/blockquote[contains(text(),"del")]/../../../..')).attr("title", "クリックで復帰").attr("floated", "1").animate2({ "opacity": "0.3", "transform": "scale(0.8)", "transform-origin": "right", "float": "right" }, 500).one("click", function() { $(elegeta('//table[@class="deleted"]|//tr/td[@class="rtd"]/blockquote[contains(text(),"del")]/../../../..')).attr("title", "").animate2({ "opacity": "0.7", "transform": "scale(1)", "transform-origin": "right", "float": "none" }, 500); */ $(elegeta('//table[@class="deleted"]')).attr("title", "クリックで復帰").attr("floated", "1").animate2({ "opacity": "0.3", "transform": "scale(0.8)", "transform-origin": "right", "float": "right" }, 500).one("click", function() { $(elegeta('//table[@class="deleted"]')).attr("title", "").animate2({ "opacity": "0.7", "transform": "scale(1)", "transform-origin": "right", "float": "none" }, 500); $(this)[0].scrollIntoView({ block: "nearest", behavior: "smooth" }) }) // 隔離を薄くして右に $(document.body).append(``) addstyle.add(`.embedyt{margin-bottom:0; max-width:43vw; margin-top:-0.5em; min-width: unset !important; width:fit-content; display: grid; grid-template-columns: repeat(auto-fit, minmax(321px, 1fr)); gap: 0px;}`) //addstyle.add(`.embedyt{margin-bottom:0; max-width:43vw; margin-top:-0.5em; min-width: unset !important; width:fit-content; display: grid; grid-template-columns: repeat(auto-fit, 323px; gap: 0px;}`) if (GF?.isFile) { // $('blockquote:has(#ytplayer , iframe):not(#pickbox p) , div.vsc-controller').remove(); $('blockquote :is(p:has( #ytplayer) , p:has( iframe)) , .youtubeBQP , .nicovideoBQP , div.vsc-controller').remove(); //$('.embedyt p:has(#ytplater , iframe):not(#pickbox p) , div.vsc-controller').remove(); elegeta('*[yte] , *[nde] , *[data-yte] , *[ytlinked] , *[data-nde]').forEach(e => { e.removeAttribute("yte"); e.removeAttribute("nde") delete e?.dataset?.yte; delete e?.dataset?.nde; e.removeAttribute("ytlinked") }) } // 検索結果ではリンクになっていないyoutube urlをリンク化 //https://dec.2chan.net/85/futaba.php?guid=on if (lh(/https:\/\/[^\.]+\.2chan\.net\/[^\/]*\/futaba\.php\?guid\=on/) || eleget0('html > body > h4:text*=の検索結果"')) elegeta('.rtd blockquote:not([ytlinked]) , div.thre > blockquote:not([ytlinked])').forEach(e => { e.setAttribute("ytlinked", "ytlinked"); e.innerHTML = replaceTextnode(e.innerHTML, /(https?:\/\/(?:www\.nicovideo\.jp|nico.ms)\/[^\<\>\"$]*)/gmi, `$1`) e.innerHTML = replaceTextnode(e.innerHTML, /(https?:\/\/(?:www\.|m\.|)(?:youtube\.com|youtu\.be)\/[^\<\>\"$]*)/gmi, `$1`) }) function replaceTextnode(html, search, replacement) { const tempDiv = document.createElement('div'); tempDiv.innerHTML = html; function replaceText(node) { if (node.nodeType === Node.TEXT_NODE) { const newContent = node.nodeValue.replace(search, replacement); if (newContent !== node.nodeValue) { const newNode = document.createElement('span'); newNode.innerHTML = newContent; node.replaceWith(newNode); } } else { node.childNodes.forEach(replaceText); } } replaceText(tempDiv); return tempDiv.innerHTML; } // ニコ動埋め込み(PrivacyBadger等は要Disable) var embednv = () => { var elea = elegeta('a[href*="nicovideo"]:not([data-nde]),a[href*="//nico.ms/sm"]:not([data-nde])').filter(e => !e.closest('font[color="#789922"]')) for (let ele of elea.filter(e => eleget0('.yhmMyMemo', e.closest("table"))).concat(elea.filter(e => isinscreen(e))).concat(elea)) { // リロード回避のためpickされるものは先に読み込む ele.setAttribute("nde", "nde"); if (ele.dataset.nde) continue; ele.dataset.nde = 1 let url = ele.href; //innerText.replace(/^ttp/i, "http"); var nico = url.match(/h?ttps?:\/\/(?:www\.|sp\.)?nicovideo.jp\/watch(?:_tmp)?\/(.*)/i); //var nico = url.match(/h?ttps?:\/\/(?:www\.|sp\.)?nicovideo.jp\/watch\/(.*)/i); if (!nico) var nico = url.match(/h?https:\/\/nico\.ms\/(.*)/i); if (!nico) continue; let bq = ele?.closest("td,div.thre>blockquote:not([yte])") let vbq = eleget0('.embedyt', ele?.closest("td,div.thre>blockquote:not([yte])")) if (!vbq) $(bq).append('
'); //$(eleget0('.embedyt', bq)).append(`

`); addstyle.add(`.youtubeBQP { overflow:hidden; padding:0 11px 0 0; margin 0px; display:inline-block;} .youtubeBQP:hover{resize:both; background-color:#ffffff88; outline:1px solid #88888888;}`) let ytv = end(eleget0('.embedyt', bq), `

`); // let ytv = end(eleget0('.embedyt', bq), `

`); ytResize2(2); dragElement2(ytv, "*", "iframe", ".youtubeBQP"); //$(ele?.closest("td,div.thre>blockquote:not([nde])")).append(`

`) // 埋め込み外部プレイヤー版 //break; // 一度に1つずつしかやらない } if (elea.length) setTimeout(embednv, 2000 + elea.length * 10); } autoPagerized(embednv) // youtube埋め込み var embedyt = () => { var elea = elegeta('a[href*="youtube.com"]:not(#pickbox a,[data-yte]) , a[href*="youtu.be"]:not(#pickbox a,[data-yte])').filter(e => !e.closest('font[color="#789922"]')) for (let ele of elea.filter(e => eleget0('.yhmMyMemo', e.closest("table"))).concat(elea.filter(e => isinscreen(e))).concat(elea)) { // リロード回避のためpickされるものは先に読み込む if (ele.dataset.yte) continue; ele.dataset.yte = 1 ele.setAttribute("yte", "yte"); let url = ele.href //innerText; var sm = (url.match(/h?ttps?:\/\/youtu\.be\/(?:watch\?v=)?([a-zA-Z0-9_\-]{11}).*[\?\&]t=(\d*).*$/i) || url.match(/h?ttps?:\/\/(?:www\.|m\.)?youtube\.com\/(?:live\/|watch\S*?[\?\&]v=)([a-zA-Z0-9_\-]{11}).*&t=(\d*).*$/i)) || url.match(/h?ttps?:\/\/youtu\.be\/(?:watch\S*?[\?\&]v=)?([a-zA-Z0-9_\-]{11})/i) || url.match(/h?ttps?:\/\/(?:www\.|m\.)?youtube\.com\/shorts\/([a-zA-Z0-9_\-]{11})/i) || url.match(/h?ttps?:\/\/(?:www\.|m\.)?youtube\.com\/(?:live\/|watch\S*?[\?\&]v=)([a-zA-Z0-9_\-]{11})/i); // var videoList = url.match0(/(?:https:\/\/www\.youtube\.com\/embed\/\?playlist=|https:\/\/www\.youtube\.com\/watch_videos\?video_ids=)([a-zA-Z0-9_\-\,]+)/)?.split(",")?.filter(v => v.match(/^[a-zA-Z0-9_\-]{11}$/)); var videoList = url.match0(/(?:https:\/\/www\.youtube\.com\/embed\/\?playlist=|https:\/\/www\.youtube\.com\/watch_videos\?video_ids=)([a-zA-Z0-9_\-\,]+)/)?.split(",")?.filter(v => v.match(/^[a-zA-Z0-9_\-]{11}$/)); //var videoList = url.match0(/https:\/\/www\.youtube\.com\/watch_videos\?video_ids=([a-zA-Z0-9_\-\,]+)/)?.split(",")?.filter(v => v.match(/^[a-zA-Z0-9_\-]{11}$/)); var pl = (url.match0(/[\&\?]list=([a-zA-Z0-9_\-]+)/)); if (!pl && !sm && !videoList) continue; if (1) { // 2列詰め込む let bq = ele?.closest("td,div.thre>blockquote:not([yte])") let vbq = eleget0('.embedyt', ele?.closest("td,div.thre>blockquote:not([yte])")) if (!vbq) $(bq).append('
'); let src = sm ? 'https://www.youtube.com/embed/' + sm[1] + (pl ? `?list=${pl}` : "") + (sm[2] ? `${pl?"&":"?"}start=` + sm[2] : "") : videoList ? `https://www.youtube.com/embed/${videoList[0]}?playlist=${videoList?.join(',')}` : pl ? `https://www.youtube.com/embed?listType=playlist&list=${pl}` : ""; if (src) { addstyle.add(`.youtubeBQP { overflow:hidden; padding:0 11px 0 0; margin 0px; display:inline-block;} .youtubeBQP:hover{resize:both; background-color:#ffffff88; outline:1px solid #88888888;}`) let ytv = end(eleget0('.embedyt', bq), `

`); ytResize2(2); dragElement2(ytv, "*", "iframe", ".youtubeBQP"); ele.title = `${sani(url)}\n↓\n${sani(src)}`; } } else { // 1列モード if (sm) $(ele?.closest("td,div.thre>blockquote:not([yte])")).append('