ニコニコ静画、簡単NGスクリプト

申し訳ないが見たくないイラストはNG

  1. // ==UserScript==
  2. // @name ニコニコ静画、簡単NGスクリプト
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.814
  5. // @description 申し訳ないが見たくないイラストはNG
  6. // @author cbxm
  7. // @match https://seiga.nicovideo.jp/tag/*
  8. // @match https://seiga.nicovideo.jp/seiga/*
  9. // @match https://seiga.nicovideo.jp/watch/*
  10. // @match https://seiga.nicovideo.jp/my/personalize*
  11. // @match https://nicoad.nicovideo.jp/widget/*
  12. // @match https://seiga.nicovideo.jp/search/*
  13. // @match https://seiga.nicovideo.jp/illust/list
  14. // @match https://seiga.nicovideo.jp/shunga/list
  15. // @connect seiga.nicovideo.jp
  16. // @connect nicoad.nicovideo.jp
  17. // @grant GM.xmlHttpRequest
  18. // @grant GM.getValue
  19. // @grant GM.setValue
  20. // @run-at document-start
  21. // @license MIT
  22. // ==/UserScript==
  23. (async () => {
  24. "use strict";
  25. ;
  26. ;
  27. //汎用関数
  28. class Util {
  29. static WaitDocumentElement() {
  30. return new Promise(r => {
  31. if (document.documentElement != null) {
  32. return r();
  33. }
  34. window.addEventListener("DOMContentLoaded", () => {
  35. return r();
  36. });
  37. });
  38. }
  39. //https://stackoverflow.com/questions/69368851/type-safe-way-of-narrowing-type-of-arrays-by-length-when-nouncheckedindexedacces
  40. //TypeScriptくんを納得させるための関数
  41. static HasLengthAtLeast(arr, len) {
  42. return arr != null && arr.length >= len;
  43. }
  44. //TypeScriptくんを納得させるための関数
  45. static IsLength(arr, len) {
  46. return arr != null && arr.length == len;
  47. }
  48. //xmlString=未探査部分
  49. static XmlToObj(xmlString) {
  50. const F = (xmlString, obj) => {
  51. //タグを抜き出す
  52. let tagMatchs = null;
  53. while (true) {
  54. tagMatchs = xmlString.match(/<([^>]+)>/); //タグを探す
  55. //タグがないということはそれが値になる
  56. if (tagMatchs == null) {
  57. return xmlString;
  58. }
  59. if (tagMatchs[1]?.[tagMatchs[1].length - 1] == "/") {
  60. xmlString = xmlString.replace(/<[^>]+>([^]*)/, "$1");
  61. }
  62. else {
  63. break;
  64. }
  65. }
  66. if (!Util.HasLengthAtLeast(tagMatchs, 2)) {
  67. return xmlString;
  68. }
  69. const tag = tagMatchs[1];
  70. //タグの内側とその先を抜き出す
  71. const matchChildlen = [];
  72. while (true) {
  73. const matchs = xmlString.match(new RegExp(`^[^<]*<${tag}>([^]+?)<\/${tag}>([^]*)`));
  74. if (matchs == null || !Util.HasLengthAtLeast(matchs, 3)) {
  75. break;
  76. }
  77. matchChildlen.push(matchs[1]);
  78. xmlString = matchs[2];
  79. }
  80. //タグあったのにマッチしなかったおかしい
  81. if (matchChildlen.length == 0) {
  82. return obj;
  83. }
  84. //そのタグが一つしかないとき、オブジェクトになる
  85. if (Util.IsLength(matchChildlen, 1)) {
  86. //子を探す
  87. obj[tag] = F(matchChildlen[0], {});
  88. }
  89. //そのタグが複数あるとき、配列になる
  90. if (matchChildlen.length > 1) {
  91. obj = [];
  92. for (let i = 0; i < matchChildlen.length; i++) {
  93. //子を探す
  94. obj[i] = F(matchChildlen[i], {});
  95. }
  96. }
  97. //兄弟を探す
  98. F(xmlString, obj);
  99. return obj;
  100. };
  101. //初期化で<xml>を取り除く
  102. xmlString = xmlString.replace(/\s*<[^>]+>([^]+)/, "$1");
  103. return F(xmlString, {});
  104. }
  105. static HtmlToDocument(str) {
  106. const parser = new DOMParser();
  107. return parser.parseFromString(str, "text/html");
  108. }
  109. static HtmlToChildNodes(str) {
  110. return this.HtmlToDocument(str).body.childNodes;
  111. }
  112. static HtmlToElement(str) {
  113. return this.HtmlToDocument(str).body.firstElementChild;
  114. }
  115. static HtmlToSVG(s) {
  116. var div = document.createElementNS('https://www.w3.org/1999/xhtml', 'div');
  117. div.innerHTML = '<svg xmlns="https://www.w3.org/2000/svg">' + s + '</svg>';
  118. var frag = document.createDocumentFragment();
  119. while (div.firstChild?.firstChild)
  120. frag.appendChild(div.firstChild.firstChild);
  121. return frag;
  122. }
  123. static Wait(ms) {
  124. return new Promise(r => setTimeout(() => r(null), ms));
  125. }
  126. static WithTimeOut(p, ms) {
  127. return Promise.race([p, this.Wait(ms)]);
  128. }
  129. static async Retry(p, retryCount, wait, predicate) {
  130. for (let i = 0; i < retryCount; i++) {
  131. const result = await p();
  132. if (predicate(result)) {
  133. return result;
  134. }
  135. //console.log("wait...");
  136. await Util.Wait(wait);
  137. }
  138. return null;
  139. }
  140. static async Download(url, name) {
  141. const link = document.createElement("a");
  142. document.body.appendChild(link);
  143. link.download = name;
  144. link.href = url;
  145. link.click();
  146. //すぐに消すと反応しないとか
  147. await this.Wait(100);
  148. document.body.removeChild(link);
  149. }
  150. static GMFetchText(url, optopns) {
  151. //console.log(url);
  152. return new Promise((resolve, reject) => {
  153. GM.xmlHttpRequest({
  154. url: url,
  155. ...optopns,
  156. onload: (response) => {
  157. // リダイレクトを処理(Chromeだと処理できないバグ?https://github.com/Tampermonkey/tampermonkey/issues/2134)
  158. if (response.responseText == null && response.status >= 300 && response.status < 400) {
  159. if (url != response.finalUrl) {
  160. return this.GMFetchText(response.finalUrl, optopns);
  161. }
  162. console.error("リダイレクトを処理できませんでした");
  163. }
  164. resolve(response.responseText);
  165. },
  166. onerror: (e) => {
  167. console.error(e);
  168. reject("");
  169. }
  170. });
  171. });
  172. }
  173. static Unique(array) {
  174. return Array.from(new Set(array));
  175. }
  176. static IsElementInViewport(el) {
  177. var rect = el.getBoundingClientRect();
  178. return (rect.top >= 0 &&
  179. rect.left >= 0 &&
  180. rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
  181. rect.right <= (window.innerWidth || document.documentElement.clientWidth));
  182. }
  183. static SetCookie(key, value, option = "") {
  184. window.document.cookie = encodeURIComponent(key) + "=" + encodeURIComponent(value) + "; " + option;
  185. }
  186. static SetCookieKeyAndValue(keyAndValue) {
  187. window.document.cookie = keyAndValue;
  188. }
  189. static GetAllCookie() {
  190. const cookies = window.document.cookie.split(";").map(c => decodeURI(c).replace(/^\s/, ""));
  191. let keyVal = [];
  192. for (let i = 0; i < cookies.length; i++) {
  193. //cookies[i]=cookies[i].replace(/^\s/,""); ???
  194. const s = cookies[i].match(/^(.*?)=(.*?)$/);
  195. if (Util.HasLengthAtLeast(s, 3)) {
  196. keyVal.push({ key: s[1], val: s[2] });
  197. }
  198. }
  199. return keyVal;
  200. }
  201. static GetCookie(key) {
  202. const cookies = window.document.cookie.split(";").map(c => decodeURI(c).replace(/^\s/, ""));
  203. for (var c of cookies) {
  204. if (RegExp(key).test(c)) {
  205. return c;
  206. }
  207. }
  208. return null;
  209. }
  210. static GetCookieVal(key) {
  211. const cookies = window.document.cookie.split(";").map(c => decodeURI(c).replace(/^\s/, ""));
  212. for (var c of cookies) {
  213. const matched = c.match(`${key}=([^]*)`);
  214. if (Util.HasLengthAtLeast(matched, 2)) {
  215. return matched[1];
  216. }
  217. }
  218. return null;
  219. }
  220. static DeleteCookie(key) {
  221. window.document.cookie = encodeURI(key) + "=; max-age=0";
  222. }
  223. }
  224. ;
  225. class Fetcher {
  226. static GMFetchText(url) {
  227. return Util.GMFetchText(url, {
  228. method: "GET"
  229. });
  230. }
  231. static async FetchIllustDatas(ids) {
  232. if (ids.length == 0) {
  233. return { illusts: [], userIds: [] };
  234. }
  235. const url = `https:\/\/seiga.nicovideo.jp/api/illust/info?id_list=${ids.join()}`;
  236. const res = await this.GMFetchText(url);
  237. const obj = Util.XmlToObj(res);
  238. if (obj.response == undefined) {
  239. return { illusts: [], userIds: [] };
  240. }
  241. const list = Array.isArray(obj.response.image_list) ? obj.response.image_list : [obj.response.image_list.image];
  242. const illusts = [];
  243. for (let i = 0; i < list.length; i++) {
  244. illusts[i] = {
  245. illustId: list[i].id,
  246. created: new Date(list[i].created)
  247. };
  248. }
  249. return {
  250. illusts: illusts,
  251. userIds: list.map(l => l.user_id)
  252. };
  253. }
  254. static async FetchUserName(userId) {
  255. const url = "https://seiga.nicovideo.jp/api/user/info?id=" + userId;
  256. const json = Util.XmlToObj(await this.GMFetchText(url));
  257. if (json.response == undefined) {
  258. return { illusts: [], userIds: [] };
  259. }
  260. return json.response.user.nickname;
  261. }
  262. static async FetchUserId(illustId) {
  263. const url = "https://seiga.nicovideo.jp/api/illust/info?id=im" + illustId;
  264. const resultText = await this.GMFetchText(url);
  265. const json = Util.XmlToObj(resultText);
  266. if (json.response == undefined) {
  267. return { illusts: [], userIds: [] };
  268. }
  269. return json.response.image.user_id;
  270. }
  271. }
  272. ;
  273. class Storage {
  274. constructor(storageName) {
  275. this.storageName = "";
  276. this.storageName = storageName;
  277. }
  278. async GetStorageData(defaultValue = null) {
  279. const text = await GM.getValue(this.storageName, null);
  280. return text != null ? JSON.parse(decodeURIComponent(text)) : defaultValue;
  281. }
  282. async SetStorageData(data) {
  283. await GM.setValue(this.storageName, encodeURIComponent(JSON.stringify(data)));
  284. }
  285. }
  286. ;
  287. ;
  288. //MutationObserver使った便利関数
  289. class Observer {
  290. static Wait(predicate, parent = document, option = null) {
  291. return new Promise(r => {
  292. if (option == null) {
  293. option = {
  294. childList: true,
  295. subtree: true
  296. };
  297. }
  298. const mutationObserver = new MutationObserver((mrs) => {
  299. if (predicate(mrs)) {
  300. mutationObserver.disconnect();
  301. r(mrs);
  302. return;
  303. }
  304. });
  305. mutationObserver.observe(parent, option);
  306. });
  307. }
  308. ;
  309. static WaitAddedNodes(predicate, parent, option = null) {
  310. return new Promise(r => {
  311. if (option == null) {
  312. option = {
  313. childList: true,
  314. subtree: true
  315. };
  316. }
  317. const mutationObserver = new MutationObserver(async (mrs) => {
  318. //console.log(document.head.innerHTML);
  319. //console.log(document.body.innerHTML);
  320. const result = [];
  321. for (let node of mrs) {
  322. //console.log(added);
  323. for (let i = 0; i < node.addedNodes.length; i++) {
  324. result.push(...await predicate(node.addedNodes[i]));
  325. }
  326. }
  327. if (result.length != 0) {
  328. mutationObserver.disconnect();
  329. r(result);
  330. return;
  331. }
  332. });
  333. mutationObserver.observe(parent, option);
  334. });
  335. }
  336. ;
  337. static WaitAddedNode(predicate, parent, option = null) {
  338. return new Promise(r => {
  339. if (option == null) {
  340. option = {
  341. childList: true,
  342. subtree: true
  343. };
  344. }
  345. if (option.childList == undefined && option.attributes == undefined && option.characterData == undefined) {
  346. option.childList = true;
  347. option.subtree = true;
  348. }
  349. const mutationObserver = new MutationObserver(async (mrs) => {
  350. //console.log(document.head.innerHTML);
  351. //console.log(document.body.innerHTML);
  352. for (let node of mrs) {
  353. //console.log(added);
  354. for (let i = 0; i < node.addedNodes.length; i++) {
  355. const ret = await predicate(node.addedNodes[i]);
  356. if (ret != null) {
  357. mutationObserver.disconnect();
  358. r(ret);
  359. return;
  360. }
  361. }
  362. }
  363. });
  364. mutationObserver.observe(parent, option);
  365. });
  366. }
  367. ;
  368. static async DefinitelyGetElementById(id, parent = document.documentElement, option = null) {
  369. if (!(option?.doNotNormalCheck ?? false)) {
  370. const e = document.getElementById(id);
  371. if (e != null) {
  372. return e;
  373. }
  374. }
  375. return this.WaitAddedNode(e => (e instanceof Element && e.id == id) ? e : null, parent, option);
  376. }
  377. //getElementsByClassNameをつかうけど単体
  378. static async DefinitelyGetElementByClassName(className, parent = document.documentElement, option = null) {
  379. if (!(option?.doNotNormalCheck ?? false)) {
  380. const e = parent.getElementsByClassName(className)[0];
  381. if (e != null) {
  382. return e;
  383. }
  384. }
  385. return this.WaitAddedNode(e => {
  386. if (e instanceof Element) {
  387. if (e.classList.contains(className)) {
  388. return e;
  389. }
  390. if (option?.isDeepSearch ?? false) {
  391. const c = e.getElementsByClassName(className);
  392. if (c.length != 0) {
  393. return c[0];
  394. }
  395. }
  396. }
  397. return null;
  398. }, parent, option);
  399. }
  400. //getElementsByTagNameをつかうけど単体
  401. static async DefinitelyGetElementByTagName(tagName, parent = document.documentElement, option = null) {
  402. tagName = tagName.toUpperCase();
  403. if (!(option?.doNotNormalCheck ?? false)) {
  404. const e = parent.getElementsByTagName(tagName)[0];
  405. if (e != null) {
  406. return e;
  407. }
  408. }
  409. return this.WaitAddedNode(e => {
  410. if (e instanceof Element) {
  411. if (e.tagName == tagName) {
  412. return e;
  413. }
  414. if (option?.isDeepSearch ?? false) {
  415. const c = e.getElementsByTagName(tagName);
  416. if (c.length != 0) {
  417. return c[0];
  418. }
  419. }
  420. }
  421. return null;
  422. }, parent, option);
  423. }
  424. static async DefinitelyGetElementsByClassName(className, parent = document.documentElement, option = null) {
  425. if (!(option?.doNotNormalCheck ?? false)) {
  426. const e = parent.getElementsByClassName(className);
  427. if (e.length != 0) {
  428. return Array.from(e);
  429. }
  430. }
  431. return this.WaitAddedNodes(e => {
  432. const ret = [];
  433. if (e instanceof Element) {
  434. if (e.classList.contains(className)) {
  435. ret.push(e);
  436. }
  437. if (option?.isDeepSearch ?? false) {
  438. ret.push(...Array.from(e.getElementsByClassName(className)));
  439. }
  440. }
  441. return ret;
  442. }, parent, option);
  443. }
  444. static async DefinitelyGetElementsByTagName(tagName, parent = document.documentElement, option = null) {
  445. tagName = tagName.toUpperCase();
  446. if (!(option?.doNotNormalCheck ?? false)) {
  447. const e = parent.getElementsByTagName(tagName);
  448. if (e.length != 0) {
  449. return Array.from(e);
  450. }
  451. }
  452. return this.WaitAddedNodes(e => {
  453. const ret = [];
  454. if (e instanceof Element) {
  455. if (e.tagName == tagName) {
  456. ret.push(e);
  457. }
  458. if (option?.isDeepSearch ?? false) {
  459. ret.push(...Array.from(e.getElementsByTagName(tagName)));
  460. }
  461. }
  462. return ret;
  463. }, parent, option);
  464. }
  465. }
  466. ;
  467. //暫定OK、暫定荒らし、確定OK、確定荒らし
  468. //type Status = "OK" | "NG" | "LOK" | "LNG"
  469. let VirtualPageType;
  470. (function (VirtualPageType) {
  471. VirtualPageType[VirtualPageType["None"] = -1] = "None";
  472. VirtualPageType[VirtualPageType["TAG_SEARCH"] = 0] = "TAG_SEARCH";
  473. VirtualPageType[VirtualPageType["ILLUST"] = 1] = "ILLUST";
  474. VirtualPageType[VirtualPageType["PERSONALIZE"] = 2] = "PERSONALIZE";
  475. VirtualPageType[VirtualPageType["ADS"] = 3] = "ADS";
  476. VirtualPageType[VirtualPageType["KEYWORD_SEARCH"] = 4] = "KEYWORD_SEARCH";
  477. VirtualPageType[VirtualPageType["MAX"] = 5] = "MAX";
  478. })(VirtualPageType || (VirtualPageType = {}));
  479. ;
  480. ;
  481. ;
  482. const pageInfos = [
  483. {
  484. type: VirtualPageType.TAG_SEARCH,
  485. regex: /https:\/\/seiga.nicovideo.jp\/tag\/.*/,
  486. name: "タグ検索",
  487. },
  488. {
  489. type: VirtualPageType.ILLUST,
  490. regex: /https:\/\/seiga.nicovideo.jp\/(seiga|watch)\/.*/,
  491. name: "イラストページ",
  492. },
  493. {
  494. type: VirtualPageType.PERSONALIZE,
  495. regex: /https:\/\/seiga.nicovideo.jp\/my\/personalize.*/,
  496. name: "定点観測",
  497. },
  498. {
  499. type: VirtualPageType.ADS,
  500. regex: /https:\/\/nicoad.nicovideo.jp\/widget\/.*/,
  501. name: "ニコニ広告",
  502. },
  503. {
  504. type: VirtualPageType.KEYWORD_SEARCH,
  505. regex: /https:\/\/seiga.nicovideo.jp\/search\/.*/,
  506. name: "キーワード検索",
  507. },
  508. {
  509. type: VirtualPageType.TAG_SEARCH,
  510. regex: /https:\/\/seiga.nicovideo.jp\/illust\/list/,
  511. name: "静画イラスト一覧",
  512. },
  513. {
  514. type: VirtualPageType.KEYWORD_SEARCH,
  515. regex: /https:\/\/seiga.nicovideo.jp\/shunga\/list/,
  516. name: "春画イラスト一覧",
  517. }
  518. ];
  519. const virtualPageInfos = [
  520. {
  521. illustListName: "item_list",
  522. illustItemName: "list_item",
  523. },
  524. {
  525. illustListName: "item_list",
  526. illustItemName: "list_item_cutout",
  527. },
  528. {
  529. illustListName: "list_body",
  530. illustItemName: "illust_thumb",
  531. },
  532. {
  533. // 空ならul,ilを使う
  534. illustListName: "",
  535. illustItemName: "",
  536. },
  537. {
  538. illustListName: "illust_pict_all",
  539. illustItemName: "illust_list_img",
  540. }
  541. ];
  542. let Status;
  543. (function (Status) {
  544. Status[Status["NONE"] = 0] = "NONE";
  545. Status[Status["OK"] = 1] = "OK";
  546. Status[Status["NG"] = 2] = "NG";
  547. Status[Status["WHITE"] = 3] = "WHITE";
  548. Status[Status["BLACK"] = 4] = "BLACK";
  549. Status[Status["AUTO"] = 5] = "AUTO";
  550. Status[Status["MAX"] = 6] = "MAX";
  551. })(Status || (Status = {}));
  552. class Main {
  553. constructor() {
  554. this.cache = new Map();
  555. this.illustInfos = [];
  556. this.illustListElements = new Set();
  557. this.selectedList = [];
  558. this.cacheStorage = new Storage("NICONICO_RENTO_ARASI_NG_DATA_CACHE");
  559. this.optionStorage = new Storage("NICONICO_RENTO_ARASI_NG_OPTION_CACHE");
  560. this.imgIntersectionObserver = new IntersectionObserver(entries => {
  561. for (let e of entries) {
  562. if (e.intersectionRatio > 0) {
  563. const img = e.target;
  564. if (img.src != null && img.dataset != null && img.dataset.src != null) {
  565. img.src = img.dataset.src;
  566. }
  567. this.imgIntersectionObserver.unobserve(img);
  568. }
  569. }
  570. });
  571. }
  572. ListToMap(list) {
  573. for (let o of list) {
  574. o.name ?? (o.name = "");
  575. }
  576. if (list[0] != null && list[0].userId != null) {
  577. return new Map(list.map(o => {
  578. const userId = o.userId;
  579. delete o.userId;
  580. for (let illust of o.illusts) {
  581. illust.illustId = illust.id;
  582. delete illust.id;
  583. }
  584. return [userId, o];
  585. }));
  586. }
  587. return new Map(list);
  588. }
  589. async GetStorageData() {
  590. const obj = await this.cacheStorage.GetStorageData([]);
  591. this.cache = this.ListToMap(obj);
  592. //console.log(this.cache);
  593. const defaultOption = {
  594. usePages: [true, true, true, true, true, true, true],
  595. judge: {
  596. isJudge: false,
  597. time: 1 * 60 * 60 * 1000, //一時間
  598. postCount: 5,
  599. period: 30,
  600. isAutoNGHidden: false
  601. },
  602. createUserLink: true,
  603. createBlackWhiteButton: true,
  604. okCacheMax: 1000, //どのくらいがいいのかわからない
  605. };
  606. this.option = await this.optionStorage.GetStorageData(defaultOption);
  607. if (this.option.usePages == undefined) {
  608. this.option.usePages = defaultOption.usePages;
  609. }
  610. for (let i = 0; i < VirtualPageType.MAX; i++) {
  611. if (this.option.usePages[i] == undefined) {
  612. this.option.usePages[i] = defaultOption.usePages[i];
  613. }
  614. }
  615. if (this.option.judge == undefined) {
  616. this.option.judge = defaultOption.judge;
  617. }
  618. if (this.option.judge.time == undefined) {
  619. this.option.judge.time = defaultOption.judge.time;
  620. }
  621. if (this.option.judge.postCount == undefined) {
  622. this.option.judge.postCount = defaultOption.judge.postCount;
  623. }
  624. if (this.option.judge.period == undefined) {
  625. this.option.judge.period = defaultOption.judge.period;
  626. }
  627. if (this.option.judge.isJudge == undefined) {
  628. this.option.judge.isJudge = defaultOption.judge.isJudge;
  629. }
  630. if (this.option.judge.isAutoNGHidden == undefined) {
  631. this.option.judge.isAutoNGHidden = defaultOption.judge.isAutoNGHidden;
  632. }
  633. if (this.option.createUserLink == undefined) {
  634. this.option.createUserLink = defaultOption.createUserLink;
  635. }
  636. if (this.option.createBlackWhiteButton == undefined) {
  637. this.option.createBlackWhiteButton = defaultOption.createBlackWhiteButton;
  638. }
  639. if (this.option.okCacheMax == undefined) {
  640. this.option.okCacheMax = defaultOption.okCacheMax;
  641. }
  642. //console.log(this.option);
  643. }
  644. GetInfo(illustId) {
  645. for (let [userId, user] of this.cache) {
  646. if (user.illusts == null) {
  647. console.log(userId, user);
  648. user.illusts = [];
  649. }
  650. for (let illust of user.illusts) {
  651. if (illust.illustId == illustId) {
  652. return { userId: userId, user: user, illust: illust };
  653. }
  654. }
  655. }
  656. return undefined;
  657. }
  658. CheckAutoNG(user) {
  659. if (user.illusts.length == 0 || user.status != Status.OK) {
  660. return;
  661. }
  662. const createdNumbers = user.illusts.map(illust => {
  663. if (typeof illust.created == "string") {
  664. illust.created = new Date(illust.created);
  665. }
  666. return illust.created.getTime();
  667. });
  668. //新しい順
  669. const sorted = createdNumbers.sort((a, b) => b - a);
  670. const currentDateNumber = new Date().getTime();
  671. const periodMS = this.option.judge.period * 86400000;
  672. for (let i = 0; i < sorted.length; i++) {
  673. if (periodMS != 0 && sorted[i] <= currentDateNumber - periodMS) {
  674. break;
  675. }
  676. let j = i + 1;
  677. let postCount = 1;
  678. while (true) {
  679. if (j >= sorted.length || sorted[i] - sorted[j] > this.option.judge.time || (periodMS != 0 && sorted[j] <= currentDateNumber - periodMS)) {
  680. break;
  681. }
  682. j++;
  683. postCount++;
  684. }
  685. if (postCount >= this.option.judge.postCount) {
  686. user.status = Status.AUTO;
  687. return;
  688. }
  689. }
  690. }
  691. ;
  692. GetIllustIds(itemListElement) {
  693. const illustIdElements = itemListElement.getElementsByTagName("img");
  694. const illustIds = new Set();
  695. for (let i = 0; i < illustIdElements.length; i++) {
  696. // https://lohas.nicoseiga.jp//thumb/xxxxxxuz?yyyyyy から xxxxx を抜き出す
  697. const idMatchs = illustIdElements[i].src.match(/(\d+).+?(?:\?\d+)?$/);
  698. if (idMatchs == null) {
  699. continue;
  700. }
  701. const id = idMatchs[idMatchs.length - 1];
  702. illustIds.add(id);
  703. }
  704. return [...illustIds];
  705. }
  706. DrawList() {
  707. if (this.optionDialog == null) {
  708. return;
  709. }
  710. const list = this.optionDialog.getElementsByClassName("scrollUL")[0];
  711. const onlyCurrentPageCheckbox = this.optionDialog.getElementsByClassName("onlyCurrentPageCheckbox")[0];
  712. const listStatusSelect = this.optionDialog.getElementsByClassName("listStatusSelect")[0];
  713. if (list == undefined || onlyCurrentPageCheckbox == undefined || listStatusSelect == undefined) {
  714. return;
  715. }
  716. const status = listStatusSelect.value == "ALL" ? "" : Status[listStatusSelect.value];
  717. list.innerHTML = "";
  718. for (let [userId, user] of this.cache) {
  719. if (status != "" && user.status != status) {
  720. continue;
  721. }
  722. const info = this.illustInfos.find(info => info.userId == userId);
  723. let sampleIllustId = (info == undefined) ? undefined : info.illust.illustId;
  724. if (onlyCurrentPageCheckbox.checked && sampleIllustId == undefined) {
  725. continue;
  726. }
  727. if (sampleIllustId == undefined && user.illusts[0] != undefined) {
  728. sampleIllustId = user.illusts[0].illustId;
  729. }
  730. const div = document.createElement("div");
  731. div.style.height = "70px";
  732. div.style.display = "flex";
  733. div.style.flexDirection = "column";
  734. div.className = "userInfoItem";
  735. list.appendChild(div);
  736. div.addEventListener("mouseup", e => this.ClickList(div));
  737. {
  738. const nameIdDiv = document.createElement("div");
  739. nameIdDiv.style.top = "relative";
  740. nameIdDiv.style.position = "4px";
  741. div.appendChild(nameIdDiv);
  742. {
  743. const nameSpan = document.createElement("span");
  744. nameSpan.className = "userName";
  745. nameSpan.textContent = user.name;
  746. nameSpan.style.fontSize = "130%";
  747. nameSpan.style.color = "black";
  748. nameSpan.style.width = "66px";
  749. nameSpan.style.height = "24px";
  750. nameSpan.style.padding = "3px";
  751. nameIdDiv.appendChild(nameSpan);
  752. const idSpan = document.createElement("span");
  753. idSpan.className = "userId";
  754. idSpan.textContent = userId;
  755. idSpan.style.fontSize = "130%";
  756. idSpan.style.color = "black";
  757. idSpan.style.width = "66px";
  758. idSpan.style.padding = "3px";
  759. nameIdDiv.appendChild(idSpan);
  760. }
  761. const userAndSampleImgDiv = document.createElement("div");
  762. div.appendChild(userAndSampleImgDiv);
  763. {
  764. const aUser = document.createElement("a");
  765. aUser.href = `https:\/\/seiga.nicovideo.jp/user/illust/${userId}`;
  766. userAndSampleImgDiv.appendChild(aUser);
  767. {
  768. const imgUser = document.createElement("img");
  769. imgUser.dataset.src = `https:\/\/secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/${Math.floor(parseInt(userId) / 10000)}/${userId}.jpg`;
  770. imgUser.style.height = "40px";
  771. imgUser.style.position = "relative";
  772. imgUser.style.padding = "0 20px 0 10px";
  773. imgUser.style.top = "-5px";
  774. this.imgIntersectionObserver.observe(imgUser);
  775. aUser.appendChild(imgUser);
  776. imgUser.addEventListener("error", () => {
  777. imgUser.src = "https:\/\/secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/defaults/blank.jpg";
  778. });
  779. }
  780. if (sampleIllustId != undefined) {
  781. const aSample = document.createElement("a");
  782. aSample.href = `https:/\/seiga.nicovideo.jp/seiga/im${sampleIllustId}`;
  783. userAndSampleImgDiv.appendChild(aSample);
  784. {
  785. const imgSample = document.createElement("img");
  786. imgSample.dataset.src = `https:\/\/lohas.nicoseiga.jp\/\/thumb/${sampleIllustId}c`;
  787. imgSample.style.height = "30px";
  788. this.imgIntersectionObserver.observe(imgSample);
  789. imgSample.style.position = "relative";
  790. imgSample.style.top = "-5px";
  791. aSample.appendChild(imgSample);
  792. const bigSample = document.createElement("img");
  793. bigSample.dataset.src = `https:\/\/lohas.nicoseiga.jp\/\/thumb/${sampleIllustId}c`;
  794. bigSample.style.height = "100px";
  795. this.imgIntersectionObserver.observe(bigSample);
  796. bigSample.style.pointerEvents = "none";
  797. bigSample.style.position = "absolute";
  798. bigSample.style.zIndex = "110";
  799. imgSample.addEventListener("mouseover", () => {
  800. const clientRect = imgSample.getBoundingClientRect();
  801. const x = window.scrollX + clientRect.left + imgSample.width / 2 - 50;
  802. const y = window.scrollY + clientRect.top + imgSample.height / 2 - 50;
  803. bigSample.style.top = y + "px";
  804. bigSample.style.left = x + "px";
  805. document.body.appendChild(bigSample);
  806. });
  807. imgSample.addEventListener("mouseleave", () => {
  808. bigSample.remove();
  809. });
  810. }
  811. }
  812. }
  813. }
  814. }
  815. }
  816. ClickList(target) {
  817. if (target != null) {
  818. if (this.selectedList.includes(target)) {
  819. target.style.backgroundColor = "";
  820. this.selectedList = this.selectedList.filter(s => s != target);
  821. }
  822. else {
  823. target.style.backgroundColor = "rgba(0, 140, 255, 0.5)";
  824. this.selectedList.push(target);
  825. }
  826. }
  827. }
  828. async SetOptionButton() {
  829. if (document.getElementById("optionSpan") != null) {
  830. return;
  831. }
  832. const optionSpan = document.createElement("span");
  833. optionSpan.id = "optionSpan";
  834. optionSpan.style.margin = "0 10px";
  835. optionSpan.style.lineHeight = "29px";
  836. if (this.currentPage == VirtualPageType.KEYWORD_SEARCH) {
  837. const nextSibling = await Util.WithTimeOut(Observer.DefinitelyGetElementByClassName("search_tab_border"), 10000);
  838. if (nextSibling == null) {
  839. return;
  840. }
  841. nextSibling.insertAdjacentElement("beforebegin", optionSpan);
  842. }
  843. else {
  844. const parent = await Util.WithTimeOut(Observer.DefinitelyGetElementByClassName("sg_pankuzu"), 10000);
  845. if (parent == null) {
  846. return;
  847. }
  848. parent.appendChild(optionSpan);
  849. }
  850. {
  851. const optionButton = document.createElement("input");
  852. optionButton.type = "button";
  853. optionButton.value = "簡単NGスクリプト";
  854. optionButton.style.backgroundColor = "yellow";
  855. optionButton.style.padding = "1px 10px";
  856. optionButton.style.fontSize = "110%";
  857. optionButton.style.cssText += "color: black !important;";
  858. optionButton.addEventListener("click", () => {
  859. if (this.optionDialog.parentElement == null) {
  860. optionSpan.appendChild(this.optionDialog);
  861. return;
  862. }
  863. this.optionDialog.style.display = (this.optionDialog.style.display == "none") ? "block" : "none";
  864. });
  865. optionSpan.appendChild(optionButton);
  866. this.optionDialog = document.createElement("div");
  867. this.optionDialog.style.backgroundColor = "white";
  868. this.optionDialog.style.position = "absolute";
  869. this.optionDialog.style.padding = "5px";
  870. this.optionDialog.style.marginLeft = "10px";
  871. this.optionDialog.style.zIndex = "100";
  872. this.optionDialog.style.border = "2px solid";
  873. {
  874. const list1 = document.createElement("div");
  875. list1.style.display = "flex";
  876. list1.style.paddingTop = "5px";
  877. list1.style.paddingBottom = "10px";
  878. this.optionDialog.appendChild(list1);
  879. {
  880. const listStatusSelect = document.createElement("select");
  881. listStatusSelect.className = "listStatusSelect";
  882. listStatusSelect.style.margin = "5px";
  883. list1.appendChild(listStatusSelect);
  884. for (let i = 1; i <= Status.MAX; i++) {
  885. const option = document.createElement("option");
  886. const text = i == Status.MAX ? "ALL" : Status[i];
  887. option.value = text;
  888. option.textContent = text;
  889. listStatusSelect.appendChild(option);
  890. }
  891. listStatusSelect.addEventListener("change", () => {
  892. while (this.selectedList.length != 0) {
  893. const element = this.selectedList.pop();
  894. if (element != undefined) {
  895. element.style.backgroundColor = "";
  896. }
  897. }
  898. this.DrawList();
  899. });
  900. const onlyCurrentPageLabel = document.createElement("label");
  901. onlyCurrentPageLabel.style.color = "black";
  902. onlyCurrentPageLabel.style.padding = "3px";
  903. onlyCurrentPageLabel.style.display = "flex";
  904. list1.appendChild(onlyCurrentPageLabel);
  905. {
  906. const onlyCurrentPageCheckbox = document.createElement("input");
  907. onlyCurrentPageCheckbox.type = "checkbox";
  908. onlyCurrentPageCheckbox.className = "onlyCurrentPageCheckbox";
  909. onlyCurrentPageCheckbox.checked = true;
  910. onlyCurrentPageCheckbox.style.padding = "3px";
  911. onlyCurrentPageCheckbox.style.margin = "10px";
  912. onlyCurrentPageCheckbox.style.marginRight = "3px";
  913. onlyCurrentPageCheckbox.style.marginLeft = "0px";
  914. onlyCurrentPageLabel.appendChild(onlyCurrentPageCheckbox);
  915. onlyCurrentPageCheckbox.addEventListener("change", () => this.DrawList());
  916. const onlyCurrentPageText = document.createElement("div");
  917. onlyCurrentPageText.textContent = "このページだけ";
  918. onlyCurrentPageText.style.color = "black";
  919. onlyCurrentPageLabel.appendChild(onlyCurrentPageText);
  920. }
  921. const allSelect = document.createElement("input");
  922. allSelect.type = "button";
  923. allSelect.value = "全選択";
  924. allSelect.style.color = "black";
  925. allSelect.style.fontSize = "120%";
  926. allSelect.style.padding = "0 5px";
  927. allSelect.style.margin = "3px";
  928. list1.appendChild(allSelect);
  929. allSelect.addEventListener("click", () => {
  930. const infos = Array.from(document.getElementsByClassName("userInfoItem"));
  931. for (let info of infos) {
  932. this.ClickList(info);
  933. }
  934. });
  935. const detailButton = document.createElement("input");
  936. detailButton.type = "button";
  937. detailButton.value = "設定";
  938. detailButton.style.color = "black";
  939. detailButton.style.fontSize = "120%";
  940. detailButton.style.margin = "3px";
  941. detailButton.style.marginLeft = "45px";
  942. detailButton.style.padding = "0 10px";
  943. list1.appendChild(detailButton);
  944. detailButton.addEventListener("click", () => detailDialog.style.display = (detailDialog.style.display == "none") ? "block" : "none");
  945. const detailDialog = document.createElement("div");
  946. detailDialog.style.backgroundColor = "white";
  947. detailDialog.style.display = "none";
  948. detailDialog.style.position = "absolute";
  949. detailDialog.style.paddingLeft = "10px";
  950. detailDialog.style.zIndex = "100";
  951. detailDialog.style.border = "2px solid";
  952. detailDialog.style.left = "360px";
  953. detailDialog.style.top = "10px";
  954. detailDialog.style.minWidth = "350px";
  955. list1.appendChild(detailDialog);
  956. const useSettingH3 = document.createElement("h1");
  957. useSettingH3.textContent = "使うところ";
  958. useSettingH3.style.fontSize = "140%";
  959. useSettingH3.style.marginTop = "10px";
  960. useSettingH3.style.color = "black";
  961. detailDialog.appendChild(useSettingH3);
  962. const setUseListDiv = document.createElement("div");
  963. setUseListDiv.style.marginBottom = "10px";
  964. setUseListDiv.style.display = "flex";
  965. setUseListDiv.style.flexWrap = "wrap";
  966. detailDialog.appendChild(setUseListDiv);
  967. {
  968. for (let i = 0; i < pageInfos.length; i++) {
  969. const setUseLabel = document.createElement("label");
  970. setUseLabel.style.display = "inline-block";
  971. setUseListDiv.appendChild(setUseLabel);
  972. {
  973. const setUsePageCheckbox = document.createElement("input");
  974. setUsePageCheckbox.type = "checkbox";
  975. setUsePageCheckbox.checked = this.option.usePages[i];
  976. setUsePageCheckbox.style.padding = "3px";
  977. setUsePageCheckbox.style.margin = "10px";
  978. setUsePageCheckbox.style.marginRight = "3px";
  979. setUseLabel.appendChild(setUsePageCheckbox);
  980. setUsePageCheckbox.addEventListener("change", async () => {
  981. this.option.usePages[i] = setUsePageCheckbox.checked;
  982. await this.optionStorage.SetStorageData(this.option);
  983. });
  984. const setUsePageText = document.createElement("span");
  985. setUsePageText.textContent = pageInfos[i].name;
  986. setUsePageText.style.padding = "3px";
  987. setUsePageText.style.fontSize = "120%";
  988. setUsePageText.style.color = "black";
  989. setUseLabel.appendChild(setUsePageText);
  990. }
  991. }
  992. }
  993. const otherSettingH3 = document.createElement("h1");
  994. otherSettingH3.textContent = "イラストサムネ";
  995. otherSettingH3.style.fontSize = "140%";
  996. otherSettingH3.style.marginTop = "10px";
  997. otherSettingH3.style.color = "black";
  998. detailDialog.appendChild(otherSettingH3);
  999. const setCreateUserLinkDiv = document.createElement("div");
  1000. setCreateUserLinkDiv.style.display = "flex";
  1001. detailDialog.appendChild(setCreateUserLinkDiv);
  1002. {
  1003. const setCreateUserLinkChackbox = document.createElement("input");
  1004. setCreateUserLinkChackbox.type = "checkbox";
  1005. setCreateUserLinkChackbox.id = "createUserLink";
  1006. setCreateUserLinkChackbox.checked = this.option.createUserLink;
  1007. setCreateUserLinkChackbox.style.padding = "3px";
  1008. setCreateUserLinkChackbox.style.margin = "10px";
  1009. setCreateUserLinkChackbox.style.marginRight = "3px";
  1010. setCreateUserLinkDiv.appendChild(setCreateUserLinkChackbox);
  1011. setCreateUserLinkChackbox.addEventListener("change", async () => {
  1012. this.option.createUserLink = setCreateUserLinkChackbox.checked;
  1013. await this.optionStorage.SetStorageData(this.option);
  1014. });
  1015. const setCreateUserLinkDivLabel = document.createElement("label");
  1016. setCreateUserLinkDivLabel.htmlFor = "createUserLink";
  1017. setCreateUserLinkDivLabel.textContent = "ユーザー名をユーザーページへのリンクにする";
  1018. setCreateUserLinkDivLabel.style.color = "black";
  1019. setCreateUserLinkDivLabel.style.padding = "3px";
  1020. setCreateUserLinkDivLabel.style.fontSize = "120%";
  1021. setCreateUserLinkDiv.appendChild(setCreateUserLinkDivLabel);
  1022. }
  1023. const setCreateBlackWhiteButtonDiv = document.createElement("div");
  1024. setCreateBlackWhiteButtonDiv.style.display = "flex";
  1025. detailDialog.appendChild(setCreateBlackWhiteButtonDiv);
  1026. {
  1027. const setCreateBlackWhiteButtonChackbox = document.createElement("input");
  1028. setCreateBlackWhiteButtonChackbox.type = "checkbox";
  1029. setCreateBlackWhiteButtonChackbox.id = "setCreateBlackWhiteButton";
  1030. setCreateBlackWhiteButtonChackbox.checked = this.option.createBlackWhiteButton;
  1031. setCreateBlackWhiteButtonChackbox.style.padding = "3px";
  1032. setCreateBlackWhiteButtonChackbox.style.margin = "10px";
  1033. setCreateBlackWhiteButtonChackbox.style.marginRight = "3px";
  1034. setCreateBlackWhiteButtonDiv.appendChild(setCreateBlackWhiteButtonChackbox);
  1035. setCreateBlackWhiteButtonChackbox.addEventListener("change", async () => {
  1036. this.option.createBlackWhiteButton = setCreateBlackWhiteButtonChackbox.checked;
  1037. await this.optionStorage.SetStorageData(this.option);
  1038. });
  1039. const setCreateBlackWhiteButtonLabel = document.createElement("label");
  1040. setCreateBlackWhiteButtonLabel.htmlFor = "setCreateBlackWhiteButton";
  1041. setCreateBlackWhiteButtonLabel.textContent = "白黒ボタンを付ける";
  1042. setCreateBlackWhiteButtonLabel.style.color = "black";
  1043. setCreateBlackWhiteButtonLabel.style.padding = "3px";
  1044. setCreateBlackWhiteButtonLabel.style.fontSize = "120%";
  1045. setCreateBlackWhiteButtonDiv.appendChild(setCreateBlackWhiteButtonLabel);
  1046. }
  1047. const otherSettingH4 = document.createElement("h1");
  1048. otherSettingH4.textContent = "連投自動NG";
  1049. otherSettingH4.style.fontSize = "140%";
  1050. otherSettingH4.style.marginTop = "10px";
  1051. otherSettingH4.style.color = "black";
  1052. detailDialog.appendChild(otherSettingH4);
  1053. const judgeRigorCover = document.createElement("div");
  1054. const setIsJudgeDiv = document.createElement("div");
  1055. setIsJudgeDiv.style.display = "flex";
  1056. detailDialog.appendChild(setIsJudgeDiv);
  1057. {
  1058. const isJudgeCheckbox = document.createElement("input");
  1059. isJudgeCheckbox.type = "checkbox";
  1060. isJudgeCheckbox.id = "isJudgeCheckbox";
  1061. isJudgeCheckbox.checked = this.option.judge.isJudge;
  1062. isJudgeCheckbox.style.padding = "3px";
  1063. isJudgeCheckbox.style.margin = "10px";
  1064. isJudgeCheckbox.style.marginRight = "3px";
  1065. setIsJudgeDiv.appendChild(isJudgeCheckbox);
  1066. isJudgeCheckbox.addEventListener("change", async () => {
  1067. this.option.judge.isJudge = isJudgeCheckbox.checked;
  1068. if (this.option.judge.isJudge) {
  1069. judgeRigorCover.style.visibility = "hidden";
  1070. }
  1071. else {
  1072. judgeRigorCover.style.visibility = "visible";
  1073. }
  1074. await this.optionStorage.SetStorageData(this.option);
  1075. });
  1076. const isJudgeLabel = document.createElement("label");
  1077. isJudgeLabel.htmlFor = "isJudgeCheckbox";
  1078. isJudgeLabel.textContent = "有効にする";
  1079. isJudgeLabel.style.color = "black";
  1080. isJudgeLabel.style.padding = "3px";
  1081. isJudgeLabel.style.fontSize = "120%";
  1082. setIsJudgeDiv.appendChild(isJudgeLabel);
  1083. }
  1084. const setJudgeDiv = document.createElement("div");
  1085. setJudgeDiv.style.padding = "5px";
  1086. setJudgeDiv.style.position = "relative";
  1087. detailDialog.appendChild(setJudgeDiv);
  1088. {
  1089. const setJudgeRigorDiv = document.createElement("div");
  1090. setJudgeRigorDiv.style.padding = "0px 10px 5px 10px";
  1091. setJudgeDiv.appendChild(setJudgeRigorDiv);
  1092. {
  1093. const setJudgeTime = document.createElement("input");
  1094. setJudgeTime.type = "time";
  1095. setJudgeTime.style.height = "20px";
  1096. setJudgeTime.style.fontSize = "120%";
  1097. const hour = ('00' + Math.floor(this.option.judge.time / 60 / 1000 / 60).toString()).slice(-2);
  1098. const minutes = ('00' + (this.option.judge.time / 60 / 1000 % 60).toString()).slice(-2);
  1099. setJudgeTime.value = `${hour}:${minutes}`;
  1100. setJudgeTime.addEventListener("change", async () => {
  1101. const [h, m] = setJudgeTime.value.split(":").map(s => parseInt(s));
  1102. const ms = ((h * 60) + m) * 60 * 1000;
  1103. if (ms >= 1) {
  1104. this.option.judge.time = ms;
  1105. await this.optionStorage.SetStorageData(this.option);
  1106. }
  1107. else {
  1108. const hour = ('00' + Math.floor(this.option.judge.time / 60 / 1000 / 60).toString()).slice(-2);
  1109. const minutes = ('00' + (this.option.judge.time / 60 / 1000 % 60).toString()).slice(-2);
  1110. setJudgeTime.value = `${hour}:${minutes}`;
  1111. }
  1112. });
  1113. setJudgeRigorDiv.appendChild(setJudgeTime);
  1114. const setJudgeText1 = document.createElement("span");
  1115. setJudgeText1.textContent = "以内に";
  1116. setJudgeText1.style.color = "black";
  1117. setJudgeText1.style.fontSize = "15px";
  1118. setJudgeRigorDiv.appendChild(setJudgeText1);
  1119. const setJudgePostCount = document.createElement("input");
  1120. setJudgePostCount.type = "number";
  1121. setJudgePostCount.value = this.option.judge.postCount.toString();
  1122. setJudgePostCount.min = "2";
  1123. setJudgePostCount.style.width = "40px";
  1124. setJudgePostCount.style.height = "20px";
  1125. setJudgePostCount.style.fontSize = "120%";
  1126. setJudgePostCount.addEventListener("change", async () => {
  1127. const num = parseInt(setJudgePostCount.value);
  1128. if (num >= 2) {
  1129. this.option.judge.postCount = num;
  1130. await this.optionStorage.SetStorageData(this.option);
  1131. }
  1132. else {
  1133. this.option.judge.postCount = 2;
  1134. setJudgePostCount.value = this.option.judge.postCount.toString();
  1135. }
  1136. });
  1137. setJudgeRigorDiv.appendChild(setJudgePostCount);
  1138. const setJudgeText2 = document.createElement("span");
  1139. setJudgeText2.textContent = "回投稿で仮荒らし認定";
  1140. setJudgeText2.style.color = "black";
  1141. setJudgeText2.style.fontSize = "15px";
  1142. setJudgeRigorDiv.appendChild(setJudgeText2);
  1143. }
  1144. const setJudgePeriodDiv = document.createElement("div");
  1145. setJudgePeriodDiv.style.padding = "0px 10px 5px 10px";
  1146. setJudgeDiv.appendChild(setJudgePeriodDiv);
  1147. {
  1148. //日
  1149. const setJudgePeriod = document.createElement("input");
  1150. setJudgePeriod.style.marginRight = "5px";
  1151. setJudgePeriod.type = "text";
  1152. setJudgePeriod.style.width = "40px";
  1153. setJudgePeriod.style.height = "18px";
  1154. setJudgePeriod.style.fontSize = "120%";
  1155. setJudgePeriod.value = this.option.judge.period.toString();
  1156. setJudgePeriodDiv.appendChild(setJudgePeriod);
  1157. setJudgePeriod.addEventListener("change", async () => {
  1158. const num = Number(setJudgePeriod.value);
  1159. if (num >= 0) {
  1160. this.option.judge.period = num;
  1161. await this.optionStorage.SetStorageData(this.option);
  1162. }
  1163. else {
  1164. this.option.judge.period = 0;
  1165. setJudgePeriod.value = this.option.judge.period.toString();
  1166. }
  1167. });
  1168. const setJudgePeriodText = document.createElement("span");
  1169. setJudgePeriodText.textContent = "日前のイラストまで対象(0で無限)";
  1170. setJudgePeriodText.style.color = "black";
  1171. setJudgePeriodText.style.fontSize = "15px";
  1172. setJudgePeriodDiv.appendChild(setJudgePeriodText);
  1173. }
  1174. const setAutoNGHiddenDiv = document.createElement("div");
  1175. setAutoNGHiddenDiv.style.padding = "0px 10px";
  1176. setJudgeDiv.appendChild(setAutoNGHiddenDiv);
  1177. {
  1178. const setAutoNGHiddenLabel = document.createElement("label");
  1179. setAutoNGHiddenDiv.appendChild(setAutoNGHiddenLabel);
  1180. {
  1181. const setAutoNGHiddenCheckbox = document.createElement("input");
  1182. setAutoNGHiddenCheckbox.style.padding = "3px";
  1183. setAutoNGHiddenCheckbox.style.marginRight = "5px";
  1184. setAutoNGHiddenCheckbox.type = "checkbox";
  1185. setAutoNGHiddenCheckbox.checked = this.option.judge.isAutoNGHidden;
  1186. setAutoNGHiddenLabel.appendChild(setAutoNGHiddenCheckbox);
  1187. setAutoNGHiddenCheckbox.addEventListener("change", async () => {
  1188. this.option.judge.isAutoNGHidden = setAutoNGHiddenCheckbox.checked;
  1189. for (let info of this.illustInfos) {
  1190. this.UpdateIllust(info);
  1191. this.DrawBlackWhiteButton(info);
  1192. }
  1193. this.UpdateIllustList();
  1194. await this.optionStorage.SetStorageData(this.option);
  1195. });
  1196. const setAutoNGHiddenText = document.createElement("span");
  1197. setAutoNGHiddenText.textContent = "自動NGしたのを非表示にする";
  1198. setAutoNGHiddenText.style.color = "black";
  1199. setAutoNGHiddenText.style.fontSize = "15px";
  1200. setAutoNGHiddenLabel.appendChild(setAutoNGHiddenText);
  1201. }
  1202. }
  1203. judgeRigorCover.style.backgroundColor = "gray";
  1204. judgeRigorCover.style.width = "320px";
  1205. judgeRigorCover.style.height = "100%";
  1206. judgeRigorCover.style.zIndex = "1000";
  1207. judgeRigorCover.style.opacity = "0.5";
  1208. judgeRigorCover.style.position = "absolute";
  1209. judgeRigorCover.style.top = "0";
  1210. setJudgeDiv.appendChild(judgeRigorCover);
  1211. if (this.option.judge.isJudge) {
  1212. judgeRigorCover.style.visibility = "hidden";
  1213. }
  1214. else {
  1215. judgeRigorCover.style.visibility = "visible";
  1216. }
  1217. }
  1218. const otherSettingH5 = document.createElement("h1");
  1219. otherSettingH5.textContent = "その他";
  1220. otherSettingH5.style.fontSize = "140%";
  1221. otherSettingH5.style.marginTop = "10px";
  1222. otherSettingH5.style.color = "black";
  1223. detailDialog.appendChild(otherSettingH5);
  1224. //const setToOKPeriodDiv = document.createElement("div");
  1225. //setToOKPeriodDiv.style.padding = "5px";
  1226. //detailDialog.appendChild(setToOKPeriodDiv);
  1227. //{
  1228. // const setToOKPeriodText1 = document.createElement("div");
  1229. // setToOKPeriodText1.textContent = "取得したなかで最新絵が";
  1230. // setToOKPeriodText1.style.color = "black";
  1231. // setToOKPeriodText1.style.fontSize = "15px";
  1232. // setToOKPeriodDiv.appendChild(setToOKPeriodText1);
  1233. // const setToOKPeriodText2 = document.createElement("div");
  1234. // setToOKPeriodText2.textContent = "これより前のものなら未分類化(0で無効)";
  1235. // setToOKPeriodText2.style.color = "black";
  1236. // setToOKPeriodText2.style.fontSize = "15px";
  1237. // setToOKPeriodDiv.appendChild(setToOKPeriodText2);
  1238. // for (let i = Status.OK; i < Status.MAX; i++) {
  1239. // const setToOKPeriodStatusDiv = document.createElement("div");
  1240. // setToOKPeriodDiv.appendChild(setToOKPeriodStatusDiv);
  1241. // const setToOKPeriodStatusNameText = document.createElement("span");
  1242. // setToOKPeriodStatusNameText.textContent = Status[i]+": ";
  1243. // setToOKPeriodStatusNameText.style.color = "black";
  1244. // setToOKPeriodStatusNameText.style.fontSize = "15px";
  1245. // setToOKPeriodStatusDiv.appendChild(setToOKPeriodStatusNameText);
  1246. // if (i == Status.OK) {
  1247. // const setToOKPeriodStatusOKText = document.createElement("span");
  1248. // setToOKPeriodStatusOKText.textContent = "これが未分類リスト";
  1249. // setToOKPeriodStatusOKText.style.color = "black";
  1250. // setToOKPeriodStatusOKText.style.fontSize = "15px";
  1251. // setToOKPeriodStatusDiv.appendChild(setToOKPeriodStatusOKText);
  1252. // continue;
  1253. // }
  1254. // //日
  1255. // const setToOKPeriod = document.createElement("input");
  1256. // setToOKPeriod.style.marginRight = "5px";
  1257. // setToOKPeriod.type = "text";
  1258. // setToOKPeriod.style.width = "40px";
  1259. // setToOKPeriod.style.height = "5px";
  1260. // setToOKPeriod.style.fontSize = "120%";
  1261. // setToOKPeriod.value = this.option.judge.period.toString();
  1262. // setToOKPeriodStatusDiv.appendChild(setToOKPeriod);
  1263. // setToOKPeriod.addEventListener("change", async () => {
  1264. // const num = Number(setToOKPeriod.value);
  1265. // if (num >= 0) {
  1266. // this.option.judge.period = num;
  1267. // await this.optionStorage.SetStorageData(this.option);
  1268. // } else {
  1269. // this.option.judge.period = 0;
  1270. // setToOKPeriod.value = this.option.judge.period.toString();
  1271. // }
  1272. // });
  1273. // const setToOKPeriodText = document.createElement("span");
  1274. // setToOKPeriodText.textContent = "日";
  1275. // setToOKPeriodText.style.color = "black";
  1276. // setToOKPeriodText.style.fontSize = "15px";
  1277. // setToOKPeriodStatusDiv.appendChild(setToOKPeriodText);
  1278. // }
  1279. //}
  1280. const setOKCacheMaxFlex = document.createElement("div");
  1281. setOKCacheMaxFlex.style.padding = "5px";
  1282. detailDialog.appendChild(setOKCacheMaxFlex);
  1283. {
  1284. const setOKCacheMaxText1 = document.createElement("span");
  1285. setOKCacheMaxText1.textContent = "OKユーザーのキャッシュ最大数:";
  1286. setOKCacheMaxText1.style.color = "black";
  1287. setOKCacheMaxText1.style.fontSize = "15px";
  1288. setOKCacheMaxFlex.appendChild(setOKCacheMaxText1);
  1289. const setOKCacheMax = document.createElement("input");
  1290. setOKCacheMax.type = "number";
  1291. setOKCacheMax.value = this.option.okCacheMax.toString();
  1292. setOKCacheMax.style.width = "80px";
  1293. setOKCacheMax.min = "100";
  1294. setOKCacheMax.style.height = "20px";
  1295. setOKCacheMax.style.fontSize = "120%";
  1296. setOKCacheMax.addEventListener("change", async () => {
  1297. const num = parseInt(setOKCacheMax.value);
  1298. if (num >= 100) {
  1299. this.option.okCacheMax = num;
  1300. await this.optionStorage.SetStorageData(this.option);
  1301. }
  1302. else {
  1303. this.option.okCacheMax = 100;
  1304. setOKCacheMax.value = this.option.okCacheMax.toString();
  1305. }
  1306. });
  1307. setOKCacheMaxFlex.appendChild(setOKCacheMax);
  1308. }
  1309. }
  1310. const list2 = document.createElement("div");
  1311. list2.style.position = "relative";
  1312. list2.style.display = "flex";
  1313. this.optionDialog.appendChild(list2);
  1314. {
  1315. const userInfoList = document.createElement("ul");
  1316. userInfoList.className = "scrollUL";
  1317. userInfoList.style.overflowY = "scroll";
  1318. userInfoList.style.overflowX = "hidden";
  1319. userInfoList.style.height = "400px";
  1320. userInfoList.style.width = "250px";
  1321. list2.appendChild(userInfoList);
  1322. const buttonList = document.createElement("ul");
  1323. buttonList.style.width = "90px";
  1324. list2.appendChild(buttonList);
  1325. {
  1326. const moveButtonList = document.createElement("div");
  1327. moveButtonList.style.marginTop = "20px";
  1328. moveButtonList.style.marginBottom = "10px";
  1329. buttonList.appendChild(moveButtonList);
  1330. {
  1331. for (let i = Status.OK; i < Status.MAX; i++) {
  1332. const div = document.createElement("div");
  1333. moveButtonList.appendChild(div);
  1334. {
  1335. const toButton = document.createElement("input");
  1336. toButton.type = "button";
  1337. toButton.style.padding = "3px";
  1338. toButton.style.fontSize = "130%";
  1339. toButton.style.margin = "3px";
  1340. toButton.value = "→ " + Status[i];
  1341. toButton.name = Status[i];
  1342. div.appendChild(toButton);
  1343. toButton.addEventListener("click", async () => {
  1344. while (this.selectedList.length != 0) {
  1345. const element = this.selectedList.pop();
  1346. if (element == undefined) {
  1347. continue;
  1348. }
  1349. element.style.backgroundColor = "";
  1350. const userId = element.getElementsByClassName("userId")[0].textContent;
  1351. if (userId == undefined) {
  1352. continue;
  1353. }
  1354. const user = this.cache.get(userId);
  1355. if (user != undefined) {
  1356. user.status = Status[toButton.name];
  1357. }
  1358. }
  1359. for (let info of this.illustInfos) {
  1360. this.UpdateIllust(info);
  1361. this.DrawBlackWhiteButton(info);
  1362. }
  1363. this.UpdateIllustList();
  1364. this.DrawList();
  1365. await this.cacheStorage.SetStorageData([...this.cache]);
  1366. });
  1367. }
  1368. }
  1369. }
  1370. const DeleteSelectedUser = () => {
  1371. while (this.selectedList.length != 0) {
  1372. const element = this.selectedList.pop();
  1373. if (element == undefined) {
  1374. continue;
  1375. }
  1376. const userId = element.getElementsByClassName("userId")[0].textContent;
  1377. if (userId == undefined) {
  1378. continue;
  1379. }
  1380. this.cache.delete(userId);
  1381. const infos = this.illustInfos.filter(info => info.userId == userId);
  1382. for (let info of infos) {
  1383. info.user.status = Status.WHITE;
  1384. this.UpdateIllust(info);
  1385. this.DrawBlackWhiteButton(info);
  1386. }
  1387. this.UpdateIllustList();
  1388. this.illustInfos = this.illustInfos.filter(info => info.userId != userId);
  1389. }
  1390. };
  1391. const div = document.createElement("div");
  1392. buttonList.appendChild(div);
  1393. {
  1394. const selectedCacheClearButton = document.createElement("input");
  1395. selectedCacheClearButton.type = "button";
  1396. selectedCacheClearButton.style.padding = "3px";
  1397. selectedCacheClearButton.style.fontSize = "120%";
  1398. selectedCacheClearButton.style.margin = "3px";
  1399. selectedCacheClearButton.style.marginTop = "5px";
  1400. selectedCacheClearButton.style.backgroundColor = "yellow";
  1401. selectedCacheClearButton.style.cssText += "color: black !important";
  1402. selectedCacheClearButton.value = "→ DELETE";
  1403. div.appendChild(selectedCacheClearButton);
  1404. selectedCacheClearButton.addEventListener("click", async () => {
  1405. if (!window.confirm("選択したアイテムのキャッシュクリアしていいですか?\nホワイト・ブラックリストも削除されます。")) {
  1406. return;
  1407. }
  1408. DeleteSelectedUser();
  1409. this.DrawList();
  1410. await this.cacheStorage.SetStorageData([...this.cache]);
  1411. });
  1412. }
  1413. const div2 = document.createElement("div");
  1414. buttonList.appendChild(div2);
  1415. {
  1416. const allCacheClearButton = document.createElement("input");
  1417. allCacheClearButton.type = "button";
  1418. allCacheClearButton.style.padding = "3px";
  1419. allCacheClearButton.style.fontSize = "120%";
  1420. allCacheClearButton.style.margin = "3px";
  1421. allCacheClearButton.style.backgroundColor = "red";
  1422. allCacheClearButton.value = "ALL DELETE";
  1423. div2.appendChild(allCacheClearButton);
  1424. allCacheClearButton.addEventListener("click", async () => {
  1425. if (!window.confirm("全キャッシュクリアしていいですか?\nホワイト・ブラックリストも削除されます。")) {
  1426. return;
  1427. }
  1428. for (let info of this.illustInfos) {
  1429. info.user.status = Status.WHITE;
  1430. this.UpdateIllust(info);
  1431. this.DrawBlackWhiteButton(info);
  1432. }
  1433. this.illustInfos = [];
  1434. this.UpdateIllustList();
  1435. this.cache.clear();
  1436. this.DrawList();
  1437. await this.cacheStorage.SetStorageData([...this.cache]);
  1438. });
  1439. }
  1440. const div3 = document.createElement("div");
  1441. buttonList.appendChild(div3);
  1442. {
  1443. const reStartButton = document.createElement("input");
  1444. reStartButton.type = "button";
  1445. reStartButton.style.padding = "3px";
  1446. reStartButton.style.fontSize = "120%";
  1447. reStartButton.style.margin = "3px";
  1448. reStartButton.style.marginTop = "10px";
  1449. reStartButton.style.backgroundColor = "green";
  1450. reStartButton.style.cssText += "color: white !important";
  1451. reStartButton.value = "RE START";
  1452. div3.appendChild(reStartButton);
  1453. reStartButton.addEventListener("click", async () => {
  1454. await this.Run();
  1455. });
  1456. }
  1457. const div4 = document.createElement("div");
  1458. div4.style.marginTop = "10px";
  1459. div4.style.marginBottom = "10px";
  1460. buttonList.appendChild(div4);
  1461. {
  1462. const importDiv = document.createElement("div");
  1463. importDiv.style.position = "relative";
  1464. div4.appendChild(importDiv);
  1465. {
  1466. const importButton = document.createElement("input");
  1467. importButton.type = "button";
  1468. importButton.style.padding = "3px";
  1469. importButton.style.fontSize = "120%";
  1470. importButton.style.margin = "3px";
  1471. importButton.style.marginTop = "10px";
  1472. importButton.value = "← IMPORT";
  1473. importDiv.appendChild(importButton);
  1474. const importFile = document.createElement("input");
  1475. importFile.type = "file";
  1476. importFile.style.position = "absolute";
  1477. importFile.style.opacity = "0";
  1478. importFile.style.width = "80px";
  1479. importFile.style.top = "8px";
  1480. importFile.style.left = "0";
  1481. importFile.accept = "text/plain";
  1482. importFile.style.padding = "0";
  1483. importDiv.appendChild(importFile);
  1484. importFile.addEventListener("change", async (e) => {
  1485. if (e.target == null) {
  1486. return;
  1487. }
  1488. const files = e.target.files;
  1489. if (files == null) {
  1490. return;
  1491. }
  1492. const file = files[0];
  1493. if (file.type != "text/plain") {
  1494. alert("テキストファイルを入れてください。");
  1495. return;
  1496. }
  1497. if (!window.confirm("インポートしていいですか?\nインポートする前に、今選択しているユーザーは削除されます。")) {
  1498. return;
  1499. }
  1500. DeleteSelectedUser();
  1501. this.DrawList();
  1502. const reader = new FileReader();
  1503. reader.readAsText(file);
  1504. reader.onload = async () => {
  1505. if (typeof reader.result != "string") {
  1506. return;
  1507. }
  1508. const importUsers = this.ListToMap(JSON.parse(reader.result));
  1509. for (let [imUserId, imUser] of importUsers) {
  1510. for (let illust of imUser.illusts) {
  1511. illust.created = new Date(illust.created);
  1512. }
  1513. }
  1514. for (let [imUserId, imUser] of importUsers) {
  1515. if (imUser == null) {
  1516. continue;
  1517. }
  1518. const cachedUser = this.cache.get(imUserId);
  1519. if (cachedUser == undefined) {
  1520. this.cache.set(imUserId, imUser);
  1521. }
  1522. else {
  1523. cachedUser.status = imUser.status;
  1524. for (let illust of cachedUser.illusts) {
  1525. if (cachedUser.illusts.some(c => c.illustId == illust.illustId)) {
  1526. continue;
  1527. }
  1528. if (illust == null) {
  1529. continue;
  1530. }
  1531. cachedUser.illusts.push(illust);
  1532. }
  1533. }
  1534. }
  1535. await this.cacheStorage.SetStorageData([...this.cache]);
  1536. this.Run();
  1537. };
  1538. });
  1539. }
  1540. const exportEiv = document.createElement("div");
  1541. div4.appendChild(exportEiv);
  1542. {
  1543. const reStartButton = document.createElement("input");
  1544. reStartButton.type = "button";
  1545. reStartButton.style.padding = "3px";
  1546. reStartButton.style.fontSize = "120%";
  1547. reStartButton.style.margin = "3px";
  1548. reStartButton.style.marginTop = "5px";
  1549. reStartButton.value = "→ EXPORT";
  1550. exportEiv.appendChild(reStartButton);
  1551. reStartButton.addEventListener("click", async () => {
  1552. const selectedUsers = new Map();
  1553. for (let element of this.selectedList) {
  1554. if (element == undefined) {
  1555. continue;
  1556. }
  1557. const userId = element.getElementsByClassName("userId")[0].textContent;
  1558. if (userId == null) {
  1559. continue;
  1560. }
  1561. const user = this.cache.get(userId);
  1562. if (user != undefined) {
  1563. selectedUsers.set(userId, user);
  1564. }
  1565. }
  1566. if (selectedUsers.size == 0) {
  1567. alert("出力するユーザーを選択してください");
  1568. return;
  1569. }
  1570. const listStatusSelect = this.optionDialog.getElementsByClassName("listStatusSelect")[0];
  1571. const status = listStatusSelect.value;
  1572. const blob = new Blob([JSON.stringify(selectedUsers)], { type: "text/plain" });
  1573. const dlUrl = URL.createObjectURL(blob);
  1574. await Util.Download(dlUrl, `niconicoNG_${status}.txt`);
  1575. });
  1576. }
  1577. }
  1578. }
  1579. }
  1580. }
  1581. }
  1582. }
  1583. UpdateIllust(info) {
  1584. const img = info.element.getElementsByTagName("img")[0] ?? info.element.getElementsByClassName("image-layer")[0];
  1585. if (info.user.status == Status.OK || info.user.status == Status.WHITE) {
  1586. if (img != null) {
  1587. img.style.filter = "brightness(1)";
  1588. }
  1589. if (info.element.parentElement == null) {
  1590. info.parent.appendChild(info.element);
  1591. }
  1592. }
  1593. if (info.user.status == Status.NG || (info.user.status == Status.AUTO && !this.option.judge.isAutoNGHidden)) {
  1594. if (img != null) {
  1595. img.style.filter = "brightness(0.3)";
  1596. }
  1597. info.parent.appendChild(info.element);
  1598. }
  1599. if (info.user.status == Status.BLACK || (info.user.status == Status.AUTO && this.option.judge.isAutoNGHidden)) {
  1600. info.element.remove();
  1601. }
  1602. }
  1603. UpdateIllustList() {
  1604. for (let illustListElement of this.illustListElements) {
  1605. if (this.currentPage == VirtualPageType.ILLUST) {
  1606. for (let moreLink of Array.from(illustListElement.getElementsByClassName("list_more_link"))) {
  1607. if (moreLink.parentElement == null) {
  1608. continue;
  1609. }
  1610. moreLink.parentElement.appendChild(moreLink);
  1611. }
  1612. }
  1613. if (this.currentPage == VirtualPageType.KEYWORD_SEARCH) {
  1614. const brs = illustListElement.getElementsByTagName("br");
  1615. while (brs.length) {
  1616. brs[0].remove();
  1617. }
  1618. for (var i = 0; i < illustListElement.childElementCount; i++) {
  1619. if ((i % 6) != 4) {
  1620. continue;
  1621. }
  1622. illustListElement.children[i].insertAdjacentHTML("afterend", "<br clear='all'>");
  1623. }
  1624. illustListElement.insertAdjacentHTML("beforeend", "<br clear='all'>");
  1625. }
  1626. if (this.currentPage == VirtualPageType.PERSONALIZE) {
  1627. const brs = Array.from(illustListElement.getElementsByTagName("br"));
  1628. for (let br of brs) {
  1629. br.remove();
  1630. }
  1631. const items = Array.from(illustListElement.getElementsByClassName(virtualPageInfos[this.currentPage].illustItemName));
  1632. for (let i = 0; i < items.length; i++) {
  1633. if ((i + 1) % 4 == 0 || i == items.length - 1) {
  1634. const br = document.createElement("br");
  1635. br.clear = "all";
  1636. items[i].insertAdjacentElement("afterend", br);
  1637. }
  1638. }
  1639. }
  1640. if (this.currentPage == VirtualPageType.ADS) {
  1641. const ds = illustListElement.getElementsByClassName("ADS_Dammy");
  1642. while (ds.length)
  1643. ds[0].remove();
  1644. if (1 < illustListElement.childElementCount) {
  1645. for (var i = illustListElement.childElementCount; i < 3; i++) {
  1646. const dammy = document.createElement("div");
  1647. dammy.classList.add("ADS_Dammy");
  1648. dammy.style.width = illustListElement.children[0].clientWidth + "px";
  1649. illustListElement.appendChild(dammy);
  1650. }
  1651. }
  1652. }
  1653. }
  1654. }
  1655. CreateUserLink(illustInfo) {
  1656. if (this.currentPage == VirtualPageType.PERSONALIZE || !this.option.createUserLink || illustInfo.element.getElementsByClassName("userLink").length > 0) {
  1657. return;
  1658. }
  1659. const userElement = illustInfo.element.getElementsByClassName("user")[0];
  1660. if (userElement == null) {
  1661. return;
  1662. }
  1663. const userA = document.createElement("a");
  1664. userA.className = "userLink";
  1665. userA.href = "https://seiga.nicovideo.jp/user/illust/" + illustInfo.userId;
  1666. userA.style.left = "0";
  1667. userA.style.zIndex = "10";
  1668. userA.style.right = "10px";
  1669. userA.style.position = "absolute";
  1670. userA.style.border = "0";
  1671. userA.style.opacity = "0";
  1672. userA.addEventListener("mouseover", () => {
  1673. userA.style.border = "solid 1px silver";
  1674. userA.style.opacity = "0.3";
  1675. });
  1676. userA.addEventListener("mouseleave", () => {
  1677. userA.style.border = "0";
  1678. userA.style.opacity = "0";
  1679. });
  1680. if (this.currentPage == VirtualPageType.TAG_SEARCH) {
  1681. userA.style.height = "10px";
  1682. userA.style.top = "34px";
  1683. userA.style.backgroundColor = "silver";
  1684. }
  1685. if (this.currentPage == VirtualPageType.ILLUST) {
  1686. userA.style.height = "20px";
  1687. userA.style.top = "20px";
  1688. userA.style.backgroundColor = "black";
  1689. }
  1690. userElement.style.position = "relative";
  1691. userElement.style.zIndex = "20";
  1692. userElement.style.pointerEvents = "none";
  1693. userElement.insertAdjacentElement("beforebegin", userA);
  1694. }
  1695. async DrawBlackWhiteButton(illustInfo) {
  1696. if (!this.option.createBlackWhiteButton) {
  1697. return;
  1698. }
  1699. if (illustInfo.user.status == Status.BLACK || illustInfo.user.status == Status.WHITE) {
  1700. if (illustInfo.user.status == Status.WHITE) {
  1701. const list = Array.from(illustInfo.element.getElementsByClassName("toListButton"));
  1702. for (let l of list) {
  1703. l.remove();
  1704. }
  1705. }
  1706. return;
  1707. }
  1708. if (illustInfo.element.getElementsByClassName("toListButton").length > 0) {
  1709. return;
  1710. }
  1711. const whiteButton = document.createElement("input");
  1712. const blackButton = document.createElement("input");
  1713. whiteButton.style.zIndex = "20";
  1714. whiteButton.style.visibility = "hidden";
  1715. whiteButton.style.cursor = "default";
  1716. if (this.currentPage == VirtualPageType.TAG_SEARCH) {
  1717. whiteButton.style.left = "117px";
  1718. whiteButton.style.top = "-30px";
  1719. whiteButton.style.width = "40px";
  1720. whiteButton.style.height = "25px";
  1721. whiteButton.style.position = "relative";
  1722. }
  1723. if (this.currentPage == VirtualPageType.ILLUST) {
  1724. whiteButton.style.left = "54px";
  1725. whiteButton.style.top = "-19px";
  1726. whiteButton.style.width = "30px";
  1727. whiteButton.style.height = "19px";
  1728. whiteButton.style.position = "relative";
  1729. }
  1730. if (this.currentPage == VirtualPageType.PERSONALIZE) {
  1731. whiteButton.style.top = "240px";
  1732. whiteButton.style.width = "40px";
  1733. whiteButton.style.height = "25px";
  1734. whiteButton.style.position = "absolute";
  1735. illustInfo.element.style.position = "relative";
  1736. illustInfo.element.style.height = "258px";
  1737. }
  1738. if (this.currentPage == VirtualPageType.ADS) {
  1739. whiteButton.style.top = "85px";
  1740. whiteButton.style.width = "30px";
  1741. whiteButton.style.height = "19px";
  1742. whiteButton.style.position = "absolute";
  1743. whiteButton.style.border = "2px solid #736b5e";
  1744. illustInfo.element.style.position = "relative";
  1745. }
  1746. if (this.currentPage == VirtualPageType.KEYWORD_SEARCH) {
  1747. whiteButton.style.top = "144px";
  1748. whiteButton.style.width = "30px";
  1749. whiteButton.style.height = "19px";
  1750. whiteButton.style.position = "absolute";
  1751. illustInfo.element.style.position = "relative";
  1752. }
  1753. //上記のスタイルを両方に適用
  1754. blackButton.style.cssText = whiteButton.style.cssText;
  1755. whiteButton.type = "button";
  1756. blackButton.type = "button";
  1757. whiteButton.className = "toListButton";
  1758. blackButton.className = "toListButton";
  1759. whiteButton.name = "white";
  1760. blackButton.name = "black";
  1761. whiteButton.style.cssText += `background-color : white !important;`;
  1762. blackButton.style.cssText += `background-color : black !important;`;
  1763. if (this.currentPage == VirtualPageType.PERSONALIZE) {
  1764. whiteButton.style.left = "77px";
  1765. blackButton.style.left = "117px";
  1766. }
  1767. if (this.currentPage == VirtualPageType.ADS) {
  1768. whiteButton.style.left = "135px";
  1769. blackButton.style.left = "165px";
  1770. }
  1771. if (this.currentPage == VirtualPageType.KEYWORD_SEARCH) {
  1772. whiteButton.style.right = "28px";
  1773. blackButton.style.right = "-2px";
  1774. }
  1775. whiteButton.addEventListener("contextmenu", async (e) => {
  1776. e.preventDefault();
  1777. illustInfo.user.status = Status.OK;
  1778. for (let info of this.illustInfos) {
  1779. this.UpdateIllust(info);
  1780. }
  1781. this.UpdateIllustList();
  1782. this.DrawList();
  1783. await this.cacheStorage.SetStorageData([...this.cache]);
  1784. });
  1785. whiteButton.addEventListener("click", async () => {
  1786. illustInfo.user.status = Status.WHITE;
  1787. for (let info of this.illustInfos) {
  1788. this.UpdateIllust(info);
  1789. const buttons = info.element.getElementsByClassName("toListButton");
  1790. while (buttons.length != 0) {
  1791. buttons[0].remove();
  1792. }
  1793. }
  1794. this.UpdateIllustList();
  1795. this.DrawList();
  1796. await this.cacheStorage.SetStorageData([...this.cache]);
  1797. });
  1798. blackButton.addEventListener("contextmenu", async (e) => {
  1799. e.preventDefault();
  1800. illustInfo.user.status = Status.NG;
  1801. for (let info of this.illustInfos) {
  1802. this.UpdateIllust(info);
  1803. }
  1804. this.UpdateIllustList();
  1805. this.DrawList();
  1806. await this.cacheStorage.SetStorageData([...this.cache]);
  1807. });
  1808. blackButton.addEventListener("click", async () => {
  1809. illustInfo.user.status = Status.BLACK;
  1810. for (let info of this.illustInfos) {
  1811. this.UpdateIllust(info);
  1812. }
  1813. this.UpdateIllustList();
  1814. this.DrawList();
  1815. await this.cacheStorage.SetStorageData([...this.cache]);
  1816. });
  1817. if (this.currentPage == VirtualPageType.TAG_SEARCH) {
  1818. const infoElement = illustInfo.element.getElementsByClassName("illust_count")[0];
  1819. blackButton.addEventListener("mouseover", () => {
  1820. infoElement.style.opacity = "1";
  1821. });
  1822. blackButton.addEventListener("mouseleave", () => {
  1823. infoElement.style.opacity = "";
  1824. });
  1825. whiteButton.addEventListener("mouseover", () => {
  1826. infoElement.style.opacity = "1";
  1827. });
  1828. whiteButton.addEventListener("mouseleave", () => {
  1829. infoElement.style.opacity = "";
  1830. });
  1831. }
  1832. if (this.currentPage == VirtualPageType.ILLUST) {
  1833. const infoElement = illustInfo.element.getElementsByClassName("illust_info")[0];
  1834. blackButton.addEventListener("mouseover", () => {
  1835. infoElement.style.bottom = "0px";
  1836. });
  1837. blackButton.addEventListener("mouseleave", () => {
  1838. infoElement.style.bottom = "";
  1839. });
  1840. whiteButton.addEventListener("mouseover", () => {
  1841. infoElement.style.bottom = "0px";
  1842. });
  1843. whiteButton.addEventListener("mouseleave", () => {
  1844. infoElement.style.bottom = "";
  1845. });
  1846. }
  1847. illustInfo.element.addEventListener("mouseover", () => {
  1848. blackButton.style.visibility = "visible";
  1849. whiteButton.style.visibility = "visible";
  1850. });
  1851. illustInfo.element.addEventListener("touchstart", () => {
  1852. blackButton.style.visibility = "visible";
  1853. whiteButton.style.visibility = "visible";
  1854. });
  1855. illustInfo.element.addEventListener("mouseleave", () => {
  1856. blackButton.style.visibility = "hidden";
  1857. whiteButton.style.visibility = "hidden";
  1858. });
  1859. illustInfo.element.addEventListener("touchend", () => {
  1860. blackButton.style.visibility = "hidden";
  1861. whiteButton.style.visibility = "hidden";
  1862. });
  1863. if (this.currentPage == VirtualPageType.ADS) {
  1864. illustInfo.element.insertAdjacentElement("afterbegin", blackButton);
  1865. illustInfo.element.insertAdjacentElement("afterbegin", whiteButton);
  1866. return;
  1867. }
  1868. illustInfo.element.appendChild(whiteButton);
  1869. illustInfo.element.appendChild(blackButton);
  1870. }
  1871. async AddInfos(illustListElement) {
  1872. const illustItemName = virtualPageInfos[this.currentPage].illustItemName;
  1873. let illustElements;
  1874. if (this.currentPage == VirtualPageType.KEYWORD_SEARCH || this.currentPage == VirtualPageType.PERSONALIZE) {
  1875. illustElements = Array.from(illustListElement.getElementsByClassName(illustItemName));
  1876. }
  1877. else {
  1878. illustElements = Array.from(illustListElement.getElementsByTagName("li"))
  1879. .filter(e => illustItemName == "" || e.classList.contains(illustItemName) || e.firstElementChild?.classList.contains(illustItemName));
  1880. }
  1881. const illustIds = this.GetIllustIds(illustListElement);
  1882. const names = Array.from(illustListElement.getElementsByClassName("user"));
  1883. //console.log(illustIds);
  1884. //console.log(illustElements);
  1885. //console.log(names);
  1886. //キャッシュからの情報と合わせて追加(もうこれ分かんねぇこともある)
  1887. for (let i = 0; i < illustIds.length; i++) {
  1888. if (this.illustInfos.some(info => info.illust.illustId == illustIds[i] && info.element == illustElements[i])) {
  1889. continue;
  1890. }
  1891. if (illustElements[i] == null) {
  1892. continue;
  1893. }
  1894. const info = this.GetInfo(illustIds[i]);
  1895. if (info != undefined && names[i]?.textContent != null) {
  1896. info.user.name = names[i].textContent ?? info.user.name;
  1897. }
  1898. this.illustInfos.push({
  1899. userId: info == undefined ? "" : info.userId,
  1900. illust: info == undefined ? { created: "", illustId: illustIds[i] } : info.illust,
  1901. user: info == undefined ? { illusts: [], status: Status.NONE, name: names[i]?.textContent ?? "" } : info.user,
  1902. element: illustElements[i],
  1903. parent: illustListElement
  1904. });
  1905. }
  1906. }
  1907. SetCurrentPage(url) {
  1908. for (let i = 0; i < pageInfos.length; i++) {
  1909. if (pageInfos[i].regex.test(url)) {
  1910. this.currentPage = pageInfos[i].type;
  1911. return this.option.usePages[i];
  1912. }
  1913. }
  1914. this.currentPage = VirtualPageType.None;
  1915. return false;
  1916. }
  1917. GetPage() {
  1918. return this.currentPage;
  1919. }
  1920. //メインクラス、メイン関数の肥大化もう始まってる!
  1921. async Run(illustListElements) {
  1922. const illustListName = virtualPageInfos[this.currentPage].illustListName;
  1923. let firstIllustListElement;
  1924. if (illustListName == "") {
  1925. firstIllustListElement = await Observer.DefinitelyGetElementByTagName("ul", undefined, { isDeepSearch: true });
  1926. }
  1927. else {
  1928. firstIllustListElement = await Observer.DefinitelyGetElementByClassName(illustListName, undefined, { isDeepSearch: true });
  1929. }
  1930. if (this.currentPage == VirtualPageType.ADS) {
  1931. await Observer.DefinitelyGetElementByTagName("li", undefined, { isDeepSearch: true });
  1932. if (firstIllustListElement) {
  1933. firstIllustListElement.style.visibility = "hidden";
  1934. firstIllustListElement.style.overflow = "hidden";
  1935. }
  1936. }
  1937. await Util.WithTimeOut(Observer.DefinitelyGetElementById("footer"), 1000);
  1938. if (illustListElements == null) {
  1939. if (illustListName == "") {
  1940. illustListElements = Array.from(document.getElementsByTagName("ul"));
  1941. }
  1942. else {
  1943. illustListElements = Array.from(document.getElementsByClassName(illustListName));
  1944. }
  1945. }
  1946. for (let illustListElement of illustListElements) {
  1947. illustListElement.style.visibility = "hidden";
  1948. await this.AddInfos(illustListElement);
  1949. this.illustListElements.add(illustListElement);
  1950. }
  1951. //console.log("infos", this.illustInfos, this.illustInfos.length);
  1952. //誰のイラストかこれもう分かんねぇやつ達
  1953. const unkownInfos = this.illustInfos.filter(info => info.userId == "");
  1954. //console.log("unkownInfos", unkownInfos);
  1955. //この戻り値なんかダサい・・・ダサくない?
  1956. const result = await Fetcher.FetchIllustDatas(unkownInfos.map(info => info.illust.illustId));
  1957. //誰のかこれもう分かんねぇやつらとキャッシュまで!?の情報更新
  1958. for (let i = 0; i < unkownInfos.length; i++) {
  1959. if (result.illusts[i] == null) {
  1960. //alert("null!!!");
  1961. //console.log(result);
  1962. //debugger;
  1963. continue;
  1964. }
  1965. unkownInfos[i].illust = result.illusts[i];
  1966. unkownInfos[i].userId = result.userIds[i];
  1967. let cachedUser = this.cache.get(result.userIds[i]);
  1968. if (cachedUser == undefined) {
  1969. if (unkownInfos[i].user == null) {
  1970. //alert("null!!!");
  1971. //console.log(unkownInfos);
  1972. //debugger;
  1973. continue;
  1974. }
  1975. unkownInfos[i].user.status = Status.OK;
  1976. this.cache.set(unkownInfos[i].userId, unkownInfos[i].user);
  1977. }
  1978. else {
  1979. ////キャッシュ使ったら後ろにしとく
  1980. this.cache.delete(result.userIds[i]);
  1981. this.cache.set(result.userIds[i], cachedUser);
  1982. unkownInfos[i].user = cachedUser;
  1983. }
  1984. if (!unkownInfos[i].user.illusts.some(illust => illust.illustId == result.illusts[i].illustId)) {
  1985. unkownInfos[i].user.illusts.push(result.illusts[i]);
  1986. }
  1987. }
  1988. // IDが見つからないものを削除する
  1989. this.illustInfos = this.illustInfos.filter(info => info.userId != "");
  1990. //増えすぎたキャッシュ削除
  1991. if (this.cache.size > 0) {
  1992. let okCount = 0;
  1993. for (let [userId, user] of this.cache) {
  1994. if (user.status == Status.OK) {
  1995. okCount++;
  1996. }
  1997. }
  1998. for (let [userId, user] of this.cache) {
  1999. if (okCount < this.option.okCacheMax) {
  2000. break;
  2001. }
  2002. //OK以外消さない
  2003. //今使ってたら消さない
  2004. if (user.status == Status.OK && !this.illustInfos.some(info => info.userId == userId)) {
  2005. this.cache.delete(userId);
  2006. okCount--;
  2007. }
  2008. }
  2009. }
  2010. //console.log(result);
  2011. //ブラック,ホワイトリストにないイラストエレメントにボタン追加
  2012. for (let illustInfo of this.illustInfos) {
  2013. this.DrawBlackWhiteButton(illustInfo);
  2014. this.CreateUserLink(illustInfo);
  2015. }
  2016. if (this.option.judge.isJudge) {
  2017. //投稿者の荒らし判定更新 ↓これは重複排除
  2018. for (let c of [...new Set(this.illustInfos.map(u => u.user))]) {
  2019. this.CheckAutoNG(c);
  2020. }
  2021. }
  2022. await this.cacheStorage.SetStorageData([...this.cache]);
  2023. for (let info of this.illustInfos) {
  2024. this.UpdateIllust(info);
  2025. }
  2026. this.UpdateIllustList();
  2027. for (let illustListElement of illustListElements) {
  2028. illustListElement.style.visibility = "visible";
  2029. }
  2030. await this.SetOptionButton();
  2031. this.DrawList();
  2032. }
  2033. async StartObserve() {
  2034. const illustListName = virtualPageInfos[this.currentPage].illustListName;
  2035. if (illustListName == "") {
  2036. return;
  2037. }
  2038. const illustListParent = (await Observer.DefinitelyGetElementByClassName(illustListName)).parentNode;
  2039. const mutationObserver = new MutationObserver(async (mrs) => {
  2040. for (let mr of mrs) {
  2041. for (let i = 0; i < mr.addedNodes.length; i++) {
  2042. const element = mr.addedNodes[i];
  2043. if (element.classList == null) {
  2044. continue;
  2045. }
  2046. if (element.classList.contains(illustListName)) {
  2047. await this.Run([element]);
  2048. }
  2049. }
  2050. }
  2051. });
  2052. mutationObserver.observe(illustListParent ?? document, {
  2053. childList: true,
  2054. subtree: true
  2055. });
  2056. }
  2057. }
  2058. ;
  2059. const main = new Main();
  2060. await main.GetStorageData();
  2061. const isUseNG = main.SetCurrentPage(location.href);
  2062. await Util.WaitDocumentElement();
  2063. main.SetOptionButton();
  2064. if (!isUseNG) {
  2065. return;
  2066. }
  2067. await main.Run();
  2068. await main.StartObserve();
  2069. })();