Greasy Fork 支持简体中文。

meatmemo

わかめて上で動作するわかめてメモのようなものです。

  1. // ==UserScript==
  2. // @name meatmemo
  3. // @namespace http://mobajinro.s178.xrea.com/wakamemo/
  4. // @version 2.0.0
  5. // @description わかめて上で動作するわかめてメモのようなものです。
  6. // @author udop_
  7. // @match http://jinrou.dip.jp/~jinrou/cgi_jinro.cgi
  8. // @match http://61.215.66.131/~jinrou/cgi_jinro.cgi
  9. // @match http://www7a.biglobe.ne.jp/~kuri/cgi_jinro.cgi
  10. // @require https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
  11. // @run-at document-start
  12. // ==/UserScript==
  13.  
  14. ;(function ($) {
  15. "use strict";
  16. Array.prototype.fillundef = function (def, lastindex) {
  17. lastindex = lastindex + 1 || this.length;
  18. for (let i = 0; i < lastindex; i++) {
  19. if (this[i] === null) {
  20. this[i] = JSON.parse(JSON.stringify(def));
  21. }
  22. }
  23. };
  24. function createSelectBox(option, selected, attr) {
  25. let attrtext = "";
  26. for (let k in attr) {
  27. attrtext = attrtext + ` ${k}="${attr[k]}"`;
  28. }
  29. let s = `<select${attrtext}>`;
  30. for (let i in option) {
  31. let issl = i == selected ? "selected" : "";
  32. s += `<option value="${i}" ${issl}>${option[i]}</option>`;
  33. }
  34. s += "</select>";
  35. return s;
  36. }
  37. var COLORLIST = {
  38. 0: "選択▼",
  39. 1: "明灰",
  40. 2: "暗灰",
  41. 3: "黄色",
  42. 4: "オレンジ",
  43. 5: "赤",
  44. 6: "水色",
  45. 7: "青",
  46. 8: "黄緑",
  47. 9: "紫",
  48. 10: "桃色",
  49. 11: "肌色",
  50. 12: "茶色",
  51. 13: "緑",
  52. 14: "若草色",
  53. 15: "真紅",
  54. 16: "薄茶色",
  55. 17: "藍色",
  56. 18: "蒼",
  57. 19: "ピンク",
  58. 20: "銀色",
  59. 21: "薄紫",
  60. 22: "象牙色",
  61. 23: "黒",
  62. };
  63. const joblist = {
  64. gray: "",
  65. fortune: "占い",
  66. necro: "霊能",
  67. share: "共有",
  68. guard: "狩人",
  69. cat: "猫又",
  70. beast: "人外",
  71. };
  72. const reasoninglist = {
  73. gray: "",
  74. real: "真",
  75. fake: "偽",
  76. villager: "村人",
  77. madman: "狂人",
  78. wolf: "人狼",
  79. fox: "妖狐",
  80. };
  81. const resultlist = { notinput: "", white: "○", black: "●" };
  82. const jobinitial = {
  83. fortune: "占",
  84. necro: "霊",
  85. share: "共",
  86. cat: "猫",
  87. guard: "狩",
  88. wolf: "狼",
  89. madman: "狂",
  90. fox: "狐",
  91. villager: "村",
  92. real: "真",
  93. fake: "偽",
  94. beast: "外",
  95. gray: "",
  96. };
  97. const themeList = {
  98. crimson: { main: "crimson", sub: "#EC365A" },
  99. darkorange: { main: "darkorange", sub: "#FF9F32" },
  100. darkgreen: { main: "darkgreen", sub: "#009300" },
  101. navy: { main: "navy", sub: "#0000b2" },
  102. purple: { main: "purple", sub: "#B200B2" },
  103. sienna: { main: "sienna", sub: "#C66538" },
  104. };
  105. const reasonflavor = { bite: "無残", note: "デスノ", exec: "処刑", sudden: "突然死" };
  106. const settingDefault = {
  107. rewrite_css: {
  108. value: "yes",
  109. option: { yes: "はい", no: "いいえ" },
  110. name: "見た目を変更する",
  111. },
  112. auto_import_log: {
  113. value: "onetime",
  114. option: { none: "しない", onetime: "投票時", alltime: "常時" },
  115. name: "自動ログ取得のタイミング",
  116. },
  117. alert_vote: {
  118. value: "yes",
  119. name: "未投票時に警告する",
  120. option: { yes: "はい", no: "いいえ" },
  121. },
  122. send_support: {
  123. value: "ctrl",
  124. name: "支援キー+ENTERで送信",
  125. option: { none: "しない", ctrl: "CTRL", shift: "SHIFT" },
  126. },
  127. autoreload_interval: {
  128. value: "30",
  129. name: "自動更新(観戦時のみ)",
  130. option: { 10: "10秒", 20: "20秒", 30: "30秒", 60: "60分" },
  131. },
  132. theme_color: {
  133. value: "navy",
  134. name: "テーマカラー",
  135. option: {
  136. crimson: "紅",
  137. darkorange: "オレンジ",
  138. darkgreen: "緑",
  139. navy: "蒼",
  140. purple: "紫",
  141. sienna: "茶",
  142. },
  143. },
  144. layout: {
  145. value: "no",
  146. name: "表を横に並べる(横幅と相談)",
  147. option: { yes: "はい", no: "いいえ" },
  148. },
  149. grayregion: {
  150. value: "no",
  151. name: "人外と推理した占い師の結果は<br>完グレ判定に使用しない",
  152. option: { yes: "はい", no: "いいえ" },
  153. },
  154. coloringName: {
  155. value: "no",
  156. name: "役職で色分けする(試験運用)",
  157. option: { yes: "はい", no: "いいえ" },
  158. },
  159. };
  160. const nameColor = {
  161. black: "なし",
  162. red: "赤",
  163. pink: "ピンク",
  164. blue: "青",
  165. green: "緑",
  166. purple: "紫",
  167. brown: "茶",
  168. gaming: "虹色",
  169. };
  170. const colorSettingDefault = {
  171. gray: {
  172. value: "black",
  173. name: "グレー",
  174. option: nameColor,
  175. },
  176. fortune: {
  177. value: "black",
  178. name: "占い",
  179. option: nameColor,
  180. },
  181. necro: {
  182. value: "black",
  183. name: "霊能",
  184. option: nameColor,
  185. },
  186. share: {
  187. value: "black",
  188. name: "共有",
  189. option: nameColor,
  190. },
  191. guard: {
  192. value: "black",
  193. name: "狩人",
  194. option: nameColor,
  195. },
  196. cat: {
  197. value: "black",
  198. name: "猫又",
  199. option: nameColor,
  200. },
  201. beast: {
  202. value: "black",
  203. name: "人外",
  204. option: nameColor,
  205. },
  206. };
  207. class Tr {
  208. constructor(id, cl) {
  209. this.id = id ? `id="${id}"` : "";
  210. this.cl = cl ? `class="${cl}"` : "";
  211. this.tds = [];
  212. }
  213. add(val, cl, colspan) {
  214. let c = cl ? `class='${cl}'` : "";
  215. let d = colspan ? `colspan=${colspan}` : "";
  216. let td = `<td ${c} ${d}>${val}</td>`;
  217. this.tds.push(td);
  218. }
  219. text() {
  220. let tds = this.tds.join("");
  221. let tr = `<tr ${this.id} ${this.cl}>${tds}</tr>`;
  222. return tr;
  223. }
  224. appendTo(jQueryObject) {
  225. jQueryObject.append(this.text());
  226. }
  227. }
  228. class MeatMemo {
  229. constructor(serverName) {
  230. this.serverName = serverName || "wakamete";
  231. this.playerManager = new PlayerManager(this);
  232. this.log = new LogManager(this);
  233. this.setting = new Setting(this);
  234. this.colorSetting = new ColorSetting(this);
  235. this.random = new Random(this);
  236. this.style = new Style(this);
  237. this.utility = new Utility(this);
  238. this.filterSetting = { show: "All", input: "simple" };
  239. this.isAutoReload = false;
  240. this.newestImportDay = 0;
  241. this.init();
  242. }
  243. init() {
  244. this.load();
  245. this.prepare();
  246. this.setting.init();
  247. this.colorSetting.init();
  248. this.style.injection();
  249. this.random.init();
  250. $(() => {
  251. this.on();
  252. this.utility.init();
  253. });
  254. }
  255. get playerNum() {
  256. this.playerManager.import();
  257. return this.playerManager.list.length;
  258. }
  259. settingIs(key, value = "yes") {
  260. return this.setting.options[key].value == value;
  261. }
  262. settingValue(key) {
  263. return this.setting.options[key].value;
  264. }
  265. prepare() {
  266. let container = `
  267. <div id="memoContainer">
  268. <div id="memoMenu">
  269. <div class='button' id='importButton'>ログの取り込み</div>
  270. <div class='button' id='resetButton'>リセット</div>
  271. <div class='button' id='reloadButton'>更新</div>
  272. </div>
  273. <div id="memoTab">
  274. <div class='tab active' data-value='log'>発言ログ</div>
  275. <div class='tab' data-value='vote'>投票履歴</div>
  276. </div>
  277. <div id="memoBody">
  278. <div id="logArea">
  279. <div id="playerInfoArea">
  280. <table id="playerInfoTable"></table>
  281. </div>
  282. <div id="buttonArea">
  283. <div>絞り込み
  284. <div class='select filter' data-value='All'>全員表示</div>
  285. <div class='select filter' data-value='Alive'>生存+役職のみ</div>
  286. </div>
  287. <div>役職入力
  288. <div class='select inputmode' data-value='none'>なし</div>
  289. <div class='select inputmode' data-value='simple'>最新のみ</div>
  290. <div class='select inputmode' data-value='full'>全日</div>
  291. </div>
  292. </div>
  293. <div id="discussLogArea">
  294. <table id="discussLogTable"></table>
  295. </div>
  296. </div>
  297. <div id="voteArea">
  298. <table id="voteTable"></table>
  299. <table id="summaryTable"></table>
  300. </div>
  301. </div>
  302. </div>
  303. `;
  304. let float = `
  305. <div id="floatButtonArea">
  306. <div id='toolArea'>
  307. <a>ツール</a>
  308. <div id='toolArea_hid'></div>
  309. </div>
  310. <div>
  311. <a id='toggleButton'>メモ表示/非表示</a>
  312. </div>
  313. </div>
  314. `;
  315. $("body").append(container).append(float);
  316. }
  317. autoImport() {
  318. if (this.settingIs("auto_import_log", "alltime")) {
  319. this.import();
  320. }
  321. else if (this.settingIs("auto_import_log", "onetime")) {
  322. let today = this.today;
  323. if (today > this.newestImportDay && /<font size="\+2">投票/.test($("body").html())) {
  324. this.newestImportDay = today;
  325. this.import();
  326. }
  327. }
  328. }
  329. import() {
  330. this.playerManager.import();
  331. this.log.import();
  332. this.save();
  333. }
  334. refresh() {
  335. this.playerManager.refresh();
  336. this.log.refresh();
  337. }
  338. switchDispArea(mode) {
  339. $("div.tab").removeClass("active");
  340. $(`div.tab[data-value=${mode}]`).addClass("active");
  341. $("#memoBody > div").hide();
  342. $(`#${mode}Area`).show();
  343. }
  344. reset() {
  345. this.playerManager.reset();
  346. this.log.reset();
  347. }
  348. switchAliveFilter(mode) {
  349. mode = mode || this.filterSetting.show;
  350. this.filterSetting.show = mode;
  351. $("div.select.filter").removeClass("active");
  352. $(`div.select.filter[data-value=${mode}]`).addClass("active");
  353. this.playerManager.filter(mode);
  354. this.save();
  355. }
  356. switchInputMode(mode) {
  357. mode = mode || this.filterSetting.input;
  358. this.filterSetting.input = mode;
  359. $("div.select.inputmode").removeClass("active");
  360. $(`div.select.inputmode[data-value=${mode}]`).addClass("active");
  361. let newestDay = this.newestDay;
  362. for (var day = 1; day <= newestDay; day++) {
  363. if (mode == "full" || (mode == "simple" && day == newestDay)) {
  364. $("#result_" + day).show();
  365. }
  366. else {
  367. $("#result_" + day).hide();
  368. }
  369. }
  370. this.save();
  371. }
  372. on() {
  373. $("table").eq(1).attr("id", "w_player");
  374. $("table[cellspacing=0]").eq(-1).attr("id", "w_textarea");
  375. $("table[cellpadding=0]").not(".CLSTABLE2").last().attr("id", "w_discuss");
  376. $("table[cellspacing=0]").eq(0).attr("id", "w_info");
  377. $("table[cellspacing=0]").eq(-2).attr("id", "w_command");
  378. let voicebutton = [
  379. "<td class='voiceloud'>",
  380. "<div class='voice' data-value='MSG'>普</div>",
  381. "<div class='voice' data-value='MSG2'><strong>強</strong></div>",
  382. "<div class='voice' data-value='MSG3'><span style='color:#6666ee;'>弱</span></div>",
  383. "</td>",
  384. ].join("");
  385. let submitbutton = "<td><input type='submit' value='行動/更新' style='height:100px; width:150px;'></td>";
  386. $("#w_textarea").find("td:last").after(submitbutton).after(voicebutton);
  387. this.switchInputMode();
  388. this.switchAliveFilter();
  389. let _this = this;
  390. $("#toggleButton").on("click", () => {
  391. $("#memoContainer").toggle();
  392. });
  393. $("#importButton").on("click", () => {
  394. this.import();
  395. this.refresh();
  396. });
  397. $("#resetButton").on("click", () => {
  398. if (!window.confirm("ログをすべてリセットします。本当によろしいですか?"))
  399. return false;
  400. this.reset();
  401. this.refresh();
  402. });
  403. $("#reloadButton").on("click", function () {
  404. $("textarea").val("");
  405. document.forms[0].submit();
  406. });
  407. $("div.tab").on("click", function () {
  408. let mode = $(this).data("value");
  409. _this.switchDispArea(mode);
  410. });
  411. $("div.select.filter").on("click", function () {
  412. let mode = $(this).data("value");
  413. _this.switchAliveFilter(mode);
  414. });
  415. $("div.select.inputmode").on("click", function () {
  416. let mode = $(this).data("value");
  417. _this.switchInputMode(mode);
  418. });
  419. $("#toolArea").hover(() => {
  420. $("#toolArea_hid").show();
  421. }, () => {
  422. $("#toolArea_hid").hide();
  423. });
  424. $("textarea").eq(0).focus();
  425. $("div.voice").on("click", function () {
  426. $("select").eq(0).val($(this).data("value"));
  427. $("div.voice").removeClass("voice_selected");
  428. $(this).addClass("voice_selected");
  429. });
  430. $(window).on("keydown", function (e) {
  431. if (e.keyCode == 27) {
  432. $("#memoContainer").hide();
  433. }
  434. });
  435. this.refresh();
  436. }
  437. toggleAutoReload() {
  438. this.isAutoReload = !this.isAutoReload;
  439. this.save();
  440. }
  441. get villageNo() {
  442. return $("title").text().slice(0, 6);
  443. }
  444. load() {
  445. let sdata = localStorage.getItem("memodata");
  446. if (!sdata)
  447. return false;
  448. let memodata = JSON.parse(sdata);
  449. this.isAutoReload = memodata.isAutoReload;
  450. if (memodata.villageNo != this.villageNo)
  451. return false;
  452. this.playerManager.load(memodata.playerInfo);
  453. this.log.load(memodata.discussLog);
  454. if (memodata.filterSetting) {
  455. this.filterSetting = memodata.filterSetting;
  456. }
  457. this.newestImportDay = memodata.newestImportDay || 0;
  458. this.save();
  459. }
  460. save() {
  461. let data = {
  462. villageNo: this.villageNo,
  463. playerInfo: this.playerManager.forSave(),
  464. discussLog: this.log.forSave(),
  465. filterSetting: this.filterSetting,
  466. isAutoReload: this.isAutoReload,
  467. newestImportDay: this.newestImportDay,
  468. };
  469. localStorage.setItem("memodata", JSON.stringify(data));
  470. }
  471. filterLog(no = 99, day = 99) {
  472. if (no < 99) {
  473. $("#discussLogTable tr").hide();
  474. $("tr.systemlog").show();
  475. $("tr.talk_player" + no).show();
  476. }
  477. else {
  478. $("#discussLogTable tr").show();
  479. }
  480. if (day < 99) {
  481. $("#discussLogTable tbody").hide();
  482. $("#log_day" + day).show();
  483. }
  484. else {
  485. $("#discussLogTable tbody").show();
  486. }
  487. }
  488. get today() {
  489. var day = /<font size="\+2">(\d{1,2})/.exec($("body").html());
  490. return day ? +day[1] - 1 : 0;
  491. }
  492. get newestDay() {
  493. return Math.max(this.today, this.log.list.length - 1);
  494. }
  495. get isDaytime() {
  496. return $("body").attr("bgcolor") != "#000000";
  497. }
  498. get isStart() {
  499. return this.log.list.length >= 2;
  500. }
  501. }
  502. class Player {
  503. constructor(data) {
  504. this.no = data.no || 0;
  505. this.name = data.name || "";
  506. this.vital = data.vital || "alive";
  507. this.job = data.job || "gray";
  508. this.reasoning = data.reasoning || "gray";
  509. this.jobresult = data.jobresult || [];
  510. this.vote = data.vote || [];
  511. this.death = data.death || { reason: null, day: null, cantco: 99 };
  512. }
  513. static wakameteHTMLof(no, html) {
  514. let name = html.split("<br>")[0];
  515. let vital = /生存中/.test(html) ? "alive" : "death";
  516. return new Player({
  517. no: no,
  518. name: name,
  519. vital: vital,
  520. });
  521. }
  522. forSave() {
  523. return {
  524. name: this.name,
  525. no: this.no,
  526. vital: this.vital,
  527. job: this.job,
  528. reasoning: this.reasoning,
  529. jobresult: this.jobresult,
  530. vote: this.vote,
  531. deathDetail: this.death,
  532. };
  533. }
  534. updateVital(vital) {
  535. this.vital = vital;
  536. }
  537. setJudge(day, judge) {
  538. this.jobresult.fillundef({ target: 99, judge: "notinput" }, +day);
  539. this.jobresult[day].judge = judge;
  540. }
  541. setTarget(day, target) {
  542. if (!target)
  543. return false;
  544. this.jobresult.fillundef({ target: 99, judge: "notinput" }, +day);
  545. this.jobresult[day].target = target;
  546. }
  547. }
  548. class PlayerManager {
  549. constructor(memo) {
  550. this.list = [];
  551. this.indexOfName = {};
  552. this.memo = memo;
  553. }
  554. reset() {
  555. this.list = [];
  556. this.indexOfName = {};
  557. }
  558. load(data) {
  559. if (!data)
  560. return false;
  561. this.list = [];
  562. this.indexOfName = {};
  563. for (let p of data) {
  564. let player = new Player(p);
  565. this.list.push(player);
  566. this.indexOfName[player.name] = player.no;
  567. }
  568. }
  569. pick(name) {
  570. if (name in this.indexOfName) {
  571. return this.list[this.indexOfName[name]];
  572. }
  573. else {
  574. return null;
  575. }
  576. }
  577. forSave() {
  578. return this.list.map((player) => player.forSave());
  579. }
  580. filter(mode) {
  581. for (var player of this.list) {
  582. if (mode == "All" || player.vital == "alive" || player.job != "gray") {
  583. $("td.player_" + player.no).show();
  584. }
  585. else {
  586. $("td.player_" + player.no).hide();
  587. }
  588. }
  589. }
  590. import() {
  591. this.update();
  592. this.import_vote_wakamete();
  593. this.import_death_wakamete();
  594. }
  595. update() {
  596. switch (this.memo.serverName) {
  597. case "wakamete":
  598. let isStart = this.memo.isStart;
  599. if (isStart) {
  600. this.vitalCheck_wakamete();
  601. }
  602. else {
  603. this.update_wakamete();
  604. }
  605. break;
  606. }
  607. }
  608. no(name) {
  609. if (name in this.indexOfName) {
  610. return this.indexOfName[name];
  611. }
  612. else {
  613. return "";
  614. }
  615. }
  616. vitalCheck_wakamete() {
  617. $("#w_player")
  618. .find("td:odd")
  619. .each((i, v) => {
  620. if (!$(v).html())
  621. return false;
  622. this.list[i].updateVital(/生存中/.test($(v).html()) ? "alive" : "death");
  623. });
  624. }
  625. update_wakamete() {
  626. this.list = [];
  627. this.indexOfName = {};
  628. $("#w_player")
  629. .find("td:odd")
  630. .each((i, v) => {
  631. let html = $(v).html();
  632. if (!html)
  633. return false;
  634. let player = Player.wakameteHTMLof(i, html);
  635. this.list.push(player);
  636. this.indexOfName[player.name] = i;
  637. });
  638. }
  639. import_death_wakamete() {
  640. let deathList = [];
  641. $("#w_discuss")
  642. .find("td[colspan='2']")
  643. .each((i, tr) => {
  644. if (/(で発見|結果処刑|突然死|猫又の呪い)/.test($(tr).text())) {
  645. deathList.push(tr);
  646. }
  647. });
  648. let day = this.memo.today;
  649. let cantco = this.memo.today;
  650. let isdaytime = this.memo.isDaytime;
  651. for (let log of deathList) {
  652. let player = this.pick($(log).find("b").eq(0).text());
  653. let text = $(log).text();
  654. let reason = "";
  655. if (/無残な姿/.test(text))
  656. reason = "bite";
  657. if (/死体で発見/.test(text))
  658. reason = "note";
  659. if (/村民協議の結果|猫又の呪い/.test(text)) {
  660. reason = "exec";
  661. isdaytime ? day-- : cantco++;
  662. }
  663. if (/突然死/.test(text)) {
  664. reason = "sudden";
  665. cantco++;
  666. }
  667. player.death = {
  668. reason: reason,
  669. cantco: cantco,
  670. day: day,
  671. };
  672. }
  673. }
  674. import_vote_wakamete() {
  675. let votelog = [];
  676. $("#w_discuss")
  677. .find("td[colspan='2']")
  678. .each(function (i, v) {
  679. if (/\d{1,2}日目 投票結果。/.test($(v).text())) {
  680. votelog.unshift(v);
  681. }
  682. });
  683. if (!votelog.length)
  684. return false;
  685. let daystr = $(votelog[0])
  686. .text()
  687. .match(/(\d{1,2})日目 投票結果。/);
  688. if (!daystr)
  689. return false;
  690. let day = +daystr[1] - 1;
  691. for (let player of this.list) {
  692. player.vote.fillundef(["-"], day);
  693. player.vote[day].fillundef("-", votelog.length - 1);
  694. }
  695. for (let times = 0; times < votelog.length; times++) {
  696. $(votelog[times])
  697. .find("tr")
  698. .each((i, vote) => {
  699. let voter = this.pick($(vote).find("b").eq(0).text());
  700. let target = $(vote).find("b").eq(1).text();
  701. if (voter)
  702. voter.vote[day][times] = target;
  703. });
  704. }
  705. }
  706. listforSelect() {
  707. let playersList = { 99: "" };
  708. for (let player of this.list) {
  709. playersList[player.no] = player.name;
  710. }
  711. return playersList;
  712. }
  713. refresh() {
  714. this.refreshPlayer();
  715. this.refreshVote();
  716. this.refreshSummary();
  717. }
  718. refreshPlayer() {
  719. let playerInfoTable = $("#playerInfoTable");
  720. playerInfoTable.empty();
  721. if (!this.list.length)
  722. return false;
  723. let playersList = this.listforSelect();
  724. let newestDay = this.memo.newestDay;
  725. let namerow = new Tr("", "namerow");
  726. namerow.add("<a id='filterlink_99_99'>全ログ</a>");
  727. for (let player of this.list) {
  728. namerow.add(`<a id='filterlink_${player.no}_99'>${player.name}</a>`, `player_${player.no}`);
  729. }
  730. namerow.appendTo(playerInfoTable);
  731. let jobrow = new Tr("", "jobrow");
  732. jobrow.add("CO");
  733. for (let player of this.list) {
  734. let select = createSelectBox(joblist, player.job, {
  735. id: `player_${player.no}_job`,
  736. class: "jobselect",
  737. });
  738. jobrow.add(select, "player_" + player.no);
  739. }
  740. jobrow.appendTo(playerInfoTable);
  741. jobrow = new Tr("", "jobrow");
  742. jobrow.add("推理");
  743. for (let player of this.list) {
  744. let select = createSelectBox(reasoninglist, player.reasoning, {
  745. id: `player_${player.no}_reasoning`,
  746. class: "reasoningselect",
  747. });
  748. jobrow.add(select, "player_" + player.no);
  749. }
  750. playerInfoTable.append(jobrow.text());
  751. for (let day = 1; day <= newestDay; day++) {
  752. let row = new Tr("", "talknumrow");
  753. row.add(`<a id="filterlink_99_${day}">${day + 1}日目</a>`);
  754. for (let player of this.list) {
  755. let no = player.no;
  756. let talknum = this.memo.log.talknum(player.name, day) || "";
  757. row.add(`<a id=filterlink_${no}_${day}>${talknum}</a>`, `player_${no}`);
  758. }
  759. row.appendTo(playerInfoTable);
  760. }
  761. for (let day = 1; day <= newestDay; day++) {
  762. let resultrow = new Tr("result_" + day, "resultrow");
  763. resultrow.add(`占霊結果 ${day + 1}日目`);
  764. for (let player of this.list) {
  765. if ((player.job == "fortune" && day < player.death.cantco) ||
  766. (player.job == "necro" && day < player.death.cantco && day > 1)) {
  767. let select1 = createSelectBox(playersList, 99, {
  768. id: `target_${player.no}_${day}`,
  769. class: "jobtarget",
  770. });
  771. let select2 = createSelectBox(resultlist, "notinput", {
  772. id: `judge_${player.no}_${day}`,
  773. class: "jobjudge",
  774. });
  775. resultrow.add(select1 + select2, "player_" + player.no);
  776. }
  777. else {
  778. resultrow.add("", "player_" + player.no);
  779. }
  780. }
  781. playerInfoTable.append(resultrow.text());
  782. }
  783. this.refreshJobResult();
  784. this.memo.switchAliveFilter();
  785. this.memo.switchInputMode();
  786. this.coloringGray();
  787. let _this = this;
  788. $("#playerInfoTable a").on("click", function (e) {
  789. let id = $(this).attr("id");
  790. if (!id)
  791. return false;
  792. let no = +id.split("_")[1];
  793. let day = +id.split("_")[2];
  794. _this.memo.filterLog(no, day);
  795. });
  796. $("select.jobselect").on("change", function (e) {
  797. let id = $(this).attr("id");
  798. if (!id)
  799. return false;
  800. let [i, no, day] = id.split("_");
  801. _this.list[+no].job = String($(this).val());
  802. _this.refresh();
  803. _this.refreshJobInitial();
  804. _this.coloring();
  805. _this.memo.save();
  806. });
  807. $("select.reasoningselect").on("change", function (e) {
  808. let id = $(this).attr("id");
  809. if (!id)
  810. return false;
  811. let [i, no, day] = id.split("_");
  812. _this.list[+no].reasoning = String($(this).val());
  813. _this.refreshJobInitial();
  814. _this.coloring();
  815. _this.memo.save();
  816. });
  817. $("select.jobtarget").on("change", function (e) {
  818. let id = $(this).attr("id");
  819. if (!id)
  820. return false;
  821. let no = +id.split("_")[1];
  822. let day = +id.split("_")[2];
  823. let target = +$(this).val();
  824. _this.list[no].setTarget(day, target);
  825. _this.refreshSummary();
  826. _this.coloringGray();
  827. _this.memo.save();
  828. });
  829. $("select.jobjudge").on("change", function (e) {
  830. let id = $(this).attr("id");
  831. if (!id)
  832. return false;
  833. let [i, no, day] = id.split("_");
  834. let judge = String($(this).val());
  835. _this.list[+no].setJudge(+day, judge);
  836. _this.refreshSummary();
  837. _this.coloringGray();
  838. _this.memo.save();
  839. });
  840. }
  841. refreshVote() {
  842. if (!this.list.length)
  843. return false;
  844. let voteTable = $("#voteTable");
  845. let newestDay = this.memo.newestDay;
  846. voteTable.empty();
  847. var tr = new Tr();
  848. tr.add("プレイヤー");
  849. for (var day = 1; day <= newestDay; day++) {
  850. if (!this.list[0].vote[day])
  851. continue;
  852. let colspan = this.list[0].vote[day].length;
  853. tr.add(`${day + 1}日目`, "", colspan);
  854. }
  855. voteTable.append(tr.text());
  856. for (var player of this.list) {
  857. tr = new Tr();
  858. tr.add(player.name);
  859. for (day = 1; day <= newestDay; day++) {
  860. if (!player.vote[day])
  861. continue;
  862. for (let vote of player.vote[day]) {
  863. tr.add(vote);
  864. }
  865. }
  866. voteTable.append(tr.text());
  867. }
  868. }
  869. refreshJobResult() {
  870. let newestDay = this.memo.newestDay;
  871. let fortunes = this.list.filter((p) => p.job == "fortune");
  872. for (let fortune of fortunes) {
  873. for (let day = 1; day < Math.min(fortune.death.cantco, newestDay + 1); day++) {
  874. if (!fortune.jobresult[day])
  875. continue;
  876. $("#target_" + fortune.no + "_" + day).val(fortune.jobresult[day].target);
  877. $("#judge_" + fortune.no + "_" + day).val(fortune.jobresult[day].judge);
  878. }
  879. }
  880. let necros = this.list.filter((p) => p.job == "necro");
  881. for (let necro of necros) {
  882. for (let day = 2; day < Math.min(necro.death.cantco, newestDay + 1); day++) {
  883. let exec = this.list.filter((p) => p.death.day == day - 1 && p.death.reason == "exec");
  884. if (necro.jobresult[day]) {
  885. $("#target_" + necro.no + "_" + day).val(necro.jobresult[day].target);
  886. $("#judge_" + necro.no + "_" + day).val(necro.jobresult[day].judge);
  887. }
  888. else if (exec) {
  889. $("#target_" + necro.no + "_" + day).val(exec[0].no);
  890. }
  891. }
  892. }
  893. }
  894. refreshSummary() {
  895. let summaryTable = $("#summaryTable");
  896. let newestDay = this.memo.newestDay;
  897. summaryTable.empty();
  898. var tr = new Tr();
  899. tr.add("", "", 2);
  900. for (var day = 1; day <= newestDay; day++) {
  901. tr.add("" + (day + 1) + "日目");
  902. }
  903. tr.appendTo(summaryTable);
  904. var fortunes = this.list.filter((p) => p.job == "fortune");
  905. let necros = this.list.filter((p) => p.job == "necro");
  906. let ability = fortunes.concat(necros);
  907. for (let player of ability) {
  908. tr = new Tr();
  909. tr.add(player.job == "fortune" ? "占い師" : "霊能者");
  910. tr.add(player.name);
  911. for (day = 1; day <= newestDay; day++) {
  912. let name = "", judge = "";
  913. if (player.jobresult[day] && player.jobresult[day].target != 99) {
  914. let result = player.jobresult[day];
  915. name = this.list[result.target].name;
  916. judge = resultlist[result.judge];
  917. }
  918. tr.add(name + judge);
  919. }
  920. tr.appendTo(summaryTable);
  921. }
  922. for (var reason in reasonflavor) {
  923. let deaths = this.list.filter((p) => p.death.reason == reason);
  924. if (!deaths.length)
  925. continue;
  926. tr = new Tr();
  927. tr.add(reasonflavor[reason], "", 2);
  928. for (day = 1; day <= newestDay; day++) {
  929. let cn = deaths.filter((p) => p.death.day == day).map((p) => p.name);
  930. let text = cn.length ? cn.join("<br>") : "-";
  931. tr.add(text);
  932. }
  933. tr.appendTo(summaryTable);
  934. }
  935. }
  936. refreshJobInitial() {
  937. for (let player of this.list) {
  938. let job = jobinitial[player.reasoning] + jobinitial[player.job];
  939. $("tr.talk_player" + player.no + " span").html(job);
  940. }
  941. }
  942. coloringGray() {
  943. let co = this.list.filter((p) => p.job != "gray").map((p) => p.no);
  944. var fortunes = this.list.filter((p) => p.job == "fortune");
  945. if (this.memo.settingIs("grayregion")) {
  946. fortunes = fortunes.filter((f) => f.reasoning == "gray" || f.reasoning == "real");
  947. }
  948. let fortuned = fortunes.map((f) => f.jobresult.map((r) => +r.target)).flat();
  949. let notgray = co.concat(fortuned);
  950. $("tr.namerow td").removeClass("death").removeClass("gray");
  951. for (var player of this.list) {
  952. if (player.vital == "death") {
  953. $("tr.namerow .player_" + player.no).addClass("death");
  954. }
  955. else if (!notgray.includes(player.no)) {
  956. $("tr.namerow .player_" + player.no).addClass("gray");
  957. }
  958. }
  959. }
  960. coloring() {
  961. if (!this.memo.settingIs("coloringName"))
  962. return false;
  963. $("#w_discuss b").each((i, e) => {
  964. let name = $(e).text();
  965. let player = this.pick(name);
  966. if (player) {
  967. let job = player.job;
  968. let color = this.memo.colorSetting.pick(job);
  969. $(e).removeClass().addClass(color);
  970. }
  971. });
  972. }
  973. }
  974. class Log {
  975. constructor(data) {
  976. this.name = data.name;
  977. this.color = data.color;
  978. this.content = data.content;
  979. }
  980. forSave() {
  981. return {
  982. name: this.name,
  983. color: this.color,
  984. content: this.content,
  985. };
  986. }
  987. }
  988. class LogManager {
  989. constructor(memo) {
  990. this.memo = memo;
  991. this.list = [];
  992. }
  993. reset() {
  994. this.list = [];
  995. }
  996. load(data) {
  997. if (!data)
  998. return false;
  999. this.list = [];
  1000. data.forEach((logs) => {
  1001. let logofday = [];
  1002. logs.forEach((d) => {
  1003. let log = new Log(d);
  1004. logofday.push(log);
  1005. });
  1006. this.list.push(logofday);
  1007. });
  1008. }
  1009. forSave() {
  1010. let result = [];
  1011. this.list.forEach((logs) => {
  1012. let l = logs.map((log) => log.forSave());
  1013. result.push(l);
  1014. });
  1015. return result;
  1016. }
  1017. talknum(name, day) {
  1018. if (!this.list[day])
  1019. return 0;
  1020. return this.list[day].filter((l) => l.name == name).length;
  1021. }
  1022. import() {
  1023. switch (this.memo.serverName) {
  1024. case "wakamete":
  1025. this.import_wakamete();
  1026. break;
  1027. }
  1028. this.memo.filterLog();
  1029. }
  1030. import_wakamete() {
  1031. this.import_discuss_wakamete();
  1032. }
  1033. import_discuss_wakamete() {
  1034. let today = this.memo.today;
  1035. let isDaytime = this.memo.isDaytime;
  1036. if (!isDaytime)
  1037. return false;
  1038. this.list.fillundef([], today);
  1039. this.list[today] = [];
  1040. $("#w_discuss")
  1041. .find("tr")
  1042. .each((i, tr) => {
  1043. if ($(tr).children().length == 2) {
  1044. let name = $(tr).children().eq(0).find("b").eq(0).html();
  1045. let content = $(tr).children().eq(1).html();
  1046. let namehtml = $(tr).children().eq(0).html();
  1047. let color = namehtml.match(/color="(.+?)"/)[1];
  1048. let log = new Log({
  1049. name: name,
  1050. color: color,
  1051. content: content,
  1052. });
  1053. this.list[today].push(log);
  1054. }
  1055. });
  1056. }
  1057. refresh() {
  1058. let discussLogTable = $("#discussLogTable");
  1059. discussLogTable.empty();
  1060. if (!this.list.length)
  1061. return false;
  1062. this.list.forEach((logs, day) => {
  1063. if (!logs)
  1064. return;
  1065. let tbody = $("<tbody></tbody>", { id: "log_day" + day });
  1066. let trs = `<tr class="systemlog"><td colspan="2">${day + 1}日目</td></tr>`;
  1067. for (let log of logs) {
  1068. let cl = "talk_player" + this.memo.playerManager.no(log.name);
  1069. let name = `<font color="${log.color}">◆</font><b>${log.name}</b>さん`;
  1070. if (log.name == "ゲームマスター") {
  1071. name = `<font color="${log.color}">◆<b>${log.name}</b></font>`;
  1072. }
  1073. trs += `<tr class="${cl}"><td>${name}<span class='jobinitial'></span></td><td>${log.content}</td></tr>`;
  1074. }
  1075. tbody.append(trs).prependTo(discussLogTable);
  1076. });
  1077. this.memo.playerManager.refreshJobInitial();
  1078. }
  1079. }
  1080. class SettingOption {
  1081. constructor(data) {
  1082. this.value = data.value;
  1083. this.option = data.option;
  1084. this.default = data.value;
  1085. this.name = data.name;
  1086. }
  1087. }
  1088. class ColorSetting {
  1089. constructor(memo) {
  1090. this.memo = memo;
  1091. this.options = {};
  1092. }
  1093. pick(job) {
  1094. return this.options[job].value;
  1095. }
  1096. init() {
  1097. for (let key in colorSettingDefault) {
  1098. this.options[key] = new SettingOption(colorSettingDefault[key]);
  1099. }
  1100. this.load();
  1101. let settingArea = $("<div></div>", {
  1102. id: "colorSetting",
  1103. class: "window southEast",
  1104. }).appendTo($("body"));
  1105. $("#toolArea_hid").append("<a id='dispcolorSetting'>名前色設定</a>");
  1106. let settingTable = $("<table></table>", { id: "colorSettingtable" }).appendTo(settingArea);
  1107. settingArea.append(`<div class="closebutton"><input type="button" id="closeColorSetting" value='閉じる'></div>`);
  1108. $("#dispcolorSetting").on("click", () => {
  1109. $("#colorSetting").show();
  1110. });
  1111. $("#closeColorSetting").on("click", () => {
  1112. $("#colorSetting").hide();
  1113. });
  1114. for (let key in this.options) {
  1115. let item = this.options[key];
  1116. let tr = new Tr();
  1117. tr.add(item.name);
  1118. tr.add(createSelectBox(item.option, item.value, { id: key }));
  1119. tr.appendTo(settingTable);
  1120. }
  1121. let _this = this;
  1122. $("#colorSetting select").on("change", function () {
  1123. let val = $(this).val();
  1124. _this.setValue($(this).attr("id"), String(val));
  1125. _this.coloring();
  1126. });
  1127. }
  1128. coloring() {
  1129. this.memo.playerManager.coloring();
  1130. }
  1131. setValue(key, value) {
  1132. if (!(key in this.options))
  1133. return false;
  1134. this.options[key].value = value;
  1135. this.save();
  1136. }
  1137. load() {
  1138. let s = localStorage.getItem("memoColorSetting");
  1139. if (!s)
  1140. return false;
  1141. let data = JSON.parse(s);
  1142. for (let key in data) {
  1143. if (key in this.options) {
  1144. this.options[key].value = data[key];
  1145. }
  1146. }
  1147. }
  1148. save() {
  1149. localStorage.setItem("memoColorSetting", JSON.stringify(this.forSave()));
  1150. }
  1151. forSave() {
  1152. let result = {};
  1153. for (let key in this.options) {
  1154. result[key] = this.options[key].value;
  1155. }
  1156. return result;
  1157. }
  1158. }
  1159. class Setting {
  1160. constructor(memo) {
  1161. this.memo = memo;
  1162. this.options = {};
  1163. }
  1164. init() {
  1165. for (let key in settingDefault) {
  1166. this.options[key] = new SettingOption(settingDefault[key]);
  1167. }
  1168. this.load();
  1169. let settingArea = $("<div></div>", { id: "setting", class: "window southEast" }).appendTo($("body"));
  1170. $("#toolArea_hid").append("<a id='dispsetting'>設定</a>");
  1171. let settingTable = $("<table></table>", { id: "settingtable" }).appendTo(settingArea);
  1172. settingArea.append(`<div class="closebutton"><input type="button" id="closeSetting" value='閉じる'></div>`);
  1173. $("#dispsetting").on("click", () => {
  1174. $("#setting").show();
  1175. });
  1176. $("#closeSetting").on("click", () => {
  1177. $("#setting").hide();
  1178. });
  1179. for (let key in this.options) {
  1180. let item = this.options[key];
  1181. let tr = new Tr();
  1182. tr.add(item.name);
  1183. tr.add(createSelectBox(item.option, item.value, { id: key }));
  1184. tr.appendTo(settingTable);
  1185. }
  1186. let _this = this;
  1187. $("#setting select").on("change", function () {
  1188. let key = $(this).attr("id");
  1189. let val = String($(this).val());
  1190. _this.setValue(key, val);
  1191. });
  1192. }
  1193. setValue(key, value) {
  1194. if (!(key in this.options))
  1195. return false;
  1196. this.options[key].value = value;
  1197. this.save();
  1198. }
  1199. load() {
  1200. let s = localStorage.getItem("memoSetting");
  1201. if (!s)
  1202. return false;
  1203. let data = JSON.parse(s);
  1204. for (let key in data) {
  1205. if (key in this.options) {
  1206. this.options[key].value = data[key];
  1207. }
  1208. }
  1209. }
  1210. save() {
  1211. localStorage.setItem("memoSetting", JSON.stringify(this.forSave()));
  1212. }
  1213. forSave() {
  1214. let result = {};
  1215. for (let key in this.options) {
  1216. result[key] = this.options[key].value;
  1217. }
  1218. return result;
  1219. }
  1220. }
  1221. class Random {
  1222. constructor(memo) {
  1223. this.memo = memo;
  1224. }
  1225. init() {
  1226. let randomWindow = `
  1227. <div id="random" class="window southEast">
  1228. <div class="closebutton">
  1229. <input type="button" id="closeRandom" value='閉じる'>
  1230. </div>
  1231. <input type="button" value="4桁乱数" id="random_rnd4">
  1232. <input type="button" value="乱数表" id="random_matrix">
  1233. <input type="button" value="背徳用数字" id="random_haitoku">
  1234. <br>
  1235. <textarea id="forCopy" style="width:200px;height:150px;"></textarea>
  1236. <div id='randomMessage'></div>
  1237. </div>
  1238. `;
  1239. $("body").append(randomWindow);
  1240. $("#toolArea_hid").append("<a id='dispRandom'>乱数</a>");
  1241. $("#dispRandom").on("click", () => {
  1242. $("#random").show();
  1243. });
  1244. $("#closeRandom").on("click", () => {
  1245. $("#random").hide();
  1246. });
  1247. $("#random_rnd4").on("click", () => {
  1248. this.copy(this.rnd4());
  1249. });
  1250. $("#random_matrix").on("click", () => {
  1251. this.copy(this.matrix());
  1252. });
  1253. $("#random_haitoku").on("click", () => {
  1254. this.copy(this.haitoku());
  1255. });
  1256. }
  1257. copy(text) {
  1258. $("#forCopy").val(text).select();
  1259. document.execCommand("copy");
  1260. $("#randomMessage").html("コピーしました。");
  1261. }
  1262. rnd(n) {
  1263. return Math.floor(Math.random() * n);
  1264. }
  1265. padding(num, digit) {
  1266. let txt = "0000000000" + num;
  1267. return txt.slice(-digit);
  1268. }
  1269. rnd4() {
  1270. let rnd = this.rnd(10000);
  1271. return this.padding(rnd, 4);
  1272. }
  1273. matrix() {
  1274. let num = this.memo.playerNum;
  1275. var l = [];
  1276. var mat = "";
  1277. for (var i = 0; i < num; i++) {
  1278. l[i] = this.padding(i + 1, 2);
  1279. }
  1280. for (i = 0; i < num; i++) {
  1281. var r = this.rnd(num - i);
  1282. mat += l[r];
  1283. mat += i % 5 == 4 ? "\n" : " / ";
  1284. for (var j = r; j < num - 1; j++) {
  1285. l[j] = l[j + 1];
  1286. }
  1287. }
  1288. return mat;
  1289. }
  1290. haitoku() {
  1291. let num = this.memo.playerNum;
  1292. var jobs = ["村 人", "占い師", "霊能者", "狩 人", "共有者", "狂 人", "背徳者"];
  1293. var cir = ["①", "②", "③", "④", "⑤", "⑥", "⑦"];
  1294. var result = "";
  1295. var isimo = num == 15 || num == 19;
  1296. var n = isimo ? 7 : 6;
  1297. if (isimo) {
  1298. result += cir[this.rnd(n)] + "\n\n";
  1299. }
  1300. var l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  1301. for (let i = 0; i < n; i++) {
  1302. let r = this.rnd(10 - i);
  1303. let rnd = this.padding(this.rnd(100), 2);
  1304. result = result + jobs[i] + ":" + l[r] + rnd + "\n";
  1305. l.splice(r, 1);
  1306. }
  1307. return result;
  1308. }
  1309. }
  1310. class Style {
  1311. constructor(memo) {
  1312. this.memo = memo;
  1313. }
  1314. injection() {
  1315. let style = [];
  1316. if (this.memo.settingIs("rewrite_css")) {
  1317. style = [
  1318. ".CLSTABLE tr td:nth-of-type(even) {font-size: 12px;line-height: 110%;padding: 2px;}",
  1319. ".CLSTABLE tr td:nth-of-type(odd) {font-size: 0px;padding: 2px;}",
  1320. 'body[bgcolor="#000000"] font[color="#6666aa"] {color: #ccccff;}',
  1321. 'font[size="-1"] {font-size: 9pt;}',
  1322. "img {padding: 0;}",
  1323. "input,select {font-size: 9pt;}",
  1324. "table {font-size: 13px;}",
  1325. 'textarea {font-family: "Meiryo";font-size: 11px;min-height: 100px;}',
  1326. 'table[cellpadding="0"] tr td:nth-of-type(2){word-break:break-all;}',
  1327. ];
  1328. }
  1329. let theme = this.memo.settingValue("theme_color");
  1330. let theme_color = themeList[theme].main;
  1331. let sub_color = themeList[theme].sub;
  1332. style = style.concat([
  1333. `:root{--theme-color:${theme_color}; --sub-color:${sub_color};}`,
  1334. "*{box-sizing:border-box;}",
  1335. "body{margin:0;}",
  1336. "form{margin:8px;}",
  1337. "#memoContainer{display:none; width:100%; height:100%; position:fixed; top:0px; left:0px; background-color:rgba(180,180,180,0.8); padding:15px; overflow:auto;}",
  1338. "#floatButtonArea{position:fixed; right:15px; top:15px;}",
  1339. "#floatButtonArea > div{margin:0px 2px; display:inline-block; vertical-align: top; width:110px; box-shadow: 0 3px 5px rgba(0, 0, 0, 0.4);}",
  1340. "#warningArea{width:100%; height:40px; padding:5px; position:fixed; bottom:0; display:none; background-color:darkorange; text-align:center; font-size:16px; color:white; font-weight:bold;}",
  1341. "#left{font-size:30px;}",
  1342. "#warningArea select, #warningArea input{font-size:11pt; vertical-align:middle;}",
  1343. "#memoMenu{width:100%; margin: 0 auto; font-size:10px;}",
  1344. "#memoMenu input, #memoMenu select, #buttonArea input{font-size:11px;}",
  1345. "#memoTab{margin-top:15px;}",
  1346. "#memoBody{width:100%; height:calc(100% - 63px); margin: 0 auto; }",
  1347. "#logArea,#voteArea{width:100%; height:100%; overflow:auto; margin: 0 auto; background-color:white; padding:10px; border-radius:0px 8px 8px 8px ;}",
  1348. "#voteArea{display:none;}",
  1349. "#discussLogTable{border-collapse:collapse;}",
  1350. "#discussLogTable td{text-align:left;vertical-align:top; color:black; word-break:break-all; font-size:9pt; line-height:140%; padding:2px;}",
  1351. "#discussLogTable font{font-size:9pt;}",
  1352. "#discussLogTable tr td:first-of-type{min-width:150px;}",
  1353. "#discussLogTable tr.systemlog td{font-weight:bold;background-color:var(--theme-color) !important; color:white; text-align:center;}",
  1354. "#playerInfoTable a{text-decoration:underline; color:blue; cursor:pointer;}",
  1355. "#playerInfoTable tr.namerow td.death {background-color:pink;}",
  1356. "#playerInfoTable tr.namerow td.gray {background-color:#e3e3e3;}",
  1357. "#playerInfoTable select {max-width:100px;}",
  1358. "#voteTable, #summaryTable{font-size:11px; border-collapse:collapse; margin-bottom:10px;color:black;}",
  1359. "#voteTable td, #summaryTable td{border:1px solid #666; padding:2px; }",
  1360. "#toolArea_hid {display:none;}",
  1361. "#setting input[type=number], #setting input[type=text]{width:60px;}",
  1362. ".coloredit, .iconsupport{display:none;}",
  1363. ".voiceloud {padding:0px 5px;}",
  1364. ".voiceloud div:not(:first-of-type){margin-top:5px;}",
  1365. ".voice {width:30px;height:30px;font-size:16px;border:1px solid black; border-radius:2px;background-color:white;line-height:28px;text-align:center;color:black; cursor:pointer;}",
  1366. ".voice.voice_selected{border:3px solid red; line-height:24px;}",
  1367. "#caspe input[type=text]{width:100px;}",
  1368. "#caspe input[type=number]{width:50px}",
  1369. ".window{box-shadow: 0 3px 5px rgba(0, 0, 0, 0.4); border-top:16px solid var(--theme-color); background-color:#efefef; padding:10px; display:none; }",
  1370. ".southEast{font-size:13px; position:fixed; right:10px; bottom:10px; width:400px; height:300px;}",
  1371. ".north{width:400px; height:80px; position:fixed; left:calc(50% - 200px); top:15px;}",
  1372. "#floatButtonArea a, .button{width:110px; color:white; background-color:var(--theme-color); cursor:pointer; display:inline-block; font-size:12px; font-weight:bold; line-height:24px; text-align:center;}",
  1373. "div.button{margin:0px 2px; box-shadow: 0 3px 5px rgba(0, 0, 0, 0.4); padding:0px 3px;}",
  1374. ".tab{width:150px; color:white; background-color:var(--theme-color);cursor:pointer; display:inline-block; font-size:12px; line-height:24px; text-align:center; border-radius:8px 8px 0 0;}",
  1375. ".tab.active{color:var(--theme-color); background-color:white;font-weight:bold;}",
  1376. "#floatButtonArea a:hover, div.button:hover{background-color:var(--sub-color);}",
  1377. ".closebutton{position:absolute; right:5px; top:5px;}",
  1378. "#caspe img{padding:2px;}",
  1379. "#caspe img.iconselected{border:2px solid var(--theme-color); padding:0px;}",
  1380. ".jobinitial{user-select:none; color:var(--theme-color); font-weight:bold; font-size:80%;}",
  1381. ".jobinitial:not(:empty):before{content:'[';}",
  1382. ".jobinitial:not(:empty):after{content:']';}",
  1383. ".black{color:black;} .pink{color:deeppink;} .red{color:red;} .green{color:green;}",
  1384. ".purple{color:purple;} .brown{color:brown;} .blue{color:blue;}",
  1385. ".gaming{background:linear-gradient(to right, #f33,#ff3,#3f3,#3ff,#33f,#f3f,#f33) ;-webkit-background-clip: text; -webkit-text-fill-color:transparent;}",
  1386. ]);
  1387. if (this.memo.settingIs("layout")) {
  1388. style = style.concat([
  1389. "#logArea{display: flex; flex-direction:column; flex-wrap:wrap;}",
  1390. "#playerInfoArea{height:calc(100% - 50px); overflow:auto hidden; width:50%; padding:0px;}",
  1391. "#playerInfoArea select{font-size:8.5pt;}",
  1392. "#buttonArea{height:50px;font-size:12px; padding:5px;}",
  1393. "#buttonArea > div{ margin-bottom:5px;}",
  1394. "#discussLogArea{width:50%; overflow:auto; padding:5px;}",
  1395. ".select{color:var(--theme-color); border-width:2px 2px 2px 0px ;border-color:var(--theme-color); border-style:solid; background-color:white; cursor:pointer; text-align:center; display:inline-block;width:100px;}",
  1396. ".select.active{color:white;background-color:var(--theme-color); font-weight:bold;}",
  1397. "#buttonArea > div > div.select:first-of-type{margin-left:5px; border-left:2px solid var(--theme-color);}",
  1398. "#buttonArea > div > div.select:lastof-type{margin-right:5px;}",
  1399. "#playerInfoTable{background-color:white; text-align:center; font-size:8pt; color:black; width:100%; margin:0px 2px 2px 0px;}",
  1400. "#playerInfoTable tbody{display:flex; flex-direction:row;border-spacing:0;}",
  1401. "#playerInfoTable tr{display:flex; flex-direction:column; flex:0 0 40px;}",
  1402. "#playerInfoTable tr.namerow{display:flex; flex-direction:column; flex:0 0 80px; position:sticky; left:0;}",
  1403. "#playerInfoTable tr.namerow td{background-color:white;}",
  1404. "#playerInfoTable tr.resultrow{display:flex; flex-direction:column; flex:0 0 140px;}",
  1405. "#playerInfoTable td{border-right:#666 solid 1px;border-bottom:#666 solid 1px;padding:1px; display:block; width:auto; height:1.8em; }",
  1406. "#playerInfoTable tr:first-of-type td{border-left:#666 solid 1px;}",
  1407. "#playerInfoTable tr td:first-of-type{border-top:#666 solid 1px;}",
  1408. ]);
  1409. }
  1410. else {
  1411. style = style.concat([
  1412. "#buttonArea{padding:10px;line-height:24px;font-size:9pt;}",
  1413. "#buttonArea > div{display:inline-block;}",
  1414. "#playerInfoArea{overflow:auto;}",
  1415. ".select{width:100px; color:var(--theme-color); border-width:2px 2px 2px 0px ;border-color:var(--theme-color); border-style:solid; background-color:white; cursor:pointer; display:inline-block; font-size:12px; line-height:20px; text-align:center;}",
  1416. ".select.active{color:white;background-color:var(--theme-color); font-weight:bold;}",
  1417. "#buttonArea > div > div.select:first-of-type{margin-left:5px; border-left:2px solid var(--theme-color);}",
  1418. "#buttonArea > div > div.select:last-of-type{margin-right:5px;}",
  1419. "#playerInfoTable {background-color:white;text-align:center; font-size:8pt;border-collapse:collapse; color:black; margin:0 auto;}",
  1420. "#playerInfoTable td{border:#666 solid 1px; padding:1px; }",
  1421. "#playerInfoTable tr.namerow{height:35px;}",
  1422. "#playerInfoTable tr td:first-child{min-width:50px;}",
  1423. ]);
  1424. }
  1425. $("<style></style>").html(style.join("\n")).appendTo($("head"));
  1426. }
  1427. }
  1428. class Utility {
  1429. constructor(memo) {
  1430. this.memo = memo;
  1431. this.setting = memo.setting;
  1432. this.left = 0;
  1433. this.autoReloadFlg = 0;
  1434. }
  1435. init() {
  1436. this.setAlertVote();
  1437. this.receiveKeyResponse();
  1438. this.dispSuggest();
  1439. this.highlightDeathnote();
  1440. this.memo.playerManager.coloring();
  1441. this.setAutoReload();
  1442. }
  1443. setAutoReload() {
  1444. if ($("td.CLSTD01").eq(1).text() != "◆ 再表示")
  1445. return false;
  1446. let isAutoReload = this.memo.isAutoReload;
  1447. let onoff = isAutoReload ? "ON" : "OFF";
  1448. $("#floatButtonArea").prepend("<div><a id='autoReload'>自動更新:" + onoff + "</a></div>");
  1449. if (isAutoReload)
  1450. this.setReloadTimer();
  1451. $("#autoReload").on("click", () => {
  1452. this.memo.toggleAutoReload();
  1453. let isAutoReload = this.memo.isAutoReload;
  1454. let onoff = isAutoReload ? "ON" : "OFF";
  1455. $("#autoReload").text("自動更新:" + onoff);
  1456. if (isAutoReload) {
  1457. this.setReloadTimer();
  1458. }
  1459. else {
  1460. clearTimeout(this.autoReloadFlg);
  1461. }
  1462. });
  1463. }
  1464. setReloadTimer() {
  1465. this.autoReloadFlg = setTimeout(() => {
  1466. $("textarea").eq(0).val("");
  1467. document.forms[0].submit();
  1468. }, +this.memo.settingValue("autoreload_interval") * 1000);
  1469. }
  1470. setAlertVote() {
  1471. if (!this.memo.settingIs("alert_vote"))
  1472. return false;
  1473. if ($("font[size=6]").length) {
  1474. let warningArea = $("<div></div>", { id: "warningArea" }).appendTo($("body"));
  1475. warningArea.show();
  1476. var cmbplayer = $("select[name=CMBPLAYER]").clone();
  1477. var votebutton = $("<input />", {
  1478. type: "button",
  1479. value: "投票",
  1480. on: {
  1481. click: function () {
  1482. $("select").eq(0).val("VOTE");
  1483. document.forms[0].submit();
  1484. },
  1485. },
  1486. });
  1487. this.left = counts;
  1488. warningArea.html("未投票です! あと<span id='left'></span>秒");
  1489. warningArea.append(cmbplayer);
  1490. warningArea.append(votebutton);
  1491. cmbplayer.on("change", function () {
  1492. let v = $(this).val();
  1493. if (!v)
  1494. return false;
  1495. $("select[name=CMBPLAYER]").val(v);
  1496. });
  1497. $("#left").html("" + this.left);
  1498. setInterval(() => {
  1499. $("#left").html(String(--this.left));
  1500. }, 1000);
  1501. }
  1502. }
  1503. receiveKeyResponse() {
  1504. if (this.memo.settingIs("send_support", "ctrl")) {
  1505. $(window).on("keydown", function (e) {
  1506. if (e.ctrlKey && e.keyCode == 13) {
  1507. document.forms[0].submit();
  1508. }
  1509. });
  1510. }
  1511. if (this.memo.settingIs("send_support", "shift")) {
  1512. $(window).on("keydown", function (e) {
  1513. if (e.shiftKey && e.keyCode == 13) {
  1514. document.forms[0].submit();
  1515. }
  1516. });
  1517. }
  1518. }
  1519. dispSuggest() {
  1520. var colorselect = createSelectBox(COLORLIST, 0, { id: "colorlist" });
  1521. var coloredit = `<td class="coloredit">アイコン色:</td><td class="coloredit">${colorselect}</td>`;
  1522. var iconsupport = "<td class='iconsupport'><input type='button' id='pasteurl' value='/../../imgbbs/img/'></td>";
  1523. let commandtable = $("#w_command");
  1524. commandtable.find("td").eq(1).after(coloredit).after(iconsupport);
  1525. commandtable.find("td").eq(-2).addClass("cmbplayer");
  1526. commandtable.find("td").eq(-1).addClass("cmbplayer");
  1527. let _this = this;
  1528. $("#colorlist").on("change", function () {
  1529. _this.editSpeakField(String($(this).val()));
  1530. });
  1531. $("#pasteurl").on("click", () => {
  1532. this.editSpeakField("/../../imgbbs/img/");
  1533. });
  1534. $("select")
  1535. .eq(0)
  1536. .on("change", function () {
  1537. $("td.coloredit").hide();
  1538. $("td.iconsupport").hide();
  1539. $("td.cmbplayer").hide();
  1540. if ($(this).val() == "ICONCHG") {
  1541. $("td.coloredit").show();
  1542. }
  1543. else if ($(this).val() == "BCONCHG") {
  1544. $("td.iconsupport").show();
  1545. }
  1546. else {
  1547. $("td.cmbplayer").show();
  1548. }
  1549. });
  1550. }
  1551. editSpeakField(val) {
  1552. $("textarea").eq(0).val(val);
  1553. }
  1554. highlightDeathnote() {
  1555. var td = $("#w_info").find("td:last");
  1556. if (/アナタの家の前に/.test(td.html())) {
  1557. td.css("color", "red");
  1558. }
  1559. }
  1560. }
  1561. if ($("body").attr("bgcolor") != "#fee3aa") {
  1562. const meatmemo = new MeatMemo("wakamete");
  1563. }
  1564. })(jQuery)