您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
カタログのスレをNGで非表示
// ==UserScript== // @name futaba_catalog_NG // @namespace https://github.com/akoya-tomo // @description カタログのスレをNGで非表示 // @author akoya_tomo // @match http://*.2chan.net/*/futaba.php?mode=cat* // @match https://*.2chan.net/*/futaba.php?mode=cat* // @version 1.10.0 // @require http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js // @require https://cdn.jsdelivr.net/npm/[email protected]/src/md5.min.js // @grant GM_registerMenuCommand // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @license MIT // @run-at document-start // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMjHxIGmVAAABXElEQVQ4T8VSTUvDQBRcEcGL0H+QQ3c3KSVtmhRMe5aC4rX+Fb15F/PVUqFSqwcV8SxSPYknUcSDKCqICNZ/YBEv62xcS0CQ5iBOeMm+92beDrsh/w8WMIuFrEdD2kdc8ZAvacvapGoPIWs84IsxJ+J96I6yftYmKLywiInCekFUdirCbJsCzfNiUMwoLdECLQPe2TfH6lhCarDxK8HrKU5UlLfKwu7agvnsUOkJDehBaaMknE1nyIsjYM8ExBkMGSQbkiid0FU6D9uzcv1DHLEB9Wkt3kH3dBNDTpIEd9uVFvdA2o3XiR4cnfIVXojFScDSNAghnnu9qUuLD1jf5Vo5gTN4RETUo66i/w7msTmI3yH64D5fQGnsq5MCcHCNa71VaXpgwAXiUqXpgd1vEOkH5Fv5mt7Qj+WJG01jTZVHh9Ewuvhd3+S32qlOqfLoqO/Xx522M6HSvwQhn8ChorvE+0t7AAAAAElFTkSuQmCC // ==/UserScript== this.$ = this.jQuery = jQuery.noConflict(true); (function ($) { /** * 設定 */ var USE_NG_IMAGES = true; // スレ画像のNGを有効にする var MAX_NG_THREADS = 512; // NGスレの最大保持数(板毎) var MAX_REGISTERED_NG_IMAGES = 2048; // NG画像の最大登録数 var MAX_OK_IMAGES = 1024; // 非NG画像名の最大保持数(板毎) var HIDE_CATALOG_BEFORE_LOAD = false; // ページの読み込みが完了するまでカタログを隠す var USE_NG_THREAD_CLEAR_BUTTON = true; // スレNGのクリアボタンを使用する var USE_NG_DISABLE_BUTTON = true; // NG機能一時無効ボタンを使用する var USE_DHASH = false; // 近似画像NGを使用する var DISTANCE_THRESHOLD = 3; // 近似画像判定閾値(デフォルト:3) var ENABLE_DHASH_TEST = false; // 近似画像NGのテストモードを有効にする var CHECK_NG_LIST = true; // NGリストが正常かチェックする var serverName = document.domain.match(/^[^.]+/); var pathName = location.pathname.match(/[^/]+/); var serverFullPath = serverName + "_" + pathName; var selectIndex = -1; var imageList = GM_getValue("_futaba_catalog_NG_images", []); var commentList = GM_getValue("_futaba_catalog_NG_comment", []); var dateList = GM_getValue("_futaba_catalog_NG_date", []); var dHashList = GM_getValue("_futaba_catalog_NG_dHashes", []); var images, ngDate, okImages, dHashes; var item_md5_text = " md5"; var item_comment_text = " コメント"; var item_date_text = " 最終検出日"; var item_dHash_text = " dHash"; var isNgEnable = true; // dHashリスト初期化 if (dHashList.length == 0 && imageList.length > 0) { dHashList = new Array(imageList.length).fill(null); GM_setValue("_futaba_catalog_NG_dHashes", dHashList); } if (CHECK_NG_LIST && USE_NG_IMAGES && !isNgListNormal()) { alert("NGリストが壊れています"); } if (HIDE_CATALOG_BEFORE_LOAD) { hideCatalog(); } else { $(init); } function isNgListNormal() { var imageNum = imageList.length; var commentNum = commentList.length; var dateNum = dateList.length; var dHashNum = dHashList.length; //console.log("futaba catalog NG - imageNum: " + imageNum); //console.log("futaba catalog NG - commentNum: " + commentNum); //console.log("futaba catalog NG - dateNum: " + dateNum); //console.log("futaba catalog NG - dHashNum: " + dHashNum); return (imageNum == commentNum) && (imageNum == dateNum) && (imageNum == dHashNum); } function init() { //clearNgNumber(); //console.log("futaba_catalog_NG - commmon: " + // GM_getValue("_futaba_catalog_NG_words", "")); //console.log("futaba_catalog_NG - indivisual: " + // getCurrentIndivValue("NG_words_indiv", "")); GM_registerMenuCommand("NGワード編集", editNgWords); if (USE_NG_IMAGES) { GM_registerMenuCommand("NGリスト編集", editNgList); } GM_registerMenuCommand("スレNGクリア", confirmClearNgNumber); setStyle(); makeNgMenubar(); makeConfigUI(); makeNgListUI(); makeNgButton(); hideNgThreads(); checkAkahukuReload(); listenKoshianDelEvent(); listenFthPickupEvent(); } /** * NG番号クリア * @param {boolean} forced 強制的にクリアするか */ function clearNgNumber(forced) { if (!forced && window.name) { return; } window.name = location.href; var ngNumberObj = getIndivObj("NG_numbers_indiv"); if (ngNumberObj === "") { ngNumberObj = {}; } ngNumberObj[serverFullPath] = []; var jsonString = JSON.stringify(ngNumberObj); GM_setValue("NG_numbers_indiv", jsonString); } /** * 各板個別NGデータを保存 * @param {string} target 保存するNG名 * @param {string|Array} val 保存するNGデータ */ function setIndivValue(target, val) { var indivObj = getIndivObj(target); if (indivObj === "") { indivObj = {}; } indivObj[serverFullPath] = val; var jsonString = JSON.stringify(indivObj); GM_setValue(target, jsonString); //console.log("futaba_catalog_NG: " + target + " updated@" + serverFullPath + " - " + val); } /** * 各板個別NGデータのオブジェクトを取得 * @param {string} target 取得するNG名 * @return {object} 取得したNGデータ */ function getIndivObj(target) { var indivVal = GM_getValue(target, ""); var indivObj = ""; if (indivVal !== "") { indivObj = JSON.parse(indivVal); } return indivObj; } /** * NGワード編集メニュー表示 */ function editNgWords(){ var wordsCommon = GM_getValue("_futaba_catalog_NG_words", ""); var wordsIndiv = getCurrentIndivValue("NG_words_indiv", ""); $("#GM_fcn_ng_words_common").val(wordsCommon); $("#GM_fcn_ng_words_individual").val(wordsIndiv); var $configContainer = $("#GM_fcn_config_container"); $configContainer.fadeIn(100); } /** * 現在の板の個別NGデータを取得 * @param {string} target NGデータ名 * @param {string|Array} defaultVal NGデータが未定義のときの既定値 * @return {string|Array} 現在の板の個別NGデータ */ function getCurrentIndivValue(target, defaultVal) { var indivObj = getIndivObj(target); var currentIndivVal; if (indivObj !== "") { currentIndivVal = indivObj[serverFullPath]; } if (!currentIndivVal) { currentIndivVal = defaultVal; } return currentIndivVal; } /** * NGリスト編集メニュー表示 */ function editNgList() { // マウスホイールリロード対策 $("<div>", { id: "GM_fcn_catalog_space" }).appendTo("body"); if ($(window).scrollTop() < 2) { $("html, body").scrollTop(2); } $("html, body").css("overflow", "hidden"); refreshNgList(true); resetNgListItemText(); var $ngListContainer = $("#GM_fcn_ng_list_container"); $ngListContainer.fadeIn(100); } /** * NGリスト項目名リセット */ function resetNgListItemText() { $("#GM_fcn_ng_list_item_md5").text(item_md5_text + " "); $("#GM_fcn_ng_list_item_comment").text(item_comment_text + " "); $("#GM_fcn_ng_list_item_date").text(item_date_text + " "); $("#GM_fcn_ng_list_item_dHash").text(item_dHash_text + " "); } /** * スレNGクリア確認 */ function confirmClearNgNumber() { if (confirm("この板のスレNGを全てクリアします。\nよろしいですか?")) { $(".GM_fcn_ng_numbers").each(function() { $(this).removeClass("GM_fcn_ng_numbers"); $(this).css("display", ""); }); clearNgNumber(true); } } /** * NG機能無効切り替え */ function toggleNgDisable() { var $ngDisableButton = $("#GM_fcn_ng_disable_button"); if (isNgEnable) { $ngDisableButton.css("background", "#a9d8ff"); isNgEnable = false; $(".GM_fcn_ng_words").css("display", ""); $(".GM_fcn_ng_words").removeClass("GM_fcn_ng_words"); $(".GM_fcn_ng_numbers").css("display", ""); $(".GM_fcn_ng_numbers").removeClass("GM_fcn_ng_numbers"); $(".GM_fcn_ng_images").css("display", ""); $(".GM_fcn_ng_images").removeClass("GM_fcn_ng_images"); $(".GM_fcn_ng_dhash_td").removeClass("GM_fcn_ng_dhash_td"); $(".GM_fcn_ng_dhash_img").removeClass("GM_fcn_ng_dhash_img"); } else { $ngDisableButton.css("background", "none"); isNgEnable = true; hideNgThreads(); } } /** * NGリスト表示更新 * @param {boolean} loadNgList NGリストデータを読み込みするか */ function refreshNgList(loadNgList) { if (loadNgList) { imageList = GM_getValue("_futaba_catalog_NG_images", []); commentList = GM_getValue("_futaba_catalog_NG_comment", []); dateList = GM_getValue("_futaba_catalog_NG_date", []); dHashList = GM_getValue("_futaba_catalog_NG_dHashes", []); } var listCount = imageList.length; $(".GM_fcn_ng_list_row").remove(); for (var i = 0; i < listCount; ++i) { var row = $("<div>", { class: "GM_fcn_ng_list_row", click: function() { // eslint-disable-line no-loop-func selectIndex = $(this).index(); selectNgList(); } }).appendTo("#GM_fcn_ng_list_content"); row.append( $("<div>", { class: "GM_fcn_ng_list_image", text: imageList[i], }), $("<div>", { class: "GM_fcn_ng_list_dHash", text: dHashList[i] ? ("000000000000" + dHashList[i].toString(16)).slice(-13) : "-", }), $("<div>", { class: "GM_fcn_ng_list_comment", text: commentList[i], }), $("<div>", { class: "GM_fcn_ng_list_date", text: dateList[i], }), $("<div>", { class: "GM_fcn_ng_list_scrl", }) ); } $(".GM_fcn_ng_list_row").css("background-color", "#ffffff"); } /** * NGリスト選択 */ function selectNgList() { $(".GM_fcn_ng_list_row").css("background-color", "#ffffff") .eq(selectIndex).css("background-color", "#ffecfd"); $("#GM_fcn_md5").val(imageList[selectIndex]); $("#GM_fcn_comment").val(commentList[selectIndex]); } /** * NGメニューバー作成 */ function makeNgMenubar() { var $ngMenubarArea = $("<div>", { id: "GM_fcn_ng_menubar", css: { "background-color": "#F0E0D6" } }); var $ngWordsHeader = $("<span>", { id: "GM_fcn_ng_words_header", text: "NGワード", css: { "background-color": "#F0E0D6", fontWeight: "bolder", "padding-right": "16px" } }); $("#cattable").before($ngMenubarArea); $ngMenubarArea.append($ngWordsHeader); // 設定ボタン var $ngWordsButton = $("<span>", { id: "GM_fcn_config_ng_words", text: "[設定]", css: { cursor: "pointer", }, click: editNgWords }); $ngWordsButton.hover(function() { $(this).css("background-color", "#EEAA88"); }, function() { $(this).css("background-color", "#F0E0D6"); }); $ngWordsHeader.append($ngWordsButton); if (USE_NG_DISABLE_BUTTON) { // NG機能一時無効 var $ngDisableHeader = $("<span>", { id: "GM_fcn_ng_disalbe_header", text: "NG機能", css: { "background-color": "#F0E0D6", fontWeight: "bolder", "padding-right": "16px" } }); $ngWordsHeader.after($ngDisableHeader); // NG機能一時無効ボタン var $ngDisableButton = $("<a>", { id: "GM_fcn_ng_disable_button", text: "[一時無効]", css: { cursor: "pointer", }, click: toggleNgDisable }); $ngDisableHeader.append($ngDisableButton); } if (USE_NG_THREAD_CLEAR_BUTTON) { // スレNG var $ngThreadHeader = $("<span>", { id: "GM_fcn_ng_thread_header", text: "スレNG", css: { "background-color": "#F0E0D6", fontWeight: "bolder", "padding-right": "16px" } }); $ngWordsHeader.after($ngThreadHeader); // スレNGクリアボタン var $ngThreadClearButton = $("<span>", { id: "GM_fcn_ng_thread_clear_button", text: "[クリア]", css: { cursor: "pointer", }, click: confirmClearNgNumber }); $ngThreadClearButton.hover(function() { $(this).css("background-color", "#EEAA88"); }, function() { $(this).css("background-color", "#F0E0D6"); }); $ngThreadHeader.append($ngThreadClearButton); } if (USE_NG_IMAGES) { // NGリスト var $ngListHeader = $("<span>", { id: "GM_fcn_ng_list_header", text: "NGリスト", css: { "background-color": "#F0E0D6", fontWeight: "bolder", "padding-right": "16px" } }); $ngWordsHeader.after($ngListHeader); // NGリスト編集ボタン var $ngListButton = $("<span>", { id: "GM_fcn_edit_ng_list", text: "[編集]", css: { cursor: "pointer", }, click: editNgList }); $ngListButton.hover(function () { $(this).css("background-color", "#EEAA88"); }, function () { $(this).css("background-color", "#F0E0D6" ); }); $ngListHeader.append($ngListButton); } } /** * NGワード編集メニュー作成 */ function makeConfigUI() { var $configContainer = $("<div>", { id: "GM_fcn_config_container", css: { position: "fixed", "z-index": "1001", left: "50%", top: "50%", "text-align": "center", "margin-left": "-475px", "margin-top": "-50px", "background-color": "rgba(240, 192, 214, 0.95)", width: "950px", //height: "100px", display: "none", fontSize: "16px", fontWeight: "normal", "box-shadow": "3px 3px 5px #853e52", "border": "1px outset", "border-radius": "10px", "padding": "5px", } }); $("#GM_fcn_ng_words_header").append($configContainer); $configContainer.append( $("<div>").append( $("<div>").text("NGワード編集").css({ "background-color": "#ffeeee", "padding": "2px", "font-weight": "bold" }), $("<div>").text("スレ本文に含まれる語句を入力してください。 | を挟むと複数指定できます。正規表現使用可。") ), $("<div>").css("margin-top", "1em").append( $("<div>").append( $("<label>").text("全板共通").attr("for", "GM_fcn_ng_words_common"), $("<input>", { id: "GM_fcn_ng_words_common", class: "GM_fcn_input", css: { width: "54em", fontSize: "13.33px" } }), $("<span>").append( $("<input>", { class: "GM_fcn_config_button", type: "button", val: "区切り文字挿入", click: function() { insertDelimiter("GM_fcn_ng_words_common"); }, }) ) ), $("<div>").append( $("<label>").text("各板個別").attr("for", "GM_fcn_ng_words_individual"), $("<input>", { "id": "GM_fcn_ng_words_individual", "class": "GM_fcn_input", css: { width: "54em", fontSize: "13.33px" } }), $("<span>").append( $("<input>", { class: "GM_fcn_config_button", type: "button", val: "区切り文字挿入", click: function() { insertDelimiter("GM_fcn_ng_words_individual"); }, }) ) ) ), $("<div>").css({ "margin-top": "1em", }).append( $("<span>").css("margin", "0 1em").append( $("<input>", { class: "GM_fcn_config_button", type: "button", val: "更新", click: setNgWords }) ), $("<span>").css("margin", "0 1em").append( $("<input>", { class: "GM_fcn_config_button", type: "button", val: "キャンセル", click: function() { setCursor(); $configContainer.fadeOut(100); }, }) ) ) ); $(".GM_fcn_config_button").css({ "cursor": "pointer", "background-color": "#FFECFD", "border": "2px outset #96ABFF", "border-radius": "5px", "font-size": "13.33px", }).hover(function() { $(this).css("background-color", "#CCE9FF"); }, function() { $(this).css("background-color", "#FFECFD"); }); /** * カーソル位置にデリミタ挿入 * @param {string} id デリミタを挿入する入力欄のID名 */ function insertDelimiter(id){ var $input = $("#" + id); var val = $input.val(); var position = $input[0].selectionStart; var newval = val.substr(0, position) + "|" + val.substr(position); $input.val(newval); $input[0].setSelectionRange(position + 1 ,position + 1); } } /** * NGワードをセット */ function setNgWords() { var inputCommon = $("#GM_fcn_ng_words_common").val(); var inputIndiv = $("#GM_fcn_ng_words_individual").val(); GM_setValue("_futaba_catalog_NG_words", inputCommon); console.log("futaba_catalog_NG: common NGword updated - " + inputCommon); // eslint-disable-line no-console setIndivValue("NG_words_indiv", inputIndiv); setCursor(); $("#GM_fcn_config_container").fadeOut(100); hideNgThreads(true); } /** * NGワードのカーソルを先頭に移動 */ function setCursor() { var inputCommonElm = $("#GM_fcn_ng_words_common").get(0); if (inputCommonElm) { inputCommonElm.focus(); inputCommonElm.setSelectionRange(0, 0); // カーソルを先頭に移動 inputCommonElm.blur(); } var inputIndivElm = $("#GM_fcn_ng_words_individual").get(0); if (inputIndivElm) { inputIndivElm.focus(); inputIndivElm.setSelectionRange(0, 0); // カーソルを先頭に移動 inputIndivElm.blur(); } } /** * NGリスト編集メニュー作成 */ function makeNgListUI() { if (!USE_NG_IMAGES) { GM_setValue("OK_images_indiv", ""); return; } imageList = GM_getValue("_futaba_catalog_NG_images", []); commentList = GM_getValue("_futaba_catalog_NG_comment", []); dateList = GM_getValue("_futaba_catalog_NG_date", []); dHashList = GM_getValue("_futaba_catalog_NG_dHashes", []); var $ngListContainer = $("<div>", { id: "GM_fcn_ng_list_container", css: { position: "fixed", "z-index": "1001", left: "50%", top: "50%", "text-align": "center", "margin-left": "-475px", "margin-top": "-250px", "background-color": "rgba(240, 192, 214, 0.95)", width: "950px", //height: "500px", display: "none", fontSize: "16px", fontWeight: "normal", "box-shadow": "3px 3px 5px #853e52", "border": "1px outset", "border-radius": "10px", "padding": "5px", } }); $("#GM_fcn_ng_list_header").append($ngListContainer); $ngListContainer.append( $("<div>").append( $("<div>").text("NGリスト編集").css({ "background-color": "#ffeeee", "padding": "2px", "font-size": "16px", "font-weight": "bold" }), $("<div>").css("margin-top", "1em").append( $("<div>").append( $("<label>").text("md5:").attr("for", "GM_fcn_md5"), $("<input>", { id: "GM_fcn_md5", class: "GM_fcn_ng_list_input", readonly: "readonly", }), $("<label>").text("コメント:").attr("for", "GM_fcn_comment"), $("<input>", { id: "GM_fcn_comment", class: "GM_fcn_ng_list_input", keypress: function(e) { if (e.key == "Enter") { editSelectedRow(); } } }) ) ), $("<div>").css("margin-top", "1em").append( $("<div>").css("margin-left", "475px").append( $("<span>").append( $("<input>", { class: "GM_fcn_ng_list_button GM_fcn_ng_list_edit_button", type: "button", val: "修正", click: editSelectedRow }) ), $("<span>").append( $("<input>", { class: "GM_fcn_ng_list_button GM_fcn_ng_list_edit_button", type: "button", val: "削除", click: deleteSelectedRow }) ), $("<span>").css("margin", "0 0 0 1em").append( $("<input>", { class: "GM_fcn_ng_list_button GM_fcn_ng_list_move_button", type: "button", val: "上", click: function() { swapRow(selectIndex - 1); }, }) ), $("<span>").append( $("<input>", { class: "GM_fcn_ng_list_button GM_fcn_ng_list_move_button", type: "button", val: "下", click: function() { swapRow(selectIndex); }, }) ) ) ) ), $("<div>").css("margin-top", "1em").append( $("<div>", { id: "GM_fcn_ng_list_pane", }).append( $("<div>", { id: "GM_fcn_ng_list_item_row" }).append( $("<div>", { id: "GM_fcn_ng_list_item_md5", class: "GM_fcn_ng_list_item", text: item_md5_text + " ", click: function() { if ($(this).text() == item_md5_text + "▲") { resetNgListItemText(); $(this).text(item_md5_text + "▼"); sortNgList(0, -1); } else { resetNgListItemText(); $(this).text(item_md5_text + "▲"); sortNgList(0); } }, }), $("<div>", { id: "GM_fcn_ng_list_item_dHash", class: "GM_fcn_ng_list_item", text: item_dHash_text + " ", click: function() { if ($(this).text() == item_dHash_text + "▲") { resetNgListItemText(); $(this).text(item_dHash_text + "▼"); sortNgList(3, -1); } else { resetNgListItemText(); $(this).text(item_dHash_text + "▲"); sortNgList(3); } }, }), $("<div>", { id: "GM_fcn_ng_list_item_comment", class: "GM_fcn_ng_list_item", text: item_comment_text + " ", click: function() { if ($(this).text() == item_comment_text + "▲") { resetNgListItemText(); $(this).text(item_comment_text + "▼"); sortNgList(1, -1); } else { resetNgListItemText(); $(this).text(item_comment_text + "▲"); sortNgList(1); } }, }), $("<div>", { id: "GM_fcn_ng_list_item_date", class: "GM_fcn_ng_list_item", text: item_date_text + " ", click: function() { if ($(this).text() == item_date_text + "▼") { resetNgListItemText(); $(this).text(item_date_text + "▲"); sortNgList(2); } else { resetNgListItemText(); $(this).text(item_date_text + "▼"); sortNgList(2, -1); } }, }), $("<div>", { id: "GM_fcn_ng_list_item_scrl", class: "GM_fcn_ng_list_item", }) ), $("<div>", { id: "GM_fcn_ng_list_content" }) ), $("<div>").css("margin-top", "1em").append( $("<span>").css("margin", "0 1em").append( $("<input>", { class: "GM_fcn_ng_list_button", type: "button", val: "更新", click: function() { if (!CHECK_NG_LIST || isNgListNormal()) { GM_setValue("_futaba_catalog_NG_images", imageList); GM_setValue("_futaba_catalog_NG_comment", commentList); GM_setValue("_futaba_catalog_NG_date", dateList); GM_setValue("_futaba_catalog_NG_dHashes", dHashList); $(".GM_fcn_ng_list_row").css("background-color", "#ffffff"); $("#GM_fcn_md5").val(""); $("#GM_fcn_comment").val(""); $("#GM_fcn_ng_list_content").scrollTop(0); $("#GM_fcn_catalog_space").remove(); $("html, body").css("overflow", ""); selectIndex = -1; $ngListContainer.fadeOut(100); $(".GM_fcn_ng_images").css("display", ""); $(".GM_fcn_ng_images").removeClass("GM_fcn_ng_images"); $(".GM_fcn_ng_dhash_td").removeClass("GM_fcn_ng_dhash_td"); $(".GM_fcn_ng_dhash_img").removeClass("GM_fcn_ng_dhash_img"); hideNgThreads(); } else { alert("NGリストが壊れています\n一度キャンセルしてからやり直してください"); } }, }) ), $("<span>").css("margin", "0 1em").append( $("<input>", { class: "GM_fcn_ng_list_button", type: "button", val: "キャンセル", click: function(){ $(".GM_fcn_ng_list_row").css("background-color", "#ffffff"); $("#GM_fcn_md5").val(""); $("#GM_fcn_comment").val(""); $("#GM_fcn_ng_list_content").scrollTop(0); $("#GM_fcn_catalog_space").remove(); $("html, body").css("overflow", ""); selectIndex = -1; $ngListContainer.fadeOut(100); }, }) ) ) ) ); $(".GM_fcn_ng_list_button").css({ "cursor": "pointer", "background-color": "#FFECFD", "border": "2px outset #96ABFF", "border-radius": "5px", "font-size": "13.33px" }).hover(function() { $(this).css("background-color", "#CCE9FF"); }, function() { $(this).css("background-color", "#FFECFD"); }); $(document).keydown((e) => { if (e.key == "ArrowUp" && selectIndex > 0) { $(".GM_fcn_ng_list_comment").eq(selectIndex - 1).click(); e.preventDefault(); scrollToSelectedRow(); $("#GM_fcn_comment").blur(); } else if (e.key == "ArrowDown" && selectIndex > -1 && selectIndex < imageList.length) { $(".GM_fcn_ng_list_comment").eq(selectIndex + 1).click(); e.preventDefault(); scrollToSelectedRow(); $("#GM_fcn_comment").blur(); } else if (e.key == "F2" && selectIndex > -1) { $("#GM_fcn_comment").focus(); e.preventDefault(); } }); /** * NGリスト選択行コメント修正 */ function editSelectedRow() { if (selectIndex > -1) { commentList[selectIndex] = $("#GM_fcn_comment").val(); $(".GM_fcn_ng_list_comment").eq(selectIndex).text(commentList[selectIndex]); } } /** * NGリスト選択行削除 */ function deleteSelectedRow() { if (selectIndex > -1) { imageList.splice(selectIndex, 1); commentList.splice(selectIndex, 1); dateList.splice(selectIndex, 1); dHashList.splice(selectIndex, 1); } $(".GM_fcn_ng_list_row").css("background-color", "#ffffff"); $("#GM_fcn_md5").val(""); $("#GM_fcn_comment").val(""); selectIndex = -1; refreshNgList(); } /** * NGリスト行入替 * indexの行と一つ後の行を入れ替える。 * @param {number} index 入替する行番号(先頭行が0) */ function swapRow(index) { if (index <= -1 || index + 1 >= imageList.length) { return; } imageList.splice(index, 2, imageList[index + 1], imageList[index]); commentList.splice(index, 2, commentList[index + 1], commentList[index]); dateList.splice(index, 2, dateList[index + 1], dateList[index]); dHashList.splice(index, 2, dHashList[index + 1], dHashList[index]); selectIndex = selectIndex == index ? index + 1 : index; scrollToSelectedRow(); refreshNgList(); selectNgList(); resetNgListItemText(); } /** * 選択行までスクロール */ function scrollToSelectedRow() { var rowHeight = 22; // NGリストの1行当たりの高さ(px) var listLines = 13; // NGリストの表示行数 var selectPos = selectIndex * rowHeight; var scrollTop = $("#GM_fcn_ng_list_content").scrollTop(); var scrollBottom = scrollTop + (rowHeight * (listLines - 2)); if (selectPos < scrollTop) { $("#GM_fcn_ng_list_content").scrollTop(selectPos); } if (selectPos > scrollBottom) { $("#GM_fcn_ng_list_content").scrollTop(selectPos - (rowHeight * (listLines - 2))); } } /** * NGリストソート * @param {number} index ソート対象の項目(md5:0, コメント:1, 最終検出日:2, dHash:3) * @param {number} order ソート方向(昇順:1, 降順:-1)未指定は昇順 */ function sortNgList(index, order = 1) { var array = new Array(); for (var i = 0; i < imageList.length; ++i) { array[i] = new Array(); array[i][0] = imageList[i]; array[i][1] = commentList[i]; array[i][2] = dateList[i]; array[i][3] = dHashList[i]; } array.sort(function(a, b){ if (a[index] > b[index]) { return order; } if (a[index] < b[index]) { return -order; } return 0; }); for (var j = 0; j < imageList.length; ++j) { imageList[j] = array[j][0]; commentList[j] = array[j][1]; dateList[j] = array[j][2]; dHashList[j] = array[j][3]; } $(".GM_fcn_ng_list_row").css("background-color", "#ffffff"); $("#GM_fcn_md5").val(""); $("#GM_fcn_comment").val(""); selectIndex = -1; refreshNgList(); } } /** * 動的リロードの状態を取得 */ function checkAkahukuReload() { // 赤福 var target = $("#akahuku_catalog_reload_status").get(0); if (target) { checkAkahukuReloadStatus(target); } else { $(document).on("AkahukuContentApplied", () => { target = $("#akahuku_catalog_reload_status").get(0); if (target) { checkAkahukuReloadStatus(target); } else { console.error ("futaba_catalog_NG - #akahuku_catalog_reload_status not found"); } }); } function checkAkahukuReloadStatus(target) { var status = ""; var config = { childList: true }; var observer = new MutationObserver(function() { if (target.textContent == status) { return; } status = target.textContent; if (status == "完了しました" || status == "アンドゥしました" || status == "リドゥしました") { if (!isNgEnable) { $("#GM_fcn_ng_disable_button").css("background", "none"); isNgEnable = true; } makeNgButton(); hideNgThreads(); $("body").attr("__fcn_catalog_visibility", "visible"); $("#cattable > tbody").css("opacity", "1"); $("#GM_fth_highlighted_threads").css("visibility", "visible"); } else if (HIDE_CATALOG_BEFORE_LOAD && status !== "") { $("body").attr("__fcn_catalog_visibility", "hidden"); $("#cattable > tbody").css("opacity", "0"); $("#GM_fth_highlighted_threads").css("visibility", "hidden"); } }); observer.observe(target, config); } // KOSHIAN $(document).on("KOSHIAN_cat_reload", () => { if (HIDE_CATALOG_BEFORE_LOAD) { $("body").attr("__fcn_catalog_visibility", "hidden"); $("#cattable > tbody").css("opacity", "0"); $("#GM_fth_highlighted_threads").css("visibility", "hidden"); } if (!isNgEnable) { $("#GM_fcn_ng_disable_button").css("background", "none"); isNgEnable = true; } makeNgButton(); hideNgThreads(); $("body").attr("__fcn_catalog_visibility", "visible"); $("#cattable > tbody").css("opacity", "1"); $("#GM_fth_highlighted_threads").css("visibility", "visible"); }); // ふたクロ checkFutakuroReloadStatus(); function checkFutakuroReloadStatus() { var opacityZero = false; var target = $("#cattable").get(0); var config = { attributes: true , attributeFilter: ["style"] }; var observer = new MutationObserver(function(mutations) { if ($("#cat_search").length) { mutations.forEach(function(mutation) { if (mutation.target.attributes.style.nodeValue == "opacity: 0;") { opacityZero = true; if (HIDE_CATALOG_BEFORE_LOAD) { $("body").attr("__fcn_catalog_visibility", "hidden"); $("#cattable > tbody").css("opacity", "0"); $("#GM_fth_highlighted_threads").css("visibility", "hidden"); } } else if (opacityZero && mutation.target.attributes.style.nodeValue != "opacity: 0;") { opacityZero = false; if (!isNgEnable) { $("#GM_fcn_ng_disable_button").css("background", "none"); isNgEnable = true; } makeNgButton(); hideNgThreads(); $("body").attr("__fcn_catalog_visibility", "visible"); $("#cattable > tbody").css("opacity", "1"); $("#GM_fth_highlighted_threads").css("visibility", "visible"); } }); } }); observer.observe(target, config); } } /** * カタログのスレにNGボタン作成 */ function makeNgButton() { // カタログソートが「設定」なら作成しない if (location.search.match(/mode=catset/)) { return; } // NGボタン var $ngButton = $("<span>", { class: "GM_fcn_ng_button", text: "[NG]", css: { color: "blue", display: "none", }, }); // NGボタンメニュー var $ngButtonMenu = $("<div>", { class: "GM_fcn_ng_menu", css: { display: "none", } }); var $catTd = $("#cattable td"); $catTd.each(function() { var $oldNgButtons = $(this).children(".GM_fcn_ng_button"); if ($oldNgButtons.length) { $oldNgButtons.remove(); } var $cloneNgButton = $ngButton.clone(); var $cloneNgButtonMenu = $ngButtonMenu.clone(); $cloneNgButton.hover(function () { $(this).css("color", "red"); }, function () { $(this).css("color", "blue"); }); $cloneNgButton.on("click",function(){ makeNgButtonMenu($(this)); }); $(this).hover(function () { $cloneNgButton.css("display", "inline"); $cloneNgButton.siblings(".KOSHIAN_response_increase").css("display", "none"); $cloneNgButton.siblings(".fvw_num").css("display", "none"); }, function () { $cloneNgButton.css("display", "none"); $cloneNgButtonMenu.css("display", "none"); $cloneNgButton.siblings(".KOSHIAN_response_increase").css("display", "inline"); $cloneNgButton.siblings(".fvw_num").css("display", ""); }); $cloneNgButton.append($cloneNgButtonMenu); $(this).append($cloneNgButton); }); } /** * NGボタンメニュー作成 * @param {jQuery} $button メニューを作成するNGボタンのjQueryオブジェクト */ function makeNgButtonMenu($button) { if (!isNgEnable) { return; } var $menu = $button.children(".GM_fcn_ng_menu"); if (!$button.find(".GM_fcn_ng_menu_item").length) { // スレNG var $ngNumber = $("<div>", { class: "GM_fcn_ng_menu_item", text: "スレNG", css: { color: "blue", "background-color": "rgba(240, 224, 214, 0.95)", } }); // 本文NG var $ngWordCommon = $("<div>", { class: "GM_fcn_ng_menu_item", text: "本文NG(共通)", css: { color: "blue", "background-color": "rgba(240, 224, 214, 0.95)", } }); var $ngWordIndiv = $("<div>", { class: "GM_fcn_ng_menu_item", text: "本文NG(板別)", css: { color: "blue", "background-color": "rgba(240, 224, 214, 0.95)", } }); // 画像NG var $ngImage = $("<div>", { class: "GM_fcn_ng_menu_item", text: "画像NG", css: { color: "blue", "background-color": "rgba(240, 224, 214, 0.95)", } }); var $td = $button.parent(); var threadNumber = $td.find("a:first").length ? $td.find("a:first").attr("href").slice(4,-4) : ""; var threadImgObj = $td.find("img:first").length ? $td.find("img:first")[0] : ""; var threadComment = $td.find("small:first, .GM_fth_pickuped_caption, .GM_fth_opened_caption").length ? $td.find("small:first, .GM_fth_pickuped_caption, .GM_fth_opened_caption").text() : ""; var $cloneNgNumber = $ngNumber.clone(); var $cloneNgWordCommon = $ngWordCommon.clone(); var $cloneNgWordIndiv = $ngWordIndiv.clone(); var $cloneNgImage = $ngImage.clone(); $cloneNgNumber.hover(function () { $(this).css("color", "red"); $(this).css("background-color", "rgba(204, 233, 255, 0.95)"); }, function () { $(this).css("color", "blue"); $(this).css("background-color", "rgba(240, 224, 214, 0.95)"); }); $cloneNgNumber.click(function () { addNgNumber(threadNumber); $td.addClass("GM_fcn_ng_numbers"); $td.css("display","none"); if ($td.hasClass("GM_fth_pickuped") || $td.hasClass("GM_fth_opened")) { hideNgThreads(); } }); $cloneNgWordCommon.hover(function () { $(this).css("color", "red"); $(this).css("background-color", "rgba(204, 233, 255, 0.95)"); }, function () { $(this).css("color", "blue"); $(this).css("background-color", "rgba(240, 224, 214, 0.95)"); }); $cloneNgWordCommon.click(function () { var words = GM_getValue("_futaba_catalog_NG_words", ""); words = addNgWord(words, threadComment); GM_setValue("_futaba_catalog_NG_words", words); $td.addClass("GM_fcn_ng_words"); $td.css("display","none"); if ($td.hasClass("GM_fth_pickuped") || $td.hasClass("GM_fth_opened")) { hideNgThreads(); } }); $cloneNgWordIndiv.hover(function () { $(this).css("color", "red"); $(this).css("background-color", "rgba(204, 233, 255, 0.95)"); }, function () { $(this).css("color", "blue"); $(this).css("background-color", "rgba(240, 224, 214, 0.95)"); }); $cloneNgWordIndiv.click(function () { var words = getCurrentIndivValue("NG_words_indiv", ""); words = addNgWord(words, threadComment); setIndivValue("NG_words_indiv", words); $td.addClass("GM_fcn_ng_words"); $td.css("display","none"); if ($td.hasClass("GM_fth_pickuped") || $td.hasClass("GM_fth_opened")) { hideNgThreads(); } }); $cloneNgImage.hover(function () { $(this).css("color", "red"); $(this).css("background-color", "rgba(204, 233, 255, 0.95)"); }, function () { $(this).css("color", "blue"); $(this).css("background-color", "rgba(240, 224, 214, 0.95)"); }); $cloneNgImage.click(function () { hideNgImageThread(threadImgObj, threadComment, $td); if ($td.hasClass("GM_fth_pickuped") || $td.hasClass("GM_fth_opened") || USE_DHASH) { hideNgThreads(); } }); if (threadNumber) { $menu.append($cloneNgNumber); } if (threadComment) { $menu.append($cloneNgWordCommon); $menu.append($cloneNgWordIndiv); } if (threadImgObj && USE_NG_IMAGES) { $menu.append($cloneNgImage); } } var menuLeft = 0; var menuTop = $button.height(); $menu.css("left", `${menuLeft}px`); $menu.css("top", `${menuTop}px`); $menu.css("display", "block"); /** * NGワード追加 * @param {string} ngWords 追加前のNGワード * @param {string} newNgWord 追加するNGワード * @return {string} 追加後のNGワード */ function addNgWord(ngWords, newNgWord) { newNgWord = newNgWord.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&"); if (newNgWord && ngWords) { ngWords = newNgWord + "|" + ngWords; } else { ngWords += newNgWord; } return ngWords; } /** * スレ画像NG * @param {HTMLImageElement} imgObj NGにするスレ画像のimg要素 * @param {string} comment NG画像のコメント * @param {jQuery} $td NGにするスレ画像の親td要素のjQueryオブジェクト */ function hideNgImageThread(imgObj, comment, $td) { var data = convertDataURI(imgObj); //console.log("futaba_catalog_NG - data: " + data); if (!data) { alert("スレ画像の取得に失敗しました"); return; } var hexHash = md5(data); var dHash = convertDHash(imgObj, true); //console.log("futaba_catalog_NG - hexHash: " + hexHash); //console.log("futaba_catalog_NG - dHash: " + dHash.toString(16)); addNgListObj("_futaba_catalog_NG_images", hexHash); addNgListObj("_futaba_catalog_NG_comment", comment); addNgListObj("_futaba_catalog_NG_date", getDate()); addNgListObj("_futaba_catalog_NG_dHashes", dHash); if (isNgEnable) { $td.addClass("GM_fcn_ng_images"); $td.css("display","none"); } // 非NG画像リストからNG画像を削除 var okImages = getCurrentIndivValue("OK_images_indiv", []); var imgNumber = parseInt($td.find("img").attr("src").match(/(\d+)s\.jpg$/)[1], 10); if (USE_DHASH) { // 近似画像NG使用時は非NG画像リストを全削除 GM_setValue("OK_images_indiv", "{}"); } else { var index = okImages.indexOf(imgNumber); if (index > -1) { okImages.splice(index, 1); setIndivValue("OK_images_indiv", okImages); } } /** * NGリストにNGデータを追加 * @param {string} target NGデータを追加するNGリスト名 * @param {string} val NGリストへ追加するNGデータ */ function addNgListObj(target, val) { var ngListObj = GM_getValue(target, ""); if (ngListObj === ""){ ngListObj = []; } ngListObj.unshift(val); if (ngListObj.length > MAX_REGISTERED_NG_IMAGES) { ngListObj.splice(MAX_REGISTERED_NG_IMAGES); } GM_setValue(target, ngListObj); } } } /** * NG番号追加 * @param {string} number 追加するNG番号 */ function addNgNumber(number) { var ngNumberObj = getIndivObj("NG_numbers_indiv"); if (ngNumberObj === ""){ ngNumberObj = {}; } if (!ngNumberObj[serverFullPath]) { ngNumberObj[serverFullPath] = []; } if (ngNumberObj[serverFullPath].indexOf(number) > -1) { return; } ngNumberObj[serverFullPath].push(number); var deleteCount = ngNumberObj[serverFullPath].length - MAX_NG_THREADS; if (deleteCount > 0) { ngNumberObj[serverFullPath].splice(0, deleteCount); } var jsonString = JSON.stringify(ngNumberObj); GM_setValue("NG_numbers_indiv", jsonString); } /** * dataURI変換 * @param {HTMLImageElement} imgObj dataURIに変換する画像のimg要素 * @param {number} width 変換する画像の幅 未指定時は画像の本来の幅 * @param {number} height 変換する画像の高さ 未指定時は画像の本来の高さ * @return {string} 変換したdataURI文字列 */ function convertDataURI(imgObj, width = imgObj.naturalWidth, height = imgObj.naturalHeight){ if (!imgObj || !imgObj.complete || !width || !height) { return; } // canvasを生成してimg要素を反映 var cvs = document.createElement("canvas"); cvs.width = width; cvs.height = height; var ctx = cvs.getContext("2d", { alpha: false // 背景不透明で高速化 }); try { ctx.drawImage(imgObj, 0, 0); } catch (e) { console.error("futaba_catalog_NG - drawImage error: src= " + imgObj.src + ", " + e.name + ": " + e.message); console.dir(e); return; } // canvasをdataURI化 var data; try { data = cvs.toDataURL("image/jpeg"); } catch (e) { console.error("futaba_catalog_NG - dataURI convert error: src= " + imgObj.src + ", " + e.name + ": " + e.message); console.dir(e); return; } if (data.substr(0,23) !== "data:image/jpeg;base64,") { console.error("futaba_catalog_NG - dataURI abnormal: src= " + imgObj.src + ", dataURI= " + data); return; } return data; } /** * dHash変換 * @param {HTMLImageElement} imgObj dHashに変換する画像のimg要素 * @param {boolean} force 近似画像NGが無効でも強制的にdHash変換する * @return {number} 変換したdHash 0 ~ 2^49 - 1 * 変換できないときはnullを返す * オリジナルは64bitのHash値だがJavaScriptの最大整数値2^53に収める為49bitのHash値を返す */ function convertDHash(imgObj, force = false) { if ((!USE_DHASH && !force) || !imgObj || !imgObj.complete || !imgObj.naturalWidth || !imgObj.naturalHeight) { return null; } // 8x7のcanvasを生成してimg要素を反映 var cvs = document.createElement("canvas"); cvs.width = 8; cvs.height = 7; var ctx = cvs.getContext("2d", { alpha: false // 背景不透明で高速化 }); try { ctx.drawImage(imgObj, 0, 0, imgObj.naturalWidth, imgObj.naturalHeight, 0, 0, cvs.width, cvs.height); } catch (e) { console.error("futaba_catalog_NG - drawImage error: src= " + imgObj.src + ", " + e.name + ": " + e.message); console.dir(e); return null; } var pixels = ctx.getImageData(0, 0, cvs.width, cvs.height); var grayScale = []; for (var y = 0; y < pixels.height; ++y) { for (var x = 0; x < pixels.width; ++x) { var i = (y * 4) * pixels.width + x * 4; var rgb = pixels.data[i] + pixels.data[i + 1] + pixels.data[i + 2]; // 画素をグレイスケール化 grayScale.push(rgb); } } var dHash = 0; var exponent = 0; // べき指数 grayScale.forEach((v, i) => { if ((i + 1) % cvs.width !== 0) { // 右端のピクセルは右隣が無いのでスキップ // 右隣の輝度と比較した結果を1bitとしてセット dHash += (v < grayScale[i + 1] ? 1 : 0) * 2 ** exponent; ++exponent; } }); //console.log("futaba_catalog_NG - imgObj.src: " + imgObj.src + ", dHash: 0x" + dHash.toString(16).toUpperCase()); return dHash; } /** * 日付取得 * @return {string} 現在の日付の文字列 yy/mm/dd */ function getDate() { var now = new Date(); var date = ("" + (now.getFullYear())).slice(-2) + "/" + ("0" + (now.getMonth() + 1)).slice(-2) + "/" + ("0" + now.getDate()).slice(-2); return date; } /** * カタログを検索してNGスレを非表示 * @param {boolean} isWordsChanged NGワードを変更したか */ function hideNgThreads(isWordsChanged) { if (!isNgEnable) { return; } var Start = new Date().getTime();//count parsing time var words = ""; var wordsCommon = GM_getValue("_futaba_catalog_NG_words", ""); var wordsIndiv = getCurrentIndivValue("NG_words_indiv", ""); var numbers = getCurrentIndivValue("NG_numbers_indiv", []); images = GM_getValue("_futaba_catalog_NG_images", []); ngDate = GM_getValue("_futaba_catalog_NG_date", []); okImages = getCurrentIndivValue("OK_images_indiv", []); dHashes = GM_getValue("_futaba_catalog_NG_dHashes", []); // NGワード if( wordsCommon !== "" ) { words += wordsCommon; if( wordsIndiv !== "" ) { words += "|" + wordsIndiv; } } else { words += wordsIndiv; } //console.log(words); //console.dir(numbers); //console.dir("futaba_catalog_NG - images.length: " + images.length); try { var re = new RegExp(words, "i"); } catch (e) { alert("NGワードのパターンが無効です\n\n" + e); editNgWords(); return; } if (isWordsChanged) { $(".GM_fcn_ng_words").css("display", ""); $(".GM_fcn_ng_words").removeClass("GM_fcn_ng_words"); } if (words !== "") { $("#cattable td small").each(function() { if (re.test($(this).text())) { if ($(this).parent("a").length) { //文字スレ $(this).parent().parent("td").addClass("GM_fcn_ng_words"); $(this).parent().parent("td").css("display", "none"); } else { $(this).parent("td").addClass("GM_fcn_ng_words"); $(this).parent("td").css("display", "none"); } //console.log("futaba catalog NG - caption: " + $(this).text() + " NG word: " + $(this).text().match(re)[0]); } }); } if (isWordsChanged) { console.log("futaba_catalog_NG - Parsing@" + serverFullPath + ": "+((new Date()).getTime()-Start) +"msec"); // eslint-disable-line no-console return; } // NG番号 if (numbers.length) { var $catAnchors = $("#cattable td[class!='GM_fcn_ng_words'] > a:first-of-type"); $catAnchors.each(function() { var hrefNum = $(this).attr("href").slice(4,-4); if (numbers.indexOf(hrefNum) > -1){ $(this).closest("td, .cs").addClass("GM_fcn_ng_numbers"); $(this).closest("td, .cs").css("display", "none"); } }); } // NG画像 if (USE_NG_IMAGES) { if (images.length) { var $catImages = $("#cattable td:not([class^='GM_fcn_ng_']) > a:first-of-type > img"); $catImages.each(function() { var $td = $(this).closest("td"); var imgSrc = this.src.match(/(\d+)s\.jpg$/); if (imgSrc) { var imgNumber = parseInt(imgSrc[1], 10); if (okImages.indexOf(imgNumber) == -1) { var data = convertDataURI(this); if (data) { var hexHash = md5(data); var index = images.indexOf(hexHash); if (index > -1){ $td.addClass("GM_fcn_ng_images"); $td.css("display", "none"); ngDate[index] = getDate(); if (dHashes[index] == null) { dHashes[index] = convertDHash(this, true); } } else { var isError = false; // md5変換エラーフラグ if (hexHash.length !== 32) { isError = true; console.error("futaba_catalog_NG - hexHash abnormal: image No." + imgNumber + ", hexHash: " + hexHash); // eslint-disable-line no-console } // dHash判定 var distance, dHash = convertDHash(this); [index, distance] = findIndexOfDHashes(dHash); if (index > -1) { if (ENABLE_DHASH_TEST) { $(this).addClass("GM_fcn_ng_dhash_img"); $td.children(".GM_fcn_ng_button").attr("title", "distance: " + distance + ", dHash: " + ("000000000000" + dHash.toString(16)).slice(-13)); $td.addClass("GM_fcn_ng_dhash_td"); } else { $td.addClass("GM_fcn_ng_images"); $td.css("display", "none"); } ngDate[index] = getDate(); } else if (!isError) { okImages.unshift(imgNumber); } } } else { // スレ画像読込完了確認 this.onload = () => { this.onload = null; var imgNumber = parseInt(this.src.match(/(\d+)s\.jpg$/)[1], 10); var data = convertDataURI(this); if (data) { var hexHash = md5(data); var index = images.indexOf(hexHash); if (index > -1){ $td.addClass("GM_fcn_ng_images"); $td.css("display", "none"); ngDate[index] = getDate(); GM_setValue("_futaba_catalog_NG_date", ngDate); if (dHashes[index] == null) { dHashes[index] = convertDHash(this, true); GM_setValue("_futaba_catalog_NG_dHashes", dHashes); } } else { var isError = false; // md5変換エラーフラグ if (hexHash.length !== 32) { isError = true; console.error("futaba_catalog_NG - hexHash abnormal: image No." + imgNumber + ", hexHash: " + hexHash); } // dHash判定 var distance, dHash = convertDHash(this); [index, distance] = findIndexOfDHashes(dHash); if (index > -1) { if (ENABLE_DHASH_TEST) { $(this).addClass("GM_fcn_ng_dhash_img"); $td.children(".GM_fcn_ng_button").attr("title", "distance: " + distance + ", dHash: " + ("000000000000" + dHash.toString(16)).slice(-13)); $td.addClass("GM_fcn_ng_dhash_td"); } else { $td.addClass("GM_fcn_ng_images"); $td.css("display", "none"); } ngDate[index] = getDate(); GM_setValue("_futaba_catalog_NG_date", ngDate); } else if (!isError) { okImages.unshift(imgNumber); } } } else { console.error("futaba_catalog_NG - image data abnormal: image No." + imgNumber); } }; if (this.complete && this.width && this.height && this.onload) { // onloadセット中に画像読込完了していたらloadをトリガーする $(this).trigger("load"); } } } } }); GM_setValue("_futaba_catalog_NG_date", ngDate); GM_setValue("_futaba_catalog_NG_dHashes", dHashes); if (okImages.length > MAX_OK_IMAGES) { okImages.splice(MAX_OK_IMAGES); } setIndivValue("OK_images_indiv", okImages); } else { var $catImg = $("#cattable td a img"); $catImg.each(function() { var imgSrc = this.src.match(/(\d+)s\.jpg$/); if (imgSrc) { var imgNumber = parseInt(imgSrc[1], 10); if (okImages.indexOf(imgNumber) == -1) { okImages.unshift(imgNumber); } } }); if (okImages.length > MAX_OK_IMAGES) { okImages.splice(MAX_OK_IMAGES); } setIndivValue("OK_images_indiv", okImages); } } //console.log("futaba_catalog_NG - okImages.length: " + okImages.length); console.log("futaba_catalog_NG - Parsing@" + serverFullPath + ": " + ((new Date()).getTime() - Start) + "msec"); // eslint-disable-line no-console /** * dHashリストのインデックス探索 * @param {number} dHash 近似画像NG判定する画像のdHash値 * @return {Array.<number>} [i, distance] * {number} i 近似度が閾値以下のdHashリスト(dHashes)配列内のインデックス * {number} distance ハミング距離 * dHashリストに該当が無ければi, distance共に-1を返す */ function findIndexOfDHashes(dHash) { if (dHash != null) { for (var i = 0, num = dHashes.length; i < num; ++i) { if (dHashes[i]) { var distance = getHammingDistance(dHash, dHashes[i]); if (distance <= DISTANCE_THRESHOLD) { if (ENABLE_DHASH_TEST) { console.debug("futaba_catalog_NG - catalog dHash: " + ("000000000000" + dHash.toString(16)).slice(-13) + ", NG list dHash: " + ("000000000000" + dHashes[i].toString(16)).slice(-13)); console.debug("futaba_catalog_NG - hamming distance: " + distance); } return [i, distance]; } } } } return [-1, -1]; } /** * ハミング距離取得 * @param {number} hash1 測定するHash(49bit) * @param {number} hash2 測定するHash(49bit) * @return {number} ハミング距離(0~49) */ function getHammingDistance(hash1, hash2) { // 2つのHashを上位17bitと下位32bitに分割して32ビット演算する var hash1L = hash1 >>> 0; var hash1H = (hash1 - hash1L) / 0x100000000; var hash2L = hash2 >>> 0; var hash2H = (hash2 - hash2L) / 0x100000000; // 下位32bitのビットの異なる位置を抽出 var xorL = hash1L ^ hash2L; var count = 0; // 立っている最下位ビットを消してカウント while (xorL) { xorL &= xorL - 1; ++count; } // 上位17bitのビットの異なる位置を抽出 var xorH = hash1H ^ hash2H; // 立っている最下位ビットを消してカウント while (xorH) { xorH &= xorH - 1; ++count; } return count; } } /** * KOSHIAN del イベント監視 */ function listenKoshianDelEvent() { $(document).on("KOSHIAN_del", () => { // delされたスレをNG登録して非表示 $(".KOSHIAN_del").each(function() { var threadNumber = $(this).find("a:first").length ? $(this).find("a:first").attr("href").slice(4,-4) : ""; if (threadNumber) { addNgNumber(threadNumber); } if (isNgEnable) { $(this).addClass("GM_fcn_ng_numbers"); $(this).css("display", "none"); } $(this).removeClass("KOSHIAN_del"); }); hideNgThreads(); }); } /** * futaba thread highlighter K ピックアップイベント監視 */ function listenFthPickupEvent() { $(document).on("FutabaTH_pickup", () => { // ピックアップされたスレにNGボタンをセット $(".GM_fth_pickuped, .GM_fth_opened").each(function() { var $ngButton = $(this).find(".GM_fcn_ng_button"); if ($ngButton.length) { var $ngButtonMenu = $ngButton.children(".GM_fcn_ng_menu"); $ngButton.hover(function() { $(this).css("color", "red"); }, function () { $(this).css("color", "blue"); }); $ngButton.on("click",function() { makeNgButtonMenu($ngButton); }); $(this).hover(function () { $ngButton.css("display", "inline"); $ngButton.siblings(".KOSHIAN_response_increase").css("display", "none"); $ngButton.siblings(".fvw_num").css("display", "none"); }, function () { $ngButton.css("display", "none"); $ngButtonMenu.css("display", "none"); $ngButton.siblings(".KOSHIAN_response_increase").css("display", "inline"); $ngButton.siblings(".fvw_num").css("display", ""); }); } }); }); } /** * カタログ非表示 */ function hideCatalog() { setCatalogHiddenStyle(); $(function() { $("body").attr("__fcn_catalog_visibility", "hidden"); $("#GM_fth_highlighted_threads").css("visibility", "hidden"); init(); }); $(window).on("load", function() { $("body").attr("__fcn_catalog_visibility", "visible"); setCatalogShownStyle(); $("#GM_fth_highlighted_threads").css("visibility", "visible"); }); } /** * カタログ非表示スタイル設定 */ function setCatalogHiddenStyle() { var css = "#cattable {" + " opacity: 0;" + "}"; GM_addStyle(css); } /** * カタログ表示スタイル設定 */ function setCatalogShownStyle() { var css = "#cattable {" + " opacity: 1;" + "}"; GM_addStyle(css); } /** * スタイル設定 */ function setStyle() { var css = // NGワード ".GM_fcn_ng_words {" + " display: none;" + "}" + // NG番号 ".GM_fcn_ng_numbers {" + " display: none;" + "}" + // NG画像 ".GM_fcn_ng_images {" + " display: none;" + "}" + // 近似画像NGスレ ".GM_fcn_ng_dhash_td {" + " background-color: #e1b2ec;" + " opacity: 0.2;" + "}" + ".GM_fcn_ng_dhash_td:hover {" + " opacity: 1;" + "}" + // 近似NG画像 ".GM_fcn_ng_dhash_img {" + " opacity: 0.2;" + "}" + ".GM_fcn_ng_dhash_img:hover {" + " opacity: 1;" + "}" + // NGボタン ".GM_fcn_ng_button {" + " position: relative;" + " font-size: 12px;" + " cursor: pointer;" + "}" + // NGメニュー ".GM_fcn_ng_menu {" + " font-size: medium;" + " background-color: rgba(240, 192, 214, 0.95);" + " z-index: 203;" + " position: absolute;" + " min-width: 140px;" + " width: auto;" + " border: 1px outset;" + " border-radius: 5px;" + " padding: 5px;" + "}" + // NGメニュー項目 ".GM_fcn_ng_menu_item {" + " padding: 5px;" + " z-index: 203;" + " cursor: pointer;" + "}" + // NGリストラベル ".GM_fcn_ng_list_label {" + " display: inline-block;" + " width: 100px;" + "}" + // NGリスト入力 ".GM_fcn_ng_list_input {" + " width: 360px;" + " margin-right: 16px;" + " font-size: 16px;" + "}" + // NGリスト編集ボタン ".GM_fcn_ng_list_edit_button {" + " width: 70px;" + " margin-right: 16px;" + "}" + // NGリスト移動ボタン ".GM_fcn_ng_list_move_button {" + " margin-right: 16px;" + "}" + // NGリスト枠 "#GM_fcn_ng_list_pane {" + " width: 928px;" + " height: 308px;" + " margin-left: 11px;" + " border-width: 1px;" + " border-style: solid;" + " background-color: #eee;" + "}" + // NGリスト項目行 "#GM_fcn_ng_list_item_row {" + " display: inline-block;" + " height: 22px;" + " overflow: hidden;" + " white-space: nowrap;" + "}" + // NGリスト項目md5 "#GM_fcn_ng_list_item_md5 {" + " width: 360px;" + "}" + // NGリスト項目dHash "#GM_fcn_ng_list_item_dHash {" + " width: 170px;" + "}" + // NGリスト項目コメント "#GM_fcn_ng_list_item_comment {" + " width: 250px;" + "}" + // NGリスト項目最終検出日 "#GM_fcn_ng_list_item_date {" + " width: 130px;" + "}" + // NGリスト項目スクロールバースペース "#GM_fcn_ng_list_item_scrl {" + " width: 18px;" + "}" + // NGリスト項目 ".GM_fcn_ng_list_item {" + " display: inline-block;" + " height: 22px;" + " border-width: 1px;" + " border-style: solid;" + " box-sizing: border-box;" + " overflow: hidden;" + " white-space: nowrap;" + " text-overflow: clip;" + " cursor: pointer;" + "}" + // NGリストコンテンツ "#GM_fcn_ng_list_content {" + " width: 928px;" + " height: 286px;" + " overflow-x: hidden;" + " overflow-y: auto;" + "}" + // NGリスト行 ".GM_fcn_ng_list_row {" + " width: 928px;" + " height: 22px;" + " cursor: pointer;" + " overflow: hidden;" + " white-space: nowrap;" + "}" + // NGリスト画像 ".GM_fcn_ng_list_image {" + " display: inline-block;" + " width: 360px;" + " height: 22px;" + " border-width: 1px;" + " border-style: solid;" + " box-sizing: border-box;" + " overflow: hidden;" + " white-space: nowrap;" + " text-overflow: ellipsis;" + " font-family: Consolas, 'Courier New', monospace;" + "}" + // NGリストdHash ".GM_fcn_ng_list_dHash {" + " display: inline-block;" + " width: 170px;" + " height: 22px;" + " border-width: 1px;" + " border-style: solid;" + " box-sizing: border-box;" + " overflow: hidden;" + " white-space: nowrap;" + " text-overflow: ellipsis;" + " font-family: Consolas, 'Courier New', monospace;" + "}" + // NGリストコメント ".GM_fcn_ng_list_comment {" + " display: inline-block;" + " width: 250px;" + " height: 22px;" + " padding-left: 10px;" + " border-width: 1px;" + " border-style: solid;" + " box-sizing: border-box;" + " text-align: left;" + " overflow: hidden;" + " white-space: nowrap;" + " text-overflow: ellipsis;" + "}" + // NGリスト日時 ".GM_fcn_ng_list_date {" + " display: inline-block;" + " width: 130px;" + " height: 22px;" + " border-width: 1px;" + " border-style: solid;" + " box-sizing: border-box;" + " overflow: hidden;" + " white-space: nowrap;" + " text-overflow: ellipsis;" + " font-family: Consolas, 'Courier New', monospace;" + "}" + // NGリストスクロールバー ".GM_fcn_ng_list_scrl {" + " display: inline-block;" + " width: 18px;" + " height: 22px;" + " border-width: 0px 1px;" + " border-style: solid;" + " box-sizing: border-box;" + " overflow: hidden;" + " white-space: nowrap;" + "}" + // カタログ下スペース "#GM_fcn_catalog_space {" + " min-height: 2000px;" + "}" + // futaba thread highlighter ピックアップ ".GM_fth_pickuped {" + " overflow: visible !important;" + "}" + // futaba thread highlighter 既読 ".GM_fth_opened {" + " overflow: visible !important;" + "}" + // ふたクロNGボタン ".fvw_ng {" + " display: none !important;" + "}" + // スレのプルダウンメニューボタン用スペース "#cattable > tbody > tr > td {" + " padding-bottom: 12px !important;" + "}"; GM_addStyle(css); } })(jQuery);
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址