futaba_thread_highlighter

スレ本文を検索してカタログでスレッド監視しちゃう

  1. // ==UserScript==
  2. // @name futaba_thread_highlighter
  3. // @namespace https://github.com/himuro-majika
  4. // @description スレ本文を検索してカタログでスレッド監視しちゃう
  5. // @include http://*.2chan.net/*/futaba.php?mode=cat*
  6. // @include https://*.2chan.net/*/futaba.php?mode=cat*
  7. // @version 1.6.6
  8. // @require http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js
  9. // @grant GM_registerMenuCommand
  10. // @grant GM_getValue
  11. // @grant GM_setValue
  12. // @grant GM_addStyle
  13. // @license MIT
  14. // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAAPUExURYv4i2PQYy2aLUe0R////zorx9oAAAAFdFJOU/////8A+7YOUwAAAElJREFUeNqUj1EOwDAIQoHn/c88bX+2fq0kRsAoUXVAfwzCttWsDWzw0kNVWd2tZ5K9gqmMZB8libt4pSg6YlO3RnTzyxePAAMAzqMDgTX8hYYAAAAASUVORK5CYII=
  15. // ==/UserScript==
  16.  
  17. this.$ = this.jQuery = jQuery.noConflict(true);
  18.  
  19. (function ($) {
  20. var serverName = document.domain.match(/^[^.]+/);
  21. var pathName = location.pathname.match(/[^/]+/);
  22. var serverFullPath = serverName + "_" + pathName;
  23. var akahukuloadstat;
  24.  
  25. init();
  26.  
  27. function init(){
  28. console.log("futaba_thread_highlighter commmon: " +
  29. GM_getValue("_futaba_thread_search_words", ""));
  30. console.log("futaba_thread_highlighter indivisual: " +
  31. getCurrentIndivValue());
  32. GM_registerMenuCommand("スレッド検索ワード編集", editWords);
  33. setStyle();
  34. makecontainer();
  35. makeConfigUI();
  36. highlight();
  37. check_akahuku_reload();
  38. }
  39.  
  40. /*
  41. *設定画面表示
  42. */
  43. function editWords(){
  44. var word_commmon = GM_getValue("_futaba_thread_search_words", "");
  45. var word_indiv = getCurrentIndivValue();
  46. $("#GM_fth_searchword_common").val(word_commmon);
  47. $("#GM_fth_searchword_individual").val(word_indiv);
  48. var $config_container_ = $("#GM_fth_config_container");
  49. $config_container_.fadeIn(100);
  50. setRandomExample();
  51. }
  52.  
  53. /*
  54. * 表示中の板の個別検索ワードの取得
  55. */
  56. function getCurrentIndivValue() {
  57. var indivobj = getIndivObj();
  58. var str_CurrentIndiv;
  59. if(indivobj !== "") {
  60. str_CurrentIndiv = indivobj[serverFullPath];
  61. }
  62. else {
  63. str_CurrentIndiv = "";
  64. }
  65. return str_CurrentIndiv;
  66. }
  67.  
  68. /*
  69. * 板毎の個別検索ワードのオブジェクトを取得
  70. */
  71. function getIndivObj() {
  72. var indivVal = GM_getValue("search_words_indiv", "");
  73. var obj_indiv;
  74. if(indivVal !== "") {
  75. obj_indiv = JSON.parse(indivVal);
  76. }
  77. else {
  78. obj_indiv = "";
  79. }
  80. return obj_indiv;
  81. }
  82.  
  83. /*
  84. * 検索ワードを設定
  85. */
  86. function setSearchWords() {
  87. var input_common = $("#GM_fth_searchword_common").val();
  88. var input_indiv = $("#GM_fth_searchword_individual").val();
  89. GM_setValue("_futaba_thread_search_words", input_common);
  90. console.log("futaba_thread_highlighter: common searchword updated - " + input_common);
  91. setIndivValue(input_indiv);
  92. $("#GM_fth_config_container").fadeOut(100);
  93. highlight(true);
  94. /*
  95. * 板毎の個別検索ワードを保存
  96. */
  97. function setIndivValue(val) {
  98. var obj_indiv = getIndivObj();
  99. if(obj_indiv === ""){
  100. obj_indiv = {};
  101. }
  102. obj_indiv[serverFullPath] = val;
  103. var jsonstring = JSON.stringify(obj_indiv);
  104. GM_setValue("search_words_indiv", jsonstring);
  105. console.log("futaba_thread_highlighter: indivisual searchword updated@" + serverFullPath + " - " + val);
  106. }
  107. }
  108.  
  109. /*
  110. *スレピックアップ表示エリアの設定
  111. */
  112. function makecontainer() {
  113. var $pickup_thread_area = $("<div>", {
  114. id: "GM_fth_container"
  115. });
  116. $("body > table[border]").before($pickup_thread_area);
  117.  
  118. var $container_header = $("<div>", {
  119. id: "GM_fth_container_header",
  120. text: "スレッド検索該当スレッド",
  121. css: {
  122. "background-color": "#F0E0D6",
  123. fontWeight: "bolder"
  124. }
  125. });
  126. $pickup_thread_area.append($container_header);
  127. //設定ボタン
  128. var $button = $("<span>", {
  129. id: "GM_fth_searchword",
  130. text: "[設定]",
  131. css: {
  132. cursor: "pointer",
  133. },
  134. click: function() {
  135. editWords();
  136. }
  137. });
  138. $button.hover(function () {
  139. $(this).css({ backgroundColor:"#EEAA88" });
  140. }, function () {
  141. $(this).css({ backgroundColor:"#F0E0D6" });
  142. });
  143. $container_header.append($button);
  144.  
  145. var $pickup_thread_container = $("<div>", {
  146. id: "GM_fth_highlighted_threads",
  147. css: {
  148. "display": "flex",
  149. "flex-wrap": "wrap",
  150. }
  151. });
  152. $pickup_thread_area.append($pickup_thread_container);
  153. }
  154.  
  155. /*
  156. * 設定画面
  157. */
  158. function makeConfigUI() {
  159. var $config_container = $("<div>", {
  160. id: "GM_fth_config_container",
  161. css: {
  162. position: "fixed",
  163. "z-index": "1001",
  164. left: "50%",
  165. top: "50%",
  166. "text-align": "center",
  167. "margin-left": "-475px",
  168. "margin-top": "-50px",
  169. "background-color": "rgba(240, 192, 214, 0.95)",
  170. width: "950px",
  171. //height: "100px",
  172. display: "none",
  173. fontWeight: "normal",
  174. "box-shadow": "3px 3px 5px #853e52",
  175. "border": "1px outset",
  176. "border-radius": "10px",
  177. "padding": "5px",
  178. }
  179. });
  180. $("#GM_fth_container_header").append($config_container);
  181. $config_container.append(
  182. $("<div>").append(
  183. $("<div>").text("スレ本文に含まれる語句を入力してください。 | を挟むと複数指定できます。正規表現使用可。"),
  184. $("<div>").text("例 : ").append(
  185. $("<span>").attr("id", "GM_fth_example").css({
  186. "background-color": "#ffeeee",
  187. "padding": "2px",
  188. "font-weight": "bold"
  189. })
  190. )
  191. ),
  192. $("<div>").css("margin-top", "1em").append(
  193. $("<div>").append(
  194. $("<label>").text("全板共通").attr("for", "GM_fth_searchword_common"),
  195. $("<input>").attr({
  196. "id": "GM_fth_searchword_common",
  197. "class": "GM_fth_input"
  198. }).css("width", "54em"),
  199. $("<span>").append(
  200. $("<input>", {
  201. class: "GM_fth_config_button",
  202. type: "button",
  203. val: "区切り文字挿入",
  204. click: function(){
  205. insertDelimiter("GM_fth_searchword_common");
  206. },
  207. })
  208. )
  209. ),
  210. $("<div>").append(
  211. $("<label>").text("各板個別").attr("for", "GM_fth_searchword_individual"),
  212. $("<input>").attr({
  213. "id": "GM_fth_searchword_individual",
  214. "class": "GM_fth_input"
  215. }).css("width", "54em"),
  216. $("<span>").append(
  217. $("<input>", {
  218. class: "GM_fth_config_button",
  219. type: "button",
  220. val: "区切り文字挿入",
  221. click: function(){
  222. insertDelimiter("GM_fth_searchword_individual");
  223. },
  224. })
  225. )
  226. )
  227. ),
  228. $("<div>").css({
  229. "margin-top": "1em",
  230. }).append(
  231. $("<span>").css("margin", "0 1em").append(
  232. $("<input>", {
  233. class: "GM_fth_config_button",
  234. type: "button",
  235. val: "更新",
  236. click: function(){
  237. setSearchWords();
  238. },
  239. })
  240. ),
  241. $("<span>").css("margin", "0 1em").append(
  242. $("<input>", {
  243. class: "GM_fth_config_button",
  244. type: "button",
  245. val: "キャンセル",
  246. click: function(){
  247. $config_container.fadeOut(100);
  248. },
  249. })
  250. )
  251. )
  252. );
  253. $(".GM_fth_config_button").css({
  254. "cursor": "pointer",
  255. "background-color": "#FFECFD",
  256. "border": "2px outset #96ABFF",
  257. "border-radius": "5px",
  258. }).hover(function() {
  259. $(this).css("background-color", "#CCE9FF");
  260. }, function() {
  261. $(this).css("background-color", "#FFECFD");
  262. });
  263. setRandomExample();
  264.  
  265. /*
  266. * カーソル位置にデリミタ挿入
  267. */
  268. function insertDelimiter(id){
  269. var $input = $("#" + id);
  270. var val = $input.val();
  271. var position = $input[0].selectionStart;
  272. var newval = val.substr(0, position) + "|" + val.substr(position);
  273. $input.val(newval);
  274. $input[0].setSelectionRange(position + 1 ,position + 1);
  275. }
  276. }
  277.  
  278. function setRandomExample() {
  279. var exampleWords = [
  280. "妹がレイ",
  281. "悪魔がおる",
  282. "みなもちゃんかわいい",
  283. "つまんね",
  284. "マジか",
  285. "落ち着け",
  286. "アオいいよね",
  287. "いい…",
  288. "フラワハハ",
  289. "(i)orz",
  290. "うま(み|あじ)",
  291. "[0-9]時から!",
  292. "mjpk\\!\\?",
  293. "よしとみくんは何が好き?",
  294. "焼肉!",
  295. "そろそろ",
  296. "(はじ)?まるよ?",
  297. "ワグナス!!",
  298. "オマンコパンティー",
  299. "ワーオ!"
  300. ];
  301. var rand, randwords = [];
  302. for(var i = 0, l = exampleWords.length; i < 3; i++, l--) {
  303. rand = Math.floor(Math.random() * l);
  304. randwords.push(exampleWords.splice(rand, 1)[0]);
  305. }
  306. var example = randwords.join("|");
  307. $("#GM_fth_example").text(example);
  308. }
  309.  
  310. /*
  311. *赤福の動的リロードの状態を取得
  312. */
  313. function check_akahuku_reload() {
  314. var target = $("html > body").get(0);
  315. if ($("#cat_search").length) {
  316. // ふたクロ
  317. highlight();
  318. target = $("html > body > table[border]").get(0);
  319. }
  320. var observer = new MutationObserver(function(mutations) {
  321. mutations.forEach(function(mutation) {
  322. var nodes = $(mutation.addedNodes);
  323. if ($("#cat_search").length) {
  324. // ふたクロ
  325. if (nodes.length) {
  326. highlight();
  327. }
  328. }
  329. else if (nodes.attr("border") == "1") {
  330. var timer = setInterval(function() {
  331. var status = $("#akahuku_catalog_reload_status").text();
  332. if(status === "" || status == "完了しました") {
  333. clearInterval(timer);
  334. highlight();
  335. }
  336. }, 10);
  337. }
  338. });
  339. });
  340. observer.observe(target, { childList: true });
  341. }
  342.  
  343. /*
  344. *カタログを検索して強調表示
  345. */
  346. function highlight(isWordsChanged) {
  347. var Start = new Date().getTime();//count parsing time
  348. var words = "";
  349. var words_common = GM_getValue("_futaba_thread_search_words", "");
  350. var words_indiv = getCurrentIndivValue();
  351. if( words_common !== "" ) {
  352. words += words_common;
  353. if( words_indiv !== "" ) {
  354. words += "|" + words_indiv;
  355. }
  356. }
  357. else {
  358. words += words_indiv;
  359. }
  360. //console.log(words);
  361. try {
  362. var re = new RegExp(words, "i");
  363. }
  364. catch (e) {
  365. alert("検索ワードのパターンが無効です\n\n" + e);
  366. editWords();
  367. return;
  368. }
  369. if( words !== "" ) {
  370. removeOldHighlighted();
  371. $("body > table[border] td small").each(function(){
  372. if( $(this).text().match(re) ) {
  373. if ( !$(this).children(".GM_fth_matchedword").length ) {
  374. $(this).html($(this).html().replace(re,
  375. "<span class='GM_fth_matchedword'>" +
  376. $(this).text().match(re)[0] +
  377. "</span>"));
  378. }
  379. if ( $(this).parent("a").length ) { //文字スレ
  380. $(this).parent().parent("td").addClass("GM_fth_highlighted");
  381. } else {
  382. $(this).parent("td").addClass("GM_fth_highlighted");
  383. }
  384. }
  385. });
  386. pickup_highlighted();
  387. }
  388. else {
  389. removeOldHighlighted();
  390. pickup_highlighted();
  391. }
  392. function removeOldHighlighted() {
  393. if(isWordsChanged) {
  394. $(".GM_fth_highlighted").removeClass("GM_fth_highlighted");
  395. $(".GM_fth_matchedword").each(function(){
  396. $(this).replaceWith($(this).text());
  397. });
  398. }
  399. }
  400. console.log('futaba_thread_highlighter - Parsing@' + serverFullPath + ': '+((new Date()).getTime()-Start) +'msec');//log parsing time
  401. }
  402.  
  403. /*
  404. *強調表示したスレを先頭にピックアップ
  405. */
  406. function pickup_highlighted() {
  407. if ( $("#GM_fth_highlighted_threads .GM_fth_pickuped").length ) {
  408. $("#GM_fth_highlighted_threads .GM_fth_pickuped").remove();
  409. }
  410. var highlighted = $("body > table .GM_fth_highlighted").clone();
  411. $("#GM_fth_highlighted_threads").append(highlighted);
  412. //要素の中身を整形
  413. highlighted.each(function(){
  414. if ( !$(this).children("small").length ) { //文字スレ
  415. //console.log($(this).children("a").html());
  416. //$(this).children("a").replaceWith("<div class='GM_fth_pickuped_caption'>" + $(this).html() + "</div>");
  417. } else {
  418. $(this).children("small:not(.aima_aimani_generated)").replaceWith("<div class='GM_fth_pickuped_caption'>" +
  419. $(this).children("small").html() + "</div>");
  420. $(this).children("br").replaceWith();
  421. }
  422. $(this).replaceWith("<div class='GM_fth_pickuped'>" + $(this).html() + "</div>");
  423. });
  424. var $pickuped = $(".GM_fth_pickuped");
  425. $pickuped.each(function(){
  426. var width = $(this).find("img").attr("width");
  427. $(this).css({
  428. //スレ画の幅に合わせる
  429. width: width,
  430. });
  431. });
  432. }
  433.  
  434. /*
  435. *スタイル設定
  436. */
  437. function setStyle() {
  438. var css =
  439. //マッチ文字列の背景色
  440. ".GM_fth_matchedword {" +
  441. " background-color: #ff0;" +
  442. "}" +
  443. //セルの背景色
  444. ".GM_fth_highlighted {" +
  445. " background-color: #FFDFE9 !important;" +
  446. "}" +
  447. //ピックアップスレ
  448. ".GM_fth_pickuped {" +
  449. " max-width: 250px;" +
  450. " min-width: 70px;" +
  451. " margin: 1px;" +
  452. " background-color: #FFDFE9;" +
  453. " border-radius: 5px;" +
  454. " word-wrap: break-word;" +
  455. "}" +
  456. //ピックアップスレ本文
  457. ".GM_fth_pickuped_caption {" +
  458. " font-size: small;" +
  459. " background-color: #ffdfe9;" +
  460. "}";
  461. GM_addStyle(css);
  462. }
  463. })(jQuery);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址