- // ==UserScript==
- // @name futaba_thread_highlighter
- // @namespace https://github.com/himuro-majika
- // @description スレ本文を検索してカタログでスレッド監視しちゃう
- // @include http://*.2chan.net/*/futaba.php?mode=cat*
- // @include https://*.2chan.net/*/futaba.php?mode=cat*
- // @version 1.6.6
- // @require http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js
- // @grant GM_registerMenuCommand
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant GM_addStyle
- // @license MIT
- // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAAPUExURYv4i2PQYy2aLUe0R////zorx9oAAAAFdFJOU/////8A+7YOUwAAAElJREFUeNqUj1EOwDAIQoHn/c88bX+2fq0kRsAoUXVAfwzCttWsDWzw0kNVWd2tZ5K9gqmMZB8libt4pSg6YlO3RnTzyxePAAMAzqMDgTX8hYYAAAAASUVORK5CYII=
- // ==/UserScript==
-
- this.$ = this.jQuery = jQuery.noConflict(true);
-
- (function ($) {
- var serverName = document.domain.match(/^[^.]+/);
- var pathName = location.pathname.match(/[^/]+/);
- var serverFullPath = serverName + "_" + pathName;
- var akahukuloadstat;
-
- init();
-
- function init(){
- console.log("futaba_thread_highlighter commmon: " +
- GM_getValue("_futaba_thread_search_words", ""));
- console.log("futaba_thread_highlighter indivisual: " +
- getCurrentIndivValue());
- GM_registerMenuCommand("スレッド検索ワード編集", editWords);
- setStyle();
- makecontainer();
- makeConfigUI();
- highlight();
- check_akahuku_reload();
- }
-
- /*
- *設定画面表示
- */
- function editWords(){
- var word_commmon = GM_getValue("_futaba_thread_search_words", "");
- var word_indiv = getCurrentIndivValue();
- $("#GM_fth_searchword_common").val(word_commmon);
- $("#GM_fth_searchword_individual").val(word_indiv);
- var $config_container_ = $("#GM_fth_config_container");
- $config_container_.fadeIn(100);
- setRandomExample();
- }
-
- /*
- * 表示中の板の個別検索ワードの取得
- */
- function getCurrentIndivValue() {
- var indivobj = getIndivObj();
- var str_CurrentIndiv;
- if(indivobj !== "") {
- str_CurrentIndiv = indivobj[serverFullPath];
- }
- else {
- str_CurrentIndiv = "";
- }
- return str_CurrentIndiv;
- }
-
- /*
- * 板毎の個別検索ワードのオブジェクトを取得
- */
- function getIndivObj() {
- var indivVal = GM_getValue("search_words_indiv", "");
- var obj_indiv;
- if(indivVal !== "") {
- obj_indiv = JSON.parse(indivVal);
- }
- else {
- obj_indiv = "";
- }
- return obj_indiv;
- }
-
- /*
- * 検索ワードを設定
- */
- function setSearchWords() {
- var input_common = $("#GM_fth_searchword_common").val();
- var input_indiv = $("#GM_fth_searchword_individual").val();
- GM_setValue("_futaba_thread_search_words", input_common);
- console.log("futaba_thread_highlighter: common searchword updated - " + input_common);
- setIndivValue(input_indiv);
- $("#GM_fth_config_container").fadeOut(100);
- highlight(true);
- /*
- * 板毎の個別検索ワードを保存
- */
- function setIndivValue(val) {
- var obj_indiv = getIndivObj();
- if(obj_indiv === ""){
- obj_indiv = {};
- }
- obj_indiv[serverFullPath] = val;
- var jsonstring = JSON.stringify(obj_indiv);
- GM_setValue("search_words_indiv", jsonstring);
- console.log("futaba_thread_highlighter: indivisual searchword updated@" + serverFullPath + " - " + val);
- }
- }
-
- /*
- *スレピックアップ表示エリアの設定
- */
- function makecontainer() {
- var $pickup_thread_area = $("<div>", {
- id: "GM_fth_container"
- });
- $("body > table[border]").before($pickup_thread_area);
-
- var $container_header = $("<div>", {
- id: "GM_fth_container_header",
- text: "スレッド検索該当スレッド",
- css: {
- "background-color": "#F0E0D6",
- fontWeight: "bolder"
- }
- });
- $pickup_thread_area.append($container_header);
- //設定ボタン
- var $button = $("<span>", {
- id: "GM_fth_searchword",
- text: "[設定]",
- css: {
- cursor: "pointer",
- },
- click: function() {
- editWords();
- }
- });
- $button.hover(function () {
- $(this).css({ backgroundColor:"#EEAA88" });
- }, function () {
- $(this).css({ backgroundColor:"#F0E0D6" });
- });
- $container_header.append($button);
-
- var $pickup_thread_container = $("<div>", {
- id: "GM_fth_highlighted_threads",
- css: {
- "display": "flex",
- "flex-wrap": "wrap",
- }
- });
- $pickup_thread_area.append($pickup_thread_container);
- }
-
- /*
- * 設定画面
- */
- function makeConfigUI() {
- var $config_container = $("<div>", {
- id: "GM_fth_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",
- fontWeight: "normal",
- "box-shadow": "3px 3px 5px #853e52",
- "border": "1px outset",
- "border-radius": "10px",
- "padding": "5px",
- }
- });
- $("#GM_fth_container_header").append($config_container);
- $config_container.append(
- $("<div>").append(
- $("<div>").text("スレ本文に含まれる語句を入力してください。 | を挟むと複数指定できます。正規表現使用可。"),
- $("<div>").text("例 : ").append(
- $("<span>").attr("id", "GM_fth_example").css({
- "background-color": "#ffeeee",
- "padding": "2px",
- "font-weight": "bold"
- })
- )
- ),
- $("<div>").css("margin-top", "1em").append(
- $("<div>").append(
- $("<label>").text("全板共通").attr("for", "GM_fth_searchword_common"),
- $("<input>").attr({
- "id": "GM_fth_searchword_common",
- "class": "GM_fth_input"
- }).css("width", "54em"),
- $("<span>").append(
- $("<input>", {
- class: "GM_fth_config_button",
- type: "button",
- val: "区切り文字挿入",
- click: function(){
- insertDelimiter("GM_fth_searchword_common");
- },
- })
- )
- ),
- $("<div>").append(
- $("<label>").text("各板個別").attr("for", "GM_fth_searchword_individual"),
- $("<input>").attr({
- "id": "GM_fth_searchword_individual",
- "class": "GM_fth_input"
- }).css("width", "54em"),
- $("<span>").append(
- $("<input>", {
- class: "GM_fth_config_button",
- type: "button",
- val: "区切り文字挿入",
- click: function(){
- insertDelimiter("GM_fth_searchword_individual");
- },
- })
- )
- )
- ),
- $("<div>").css({
- "margin-top": "1em",
- }).append(
- $("<span>").css("margin", "0 1em").append(
- $("<input>", {
- class: "GM_fth_config_button",
- type: "button",
- val: "更新",
- click: function(){
- setSearchWords();
- },
- })
- ),
- $("<span>").css("margin", "0 1em").append(
- $("<input>", {
- class: "GM_fth_config_button",
- type: "button",
- val: "キャンセル",
- click: function(){
- $config_container.fadeOut(100);
- },
- })
- )
- )
- );
- $(".GM_fth_config_button").css({
- "cursor": "pointer",
- "background-color": "#FFECFD",
- "border": "2px outset #96ABFF",
- "border-radius": "5px",
- }).hover(function() {
- $(this).css("background-color", "#CCE9FF");
- }, function() {
- $(this).css("background-color", "#FFECFD");
- });
- setRandomExample();
-
- /*
- * カーソル位置にデリミタ挿入
- */
- 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);
- }
- }
-
- function setRandomExample() {
- var exampleWords = [
- "妹がレイ",
- "悪魔がおる",
- "みなもちゃんかわいい",
- "つまんね",
- "マジか",
- "落ち着け",
- "アオいいよね",
- "いい…",
- "フラワハハ",
- "(i)orz",
- "うま(み|あじ)",
- "[0-9]時から!",
- "mjpk\\!\\?",
- "よしとみくんは何が好き?",
- "焼肉!",
- "そろそろ",
- "(はじ)?まるよ?",
- "ワグナス!!",
- "オマンコパンティー",
- "ワーオ!"
- ];
- var rand, randwords = [];
- for(var i = 0, l = exampleWords.length; i < 3; i++, l--) {
- rand = Math.floor(Math.random() * l);
- randwords.push(exampleWords.splice(rand, 1)[0]);
- }
- var example = randwords.join("|");
- $("#GM_fth_example").text(example);
- }
-
- /*
- *赤福の動的リロードの状態を取得
- */
- function check_akahuku_reload() {
- var target = $("html > body").get(0);
- if ($("#cat_search").length) {
- // ふたクロ
- highlight();
- target = $("html > body > table[border]").get(0);
- }
- var observer = new MutationObserver(function(mutations) {
- mutations.forEach(function(mutation) {
- var nodes = $(mutation.addedNodes);
- if ($("#cat_search").length) {
- // ふたクロ
- if (nodes.length) {
- highlight();
- }
- }
- else if (nodes.attr("border") == "1") {
- var timer = setInterval(function() {
- var status = $("#akahuku_catalog_reload_status").text();
- if(status === "" || status == "完了しました") {
- clearInterval(timer);
- highlight();
- }
- }, 10);
- }
- });
- });
- observer.observe(target, { childList: true });
- }
-
- /*
- *カタログを検索して強調表示
- */
- function highlight(isWordsChanged) {
- var Start = new Date().getTime();//count parsing time
- var words = "";
- var words_common = GM_getValue("_futaba_thread_search_words", "");
- var words_indiv = getCurrentIndivValue();
- if( words_common !== "" ) {
- words += words_common;
- if( words_indiv !== "" ) {
- words += "|" + words_indiv;
- }
- }
- else {
- words += words_indiv;
- }
- //console.log(words);
- try {
- var re = new RegExp(words, "i");
- }
- catch (e) {
- alert("検索ワードのパターンが無効です\n\n" + e);
- editWords();
- return;
- }
- if( words !== "" ) {
- removeOldHighlighted();
- $("body > table[border] td small").each(function(){
- if( $(this).text().match(re) ) {
- if ( !$(this).children(".GM_fth_matchedword").length ) {
- $(this).html($(this).html().replace(re,
- "<span class='GM_fth_matchedword'>" +
- $(this).text().match(re)[0] +
- "</span>"));
- }
- if ( $(this).parent("a").length ) { //文字スレ
- $(this).parent().parent("td").addClass("GM_fth_highlighted");
- } else {
- $(this).parent("td").addClass("GM_fth_highlighted");
- }
- }
- });
- pickup_highlighted();
- }
- else {
- removeOldHighlighted();
- pickup_highlighted();
- }
- function removeOldHighlighted() {
- if(isWordsChanged) {
- $(".GM_fth_highlighted").removeClass("GM_fth_highlighted");
- $(".GM_fth_matchedword").each(function(){
- $(this).replaceWith($(this).text());
- });
- }
- }
- console.log('futaba_thread_highlighter - Parsing@' + serverFullPath + ': '+((new Date()).getTime()-Start) +'msec');//log parsing time
- }
-
- /*
- *強調表示したスレを先頭にピックアップ
- */
- function pickup_highlighted() {
- if ( $("#GM_fth_highlighted_threads .GM_fth_pickuped").length ) {
- $("#GM_fth_highlighted_threads .GM_fth_pickuped").remove();
- }
- var highlighted = $("body > table .GM_fth_highlighted").clone();
- $("#GM_fth_highlighted_threads").append(highlighted);
- //要素の中身を整形
- highlighted.each(function(){
- if ( !$(this).children("small").length ) { //文字スレ
- //console.log($(this).children("a").html());
- //$(this).children("a").replaceWith("<div class='GM_fth_pickuped_caption'>" + $(this).html() + "</div>");
- } else {
- $(this).children("small:not(.aima_aimani_generated)").replaceWith("<div class='GM_fth_pickuped_caption'>" +
- $(this).children("small").html() + "</div>");
- $(this).children("br").replaceWith();
- }
- $(this).replaceWith("<div class='GM_fth_pickuped'>" + $(this).html() + "</div>");
- });
- var $pickuped = $(".GM_fth_pickuped");
- $pickuped.each(function(){
- var width = $(this).find("img").attr("width");
- $(this).css({
- //スレ画の幅に合わせる
- width: width,
- });
- });
- }
-
- /*
- *スタイル設定
- */
- function setStyle() {
- var css =
- //マッチ文字列の背景色
- ".GM_fth_matchedword {" +
- " background-color: #ff0;" +
- "}" +
- //セルの背景色
- ".GM_fth_highlighted {" +
- " background-color: #FFDFE9 !important;" +
- "}" +
- //ピックアップスレ
- ".GM_fth_pickuped {" +
- " max-width: 250px;" +
- " min-width: 70px;" +
- " margin: 1px;" +
- " background-color: #FFDFE9;" +
- " border-radius: 5px;" +
- " word-wrap: break-word;" +
- "}" +
- //ピックアップスレ本文
- ".GM_fth_pickuped_caption {" +
- " font-size: small;" +
- " background-color: #ffdfe9;" +
- "}";
- GM_addStyle(css);
- }
- })(jQuery);