AtCoder Easy Test v2

Make testing sample cases easy

Verze ze dne 13. 10. 2024. Zobrazit nejnovější verzi.

  1. // ==UserScript==
  2. // @name AtCoder Easy Test v2
  3. // @namespace https://atcoder.jp/
  4. // @version 2.11.14
  5. // @description Make testing sample cases easy
  6. // @author magurofly
  7. // @license MIT
  8. // @supportURL https://github.com/magurofly/atcoder-easy-test/
  9. // @match https://atcoder.jp/contests/*/tasks/*
  10. // @match https://atcoder.jp/contests/*/submit*
  11. // @match https://yukicoder.me/problems/no/*
  12. // @match https://yukicoder.me/problems/*
  13. // @match http://codeforces.com/contest/*/problem/*
  14. // @match http://codeforces.com/gym/*/problem/*
  15. // @match http://codeforces.com/problemset/problem/*
  16. // @match http://codeforces.com/group/*/contest/*/problem/*
  17. // @match http://*.contest.codeforces.com/group/*/contest/*/problem/*
  18. // @match https://codeforces.com/contest/*/problem/*
  19. // @match https://codeforces.com/gym/*/problem/*
  20. // @match https://codeforces.com/problemset/problem/*
  21. // @match https://codeforces.com/group/*/contest/*/problem/*
  22. // @match https://*.contest.codeforces.com/group/*/contest/*/problem/*
  23. // @match https://m1.codeforces.com/contest/*/problem/*
  24. // @match https://m2.codeforces.com/contest/*/problem/*
  25. // @match https://m3.codeforces.com/contest/*/problem/*
  26. // @match https://greatest.deepsurf.us/*/scripts/433152-atcoder-easy-test-v2
  27. // @grant unsafeWindow
  28. // @grant GM_getValue
  29. // @grant GM_setValue
  30. // ==/UserScript==
  31. (function() {
  32.  
  33. if (typeof GM_getValue !== "function") {
  34. if (typeof GM === "object" && typeof GM.getValue === "function") {
  35. GM_getValue = GM.getValue;
  36. GM_setValue = GM.setValeu;
  37. } else {
  38. const storage = JSON.parse(localStorage.AtCoderEasyTest || "{}");
  39. GM_getValue = (key, defaultValue = null) => ((key in storage) ? storage[key] : defaultValue);
  40. GM_setValue = (key, value) => {
  41. storage[key] = value;
  42. localStorage.AtCoderEasyTest = JSON.stringify(storage);
  43. };
  44. }
  45. }
  46.  
  47. if (typeof unsafeWindow !== "object") unsafeWindow = window;
  48. function buildParams(data) {
  49. return Object.entries(data).map(([key, value]) => encodeURIComponent(key) + "=" + encodeURIComponent(value)).join("&");
  50. }
  51. function sleep(ms) {
  52. return new Promise(done => setTimeout(done, ms));
  53. }
  54. function doneOrFail(p) {
  55. return p.then(() => Promise.resolve(), () => Promise.resolve());
  56. }
  57. function html2element(html) {
  58. const template = document.createElement("template");
  59. template.innerHTML = html;
  60. return template.content.firstChild;
  61. }
  62. function newElement(tagName, attrs = {}, children = []) {
  63. const e = document.createElement(tagName);
  64. for (const [key, value] of Object.entries(attrs)) {
  65. if (key == "style") {
  66. for (const [propKey, propValue] of Object.entries(value)) {
  67. e.style[propKey] = propValue;
  68. }
  69. }
  70. else {
  71. e[key] = value;
  72. }
  73. }
  74. for (const child of children) {
  75. e.appendChild(child);
  76. }
  77. return e;
  78. }
  79. function uuid() {
  80. return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".
  81. replace(/x/g, () => "0123456789abcdef"[Math.random() * 16 | 0]).
  82. replace(/y/g, () => "89ab"[Math.random() * 4 | 0]);
  83. }
  84. async function loadScript(src, ctx = null, env = {}) {
  85. const js = await fetch(src).then(res => res.text());
  86. const keys = [];
  87. const values = [];
  88. for (const [key, value] of Object.entries(env)) {
  89. keys.push(key);
  90. values.push(value);
  91. }
  92. unsafeWindow["Function"](keys.join(), js).apply(ctx, values);
  93. }
  94. const eventListeners = {};
  95. const events = {
  96. on(name, listener) {
  97. const listeners = (name in eventListeners ? eventListeners[name] : eventListeners[name] = []);
  98. listeners.push(listener);
  99. },
  100. trig(name) {
  101. if (name in eventListeners) {
  102. for (const listener of eventListeners[name])
  103. listener();
  104. }
  105. },
  106. };
  107. class ObservableValue {
  108. _value;
  109. _listeners;
  110. constructor(value) {
  111. this._value = value;
  112. this._listeners = new Set();
  113. }
  114. get value() {
  115. return this._value;
  116. }
  117. set value(value) {
  118. this._value = value;
  119. for (const listener of this._listeners)
  120. listener(value);
  121. }
  122. addListener(listener) {
  123. this._listeners.add(listener);
  124. listener(this._value);
  125. }
  126. removeListener(listener) {
  127. this._listeners.delete(listener);
  128. }
  129. map(f) {
  130. const y = new ObservableValue(f(this.value));
  131. this.addListener(x => {
  132. y.value = f(x);
  133. });
  134. return y;
  135. }
  136. }
  137.  
  138. var hPage = "<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n <title>AtCoder Easy Test</title>\n <link href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css\" rel=\"stylesheet\">\n </head>\n <body>\n <div class=\"container\" id=\"root\">\n </div>\n <script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js\"></script>\n <script src=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js\"></script>\n </body>\n</html>";
  139.  
  140. const components = [];
  141. const settings = {
  142. add(title, generator) {
  143. components.push({ title, generator });
  144. },
  145. open() {
  146. const win = window.open("about:blank");
  147. const doc = win.document;
  148. doc.open();
  149. doc.write(hPage);
  150. doc.close();
  151. const root = doc.getElementById("root");
  152. for (const { title, generator } of components) {
  153. const panel = newElement("div", { className: "panel panel-default" }, [
  154. newElement("div", { className: "panel-heading", textContent: title }),
  155. newElement("div", { className: "panel-body" }, [generator(win)]),
  156. ]);
  157. root.appendChild(panel);
  158. }
  159. },
  160. };
  161.  
  162. const options = [];
  163. let data = {};
  164. function toString() {
  165. return JSON.stringify(data);
  166. }
  167. function save() {
  168. GM_setValue("config", toString());
  169. }
  170. function load() {
  171. data = JSON.parse(GM_getValue("config") || "{}");
  172. }
  173. function reset() {
  174. data = {};
  175. save();
  176. }
  177. load();
  178. // 設定ページ
  179. settings.add("config", (win) => {
  180. const root = newElement("form", { className: "form-horizontal" });
  181. options.sort((a, b) => {
  182. const x = a.key.split(".");
  183. const y = b.key.split(".");
  184. return x < y ? -1 : x > y ? 1 : 0;
  185. });
  186. for (const { type, key, defaultValue, description } of options) {
  187. const id = uuid();
  188. const control = newElement("div", { className: "col-sm-3 text-center" });
  189. const group = newElement("div", { className: "form-group" }, [
  190. control,
  191. newElement("label", {
  192. className: "col-sm-3",
  193. htmlFor: id,
  194. textContent: key,
  195. style: {
  196. fontFamily: "monospace",
  197. },
  198. }),
  199. newElement("label", {
  200. className: "col-sm-6",
  201. htmlFor: id,
  202. textContent: description,
  203. }),
  204. ]);
  205. root.appendChild(group);
  206. switch (type) {
  207. case "flag": {
  208. control.appendChild(newElement("input", {
  209. id,
  210. type: "checkbox",
  211. checked: config.get(key, defaultValue),
  212. onchange() {
  213. config.set(key, this.checked);
  214. },
  215. }));
  216. break;
  217. }
  218. case "count": {
  219. control.appendChild(newElement("input", {
  220. id,
  221. type: "number",
  222. min: "0",
  223. value: config.get(key, defaultValue),
  224. onchange() {
  225. config.set(key, +this.value);
  226. },
  227. }));
  228. break;
  229. }
  230. default:
  231. throw new TypeError(`AtCoderEasyTest.setting: undefined option type ${type} for ${key}`);
  232. }
  233. }
  234. root.appendChild(newElement("button", {
  235. className: "btn btn-danger",
  236. textContent: "Reset",
  237. type: "button",
  238. onclick() {
  239. if (win.confirm("Configuration data will be cleared. Are you sure?")) {
  240. config.reset();
  241. }
  242. },
  243. }));
  244. return root;
  245. });
  246. const config = {
  247. getString(key, defaultValue = "") {
  248. if (!(key in data))
  249. config.setString(key, defaultValue);
  250. return data[key];
  251. },
  252. setString(key, value) {
  253. data[key] = value;
  254. save();
  255. },
  256. has(key) {
  257. return key in data;
  258. },
  259. get(key, defaultValue = null) {
  260. if (!(key in data))
  261. config.set(key, defaultValue);
  262. return JSON.parse(data[key]);
  263. },
  264. set(key, value) {
  265. config.setString(key, JSON.stringify(value));
  266. },
  267. save,
  268. load,
  269. toString,
  270. reset,
  271. /** 設定項目を登録 */
  272. registerFlag(key, defaultValue, description) {
  273. options.push({
  274. type: "flag",
  275. key,
  276. defaultValue,
  277. description,
  278. });
  279. },
  280. registerCount(key, defaultValue, description) {
  281. options.push({
  282. type: "count",
  283. key,
  284. defaultValue,
  285. description,
  286. });
  287. },
  288. };
  289.  
  290. config.registerCount("codeSaver.limit", 10, "Max number to save codes");
  291. const codeSaver = {
  292. get() {
  293. // `json` は、ソースコード文字列またはJSON文字列
  294. let json = unsafeWindow.localStorage.AtCoderEasyTest$lastCode;
  295. let data = [];
  296. try {
  297. if (typeof json == "string") {
  298. data.push(...JSON.parse(json));
  299. }
  300. else {
  301. data = [];
  302. }
  303. }
  304. catch (e) {
  305. data.push({
  306. path: unsafeWindow.localStorage.AtCoderEasyTset$lastPage,
  307. code: json,
  308. });
  309. }
  310. return data;
  311. },
  312. set(data) {
  313. unsafeWindow.localStorage.AtCoderEasyTest$lastCode = JSON.stringify(data);
  314. },
  315. save(savePath, code) {
  316. let data = codeSaver.get();
  317. const idx = data.findIndex(({ path }) => path == savePath);
  318. if (idx != -1)
  319. data.splice(idx, idx + 1);
  320. data.push({
  321. path: savePath,
  322. code,
  323. });
  324. while (data.length > config.get("codeSaver.limit", 10))
  325. data.shift();
  326. codeSaver.set(data);
  327. },
  328. restore(savedPath) {
  329. const data = codeSaver.get();
  330. const idx = data.findIndex(({ path }) => path === savedPath);
  331. if (idx == -1 || !(data[idx] instanceof Object))
  332. return Promise.reject(`No saved code found for ${location.pathname}`);
  333. return Promise.resolve(data[idx].code);
  334. }
  335. };
  336. settings.add(`codeSaver (${location.host})`, (win) => {
  337. const root = newElement("table", { className: "table" }, [
  338. newElement("thead", {}, [
  339. newElement("tr", {}, [
  340. newElement("th", { textContent: "path" }),
  341. newElement("th", { textContent: "code" }),
  342. ]),
  343. ]),
  344. newElement("tbody"),
  345. ]);
  346. root.tBodies;
  347. for (const savedCode of codeSaver.get()) {
  348. root.tBodies[0].appendChild(newElement("tr", {}, [
  349. newElement("td", { textContent: savedCode.path }),
  350. newElement("td", {}, [
  351. newElement("textarea", {
  352. rows: 1,
  353. cols: 30,
  354. textContent: savedCode.code,
  355. }),
  356. ]),
  357. ]));
  358. }
  359. return root;
  360. });
  361.  
  362. function similarLangs(targetLang, candidateLangs) {
  363. const [targetName, targetDetail] = targetLang.split(" ", 2);
  364. const selectedLangs = candidateLangs.filter(candidateLang => {
  365. const [name, _] = candidateLang.split(" ", 2);
  366. return name == targetName;
  367. }).map(candidateLang => {
  368. const [_, detail] = candidateLang.split(" ", 2);
  369. return [candidateLang, similarity(detail, targetDetail)];
  370. });
  371. return selectedLangs.sort((a, b) => a[1] - b[1]).map(([lang, _]) => lang);
  372. }
  373. function similarity(s, t) {
  374. const n = s.length, m = t.length;
  375. let dp = new Array(m + 1).fill(0);
  376. for (let i = 0; i < n; i++) {
  377. const dp2 = new Array(m + 1).fill(0);
  378. for (let j = 0; j < m; j++) {
  379. const cost = (s.charCodeAt(i) - t.charCodeAt(j)) ** 2;
  380. dp2[j + 1] = Math.min(dp[j] + cost, dp[j + 1] + cost * 0.25, dp2[j] + cost * 0.25);
  381. }
  382. dp = dp2;
  383. }
  384. return dp[m];
  385. }
  386.  
  387. class CodeRunner {
  388. get label() {
  389. return this._label;
  390. }
  391. constructor(label, site) {
  392. this._label = `${label} [${site}]`;
  393. }
  394. async test(sourceCode, input, expectedOutput, options) {
  395. let result = { status: "IE", input };
  396. try {
  397. result = await this.run(sourceCode, input, options);
  398. }
  399. catch (e) {
  400. result.error = e.toString();
  401. return result;
  402. }
  403. if (expectedOutput != null)
  404. result.expectedOutput = expectedOutput;
  405. if (result.status != "OK" || typeof expectedOutput != "string")
  406. return result;
  407. let output = result.output || "";
  408. if (options.trim) {
  409. expectedOutput = expectedOutput.trim();
  410. output = output.trim();
  411. }
  412. let equals = (x, y) => x === y;
  413. if (options.allowableError) {
  414. const floatPattern = /^[-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?$/;
  415. const superEquals = equals;
  416. equals = (x, y) => {
  417. if (floatPattern.test(x) || floatPattern.test(y)) {
  418. const a = parseFloat(x);
  419. const b = parseFloat(y);
  420. return Math.abs(a - b) <= Math.max(options.allowableError, Math.abs(b) * options.allowableError);
  421. }
  422. return superEquals(x, y);
  423. };
  424. }
  425. if (options.split) {
  426. const superEquals = equals;
  427. equals = (x, y) => {
  428. const xs = x.split(/\s+/);
  429. const ys = y.split(/\s+/);
  430. if (xs.length != ys.length)
  431. return false;
  432. const len = xs.length;
  433. for (let i = 0; i < len; i++) {
  434. if (!superEquals(xs[i], ys[i]))
  435. return false;
  436. }
  437. return true;
  438. };
  439. }
  440. result.status = equals(output, expectedOutput) ? "AC" : "WA";
  441. return result;
  442. }
  443. }
  444.  
  445. class CustomRunner extends CodeRunner {
  446. run;
  447. constructor(label, run) {
  448. super(label, "Browser");
  449. this.run = run;
  450. }
  451. }
  452.  
  453. let waitAtCoderCustomTest = Promise.resolve();
  454. const AtCoderCustomTestBase = location.href.replace(/\/tasks\/.+$/, "/custom_test");
  455. const AtCoderCustomTestResultAPI = AtCoderCustomTestBase + "/json?reload=true";
  456. const AtCoderCustomTestSubmitAPI = AtCoderCustomTestBase + "/submit/json";
  457. const ce_groups = new Set();
  458. class AtCoderRunner extends CodeRunner {
  459. languageId;
  460. constructor(languageId, label) {
  461. super(label, "AtCoder");
  462. this.languageId = languageId;
  463. }
  464. async run(sourceCode, input, options = {}) {
  465. const promise = this.submit(sourceCode, input, options);
  466. waitAtCoderCustomTest = promise;
  467. return await promise;
  468. }
  469. async submit(sourceCode, input, options = {}) {
  470. try {
  471. await waitAtCoderCustomTest;
  472. }
  473. catch (error) {
  474. console.error(error);
  475. }
  476. // 同じグループで CE なら実行を省略し CE を返す
  477. if ("runGroupId" in options && ce_groups.has(options.runGroupId)) {
  478. return {
  479. status: "CE",
  480. input,
  481. };
  482. }
  483. const error = await fetch(AtCoderCustomTestSubmitAPI, {
  484. method: "POST",
  485. credentials: "include",
  486. headers: {
  487. "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
  488. },
  489. body: buildParams({
  490. "data.LanguageId": String(this.languageId),
  491. sourceCode,
  492. input,
  493. csrf_token: unsafeWindow.csrfToken,
  494. }),
  495. }).then(r => r.text());
  496. if (error) {
  497. throw new Error(error);
  498. }
  499. await sleep(100);
  500. for (;;) {
  501. const data = await fetch(AtCoderCustomTestResultAPI, {
  502. method: "GET",
  503. credentials: "include",
  504. }).then(r => r.json());
  505. if (!("Result" in data))
  506. continue;
  507. const result = data.Result;
  508. if ("Interval" in data) {
  509. await sleep(data.Interval);
  510. continue;
  511. }
  512. const status = (result.ExitCode == 0) ? "OK" : (result.TimeConsumption == -1) ? "CE" : "RE";
  513. if (status == "CE" && "runGroupId" in options) {
  514. ce_groups.add(options.runGroupId);
  515. }
  516. return {
  517. status,
  518. exitCode: result.ExitCode,
  519. execTime: result.TimeConsumption,
  520. memory: result.MemoryConsumption,
  521. input,
  522. output: data.Stdout,
  523. error: data.Stderr,
  524. };
  525. }
  526. }
  527. }
  528.  
  529. class PaizaIORunner extends CodeRunner {
  530. name;
  531. constructor(name, label) {
  532. super(label, "PaizaIO");
  533. this.name = name;
  534. }
  535. async run(sourceCode, input, options = {}) {
  536. let id, status, error;
  537. try {
  538. const res = await fetch("https://api.paiza.io/runners/create?" + buildParams({
  539. source_code: sourceCode,
  540. language: this.name,
  541. input,
  542. longpoll: "true",
  543. longpoll_timeout: "10",
  544. api_key: "guest",
  545. }), {
  546. method: "POST",
  547. mode: "cors",
  548. }).then(r => r.json());
  549. id = res.id;
  550. status = res.status;
  551. error = res.error;
  552. }
  553. catch (error) {
  554. return {
  555. status: "IE",
  556. input,
  557. error: String(error),
  558. };
  559. }
  560. while (status == "running") {
  561. const res = await fetch("https://api.paiza.io/runners/get_status?" + buildParams({
  562. id,
  563. api_key: "guest",
  564. }), {
  565. mode: "cors",
  566. }).then(res => res.json());
  567. status = res.status;
  568. error = res.error;
  569. }
  570. const res = await fetch("https://api.paiza.io/runners/get_details?" + buildParams({
  571. id,
  572. api_key: "guest",
  573. }), {
  574. mode: "cors",
  575. }).then(r => r.json());
  576. const result = {
  577. status: "OK",
  578. exitCode: String(res.exit_code),
  579. execTime: +res.time * 1e3,
  580. memory: +res.memory * 1e-3,
  581. input,
  582. };
  583. if (res.build_result == "failure") {
  584. result.status = "CE";
  585. result.exitCode = res.build_exit_code;
  586. result.output = res.build_stdout;
  587. result.error = res.build_stderr;
  588. }
  589. else {
  590. result.status = (res.result == "timeout") ? "TLE" : (res.result == "failure") ? "RE" : "OK";
  591. result.exitCode = res.exit_code;
  592. result.output = res.stdout;
  593. result.error = res.stderr;
  594. }
  595. return result;
  596. }
  597. }
  598.  
  599. class WandboxRunner extends CodeRunner {
  600. name;
  601. options;
  602. constructor(name, label, options = {}) {
  603. super(label, "Wandbox");
  604. this.name = name;
  605. this.options = options;
  606. }
  607. getOptions(sourceCode, input) {
  608. if (typeof this.options == "function")
  609. return this.options(sourceCode, input);
  610. return this.options;
  611. }
  612. run(sourceCode, input, options = {}) {
  613. return this.request(Object.assign({
  614. compiler: this.name,
  615. code: sourceCode,
  616. stdin: input,
  617. }, Object.assign(options, this.getOptions(sourceCode, input))));
  618. }
  619. async request(body) {
  620. const startTime = Date.now();
  621. let res;
  622. try {
  623. res = await fetch("https://wandbox.org/api/compile.json", {
  624. method: "POST",
  625. mode: "cors",
  626. headers: {
  627. "Content-Type": "application/json",
  628. },
  629. body: JSON.stringify(body),
  630. }).then(r => r.json());
  631. }
  632. catch (error) {
  633. console.error(error);
  634. return {
  635. status: "IE",
  636. input: body.stdin,
  637. error: String(error),
  638. };
  639. }
  640. const endTime = Date.now();
  641. const result = {
  642. status: "OK",
  643. exitCode: String(res.status),
  644. execTime: endTime - startTime,
  645. input: body.stdin,
  646. output: String(res.program_output || ""),
  647. error: String(res.program_error || ""),
  648. };
  649. // 正常終了以外の場合
  650. if (res.status != 0) {
  651. if (res.signal) {
  652. result.exitCode += ` (${res.signal})`;
  653. }
  654. result.output = String(res.compiler_output || "") + String(result.output || "");
  655. result.error = String(res.compiler_error || "") + String(result.error || "");
  656. if (res.compiler_output || res.compiler_error) {
  657. result.status = "CE";
  658. }
  659. else {
  660. result.status = "RE";
  661. }
  662. }
  663. return result;
  664. }
  665. }
  666.  
  667. class WandboxCppRunner extends WandboxRunner {
  668. async run(sourceCode, input, options = {}) {
  669. // ACL を結合する
  670. const ACLBase = "https://cdn.jsdelivr.net/gh/atcoder/ac-library/";
  671. const files = new Map();
  672. const includeHeader = async (source) => {
  673. const pattern = /^#\s*include\s*[<"]atcoder\/([^>"]+)[>"]/gm;
  674. const loaded = [];
  675. let match;
  676. while (match = pattern.exec(source)) {
  677. const file = "atcoder/" + match[1];
  678. if (files.has(file))
  679. continue;
  680. files.set(file, null);
  681. loaded.push([file, fetch(ACLBase + file, { mode: "cors", cache: "force-cache", }).then(r => r.text())]);
  682. }
  683. const included = await Promise.all(loaded.map(async ([file, r]) => {
  684. const source = await r;
  685. files.set(file, source);
  686. return source;
  687. }));
  688. for (const source of included) {
  689. await includeHeader(source);
  690. }
  691. };
  692. await includeHeader(sourceCode);
  693. const codes = [];
  694. for (const [file, code] of files) {
  695. codes.push({ file, code, });
  696. }
  697. return await this.request(Object.assign({
  698. compiler: this.name,
  699. code: sourceCode,
  700. stdin: input,
  701. codes,
  702. }, Object.assign(options, this.getOptions(sourceCode, input))));
  703. }
  704. }
  705.  
  706. let brythonRunnerLoaded = false;
  707. const brythonRunner = new CustomRunner("Brython", async (sourceCode, input, options = {}) => {
  708. if (!brythonRunnerLoaded) {
  709. // BrythonRunner を読み込む
  710. await new Promise((resolve) => {
  711. const script = document.createElement("script");
  712. script.src = "https://cdn.jsdelivr.net/gh/pythonpad/brython-runner/lib/brython-runner.bundle.js";
  713. script.onload = () => {
  714. brythonRunnerLoaded = true;
  715. resolve(null);
  716. };
  717. document.head.appendChild(script);
  718. });
  719. }
  720. let stdout = "";
  721. let stderr = "";
  722. let stdinOffset = 0;
  723. const BrythonRunner = unsafeWindow.BrythonRunner;
  724. const runner = new BrythonRunner({
  725. stdout: { write(content) { stdout += content; }, flush() { } },
  726. stderr: { write(content) { stderr += content; }, flush() { } },
  727. stdin: { async readline() {
  728. let index = input.indexOf("\n", stdinOffset) + 1;
  729. if (index == 0)
  730. index = input.length;
  731. const text = input.slice(stdinOffset, index);
  732. stdinOffset = index;
  733. return text;
  734. } },
  735. });
  736. const timeStart = Date.now();
  737. await runner.runCode(sourceCode);
  738. const timeEnd = Date.now();
  739. return {
  740. status: "OK",
  741. exitCode: "0",
  742. execTime: (timeEnd - timeStart),
  743. input,
  744. output: stdout,
  745. error: stderr,
  746. };
  747. });
  748.  
  749. async function loadPyodide() {
  750. const script = await fetch("https://cdn.jsdelivr.net/pyodide/v0.24.0/full/pyodide.js").then((res) => res.text());
  751. unsafeWindow["Function"](script)();
  752. const pyodide = await unsafeWindow["loadPyodide"]({
  753. indexURL: "https://cdn.jsdelivr.net/pyodide/v0.24.0/full/",
  754. });
  755. await pyodide.runPythonAsync(`
  756. import contextlib, io, platform
  757. class __redirect_stdin(contextlib._RedirectStream):
  758. _stream = "stdin"
  759. `);
  760. return pyodide;
  761. }
  762. let _pyodide = Promise.reject("Pyodide is not yet loaded");
  763. let _serial = Promise.resolve();
  764. const pyodideRunner = new CustomRunner("Pyodide", (sourceCode, input, options = {}) => new Promise((resolve, reject) => {
  765. _serial = _serial.finally(async () => {
  766. const pyodide = await (_pyodide = _pyodide.catch(loadPyodide));
  767. const code = `
  768. def __run():
  769. global __stdout, __stderr, __stdin, __code
  770. with __redirect_stdin(io.StringIO(__stdin)):
  771. with contextlib.redirect_stdout(io.StringIO()) as __stdout:
  772. with contextlib.redirect_stderr(io.StringIO()) as __stderr:
  773. try:
  774. pass
  775. ` +
  776. sourceCode
  777. .split("\n")
  778. .map((line) => " " + line)
  779. .join("\n") +
  780. `
  781. except SystemExit as e:
  782. __code = e.code
  783. `;
  784. let status = "OK";
  785. let exitCode = "0";
  786. let stdout = "";
  787. let stderr = "";
  788. let startTime = -Infinity;
  789. let endTime = Infinity;
  790. pyodide.globals.set("__stdin", input);
  791. try {
  792. pyodide.globals.set("__code", null);
  793. await pyodide.loadPackagesFromImports(code);
  794. await pyodide.runPythonAsync(code);
  795. startTime = Date.now();
  796. pyodide.runPython("__run()");
  797. endTime = Date.now();
  798. stdout = pyodide.globals.get("__stdout").getvalue();
  799. stderr = pyodide.globals.get("__stderr").getvalue();
  800. const __code = pyodide.globals.get("__code");
  801. if (typeof __code == "number") {
  802. exitCode = String(__code);
  803. if (__code != 0)
  804. status = "RE";
  805. }
  806. }
  807. catch (error) {
  808. status = "RE";
  809. exitCode = "-1";
  810. stderr += error.toString();
  811. }
  812. resolve({
  813. status,
  814. exitCode,
  815. execTime: endTime - startTime,
  816. input,
  817. output: stdout,
  818. error: stderr,
  819. });
  820. });
  821. }));
  822.  
  823. function pairs(list) {
  824. const pairs = [];
  825. const len = list.length >> 1;
  826. for (let i = 0; i < len; i++)
  827. pairs.push([list[i * 2], list[i * 2 + 1]]);
  828. return pairs;
  829. }
  830. async function init$5() {
  831. if (location.host != "atcoder.jp")
  832. throw "Not AtCoder";
  833. const doc = unsafeWindow.document;
  834. // "言語名 その他の説明..." となっている
  835. // 注意:
  836. // * 言語名にはスペースが入ってはいけない(スペース以降は説明とみなされる)
  837. // * Python2 の言語名は「Python」、 Python3 の言語名は「Python3」
  838. const langMap = {
  839. 4001: "C GCC 9.2.1",
  840. 4002: "C Clang 10.0.0",
  841. 4003: "C++ GCC 9.2.1",
  842. 4004: "C++ Clang 10.0.0",
  843. 4005: "Java OpenJDK 11.0.6",
  844. 4006: "Python3 CPython 3.8.2",
  845. 4007: "Bash 5.0.11",
  846. 4008: "bc 1.07.1",
  847. 4009: "Awk GNU Awk 4.1.4",
  848. 4010: "C# .NET Core 3.1.201",
  849. 4011: "C# Mono-mcs 6.8.0.105",
  850. 4012: "C# Mono-csc 3.5.0",
  851. 4013: "Clojure 1.10.1.536",
  852. 4014: "Crystal 0.33.0",
  853. 4015: "D DMD 2.091.0",
  854. 4016: "D GDC 9.2.1",
  855. 4017: "D LDC 1.20.1",
  856. 4018: "Dart 2.7.2",
  857. 4019: "dc 1.4.1",
  858. 4020: "Erlang 22.3",
  859. 4021: "Elixir 1.10.2",
  860. 4022: "F# .NET Core 3.1.201",
  861. 4023: "F# Mono 10.2.3",
  862. 4024: "Forth gforth 0.7.3",
  863. 4025: "Fortran GNU Fortran 9.2.1",
  864. 4026: "Go 1.14.1",
  865. 4027: "Haskell GHC 8.8.3",
  866. 4028: "Haxe 4.0.3",
  867. 4029: "Haxe 4.0.3",
  868. 4030: "JavaScript Node.js 12.16.1",
  869. 4031: "Julia 1.4.0",
  870. 4032: "Kotlin 1.3.71",
  871. 4033: "Lua Lua 5.3.5",
  872. 4034: "Lua LuaJIT 2.1.0",
  873. 4035: "Dash 0.5.8",
  874. 4036: "Nim 1.0.6",
  875. 4037: "Objective-C Clang 10.0.0",
  876. 4038: "Lisp SBCL 2.0.3",
  877. 4039: "OCaml 4.10.0",
  878. 4040: "Octave 5.2.0",
  879. 4041: "Pascal FPC 3.0.4",
  880. 4042: "Perl 5.26.1",
  881. 4043: "Raku Rakudo 2020.02.1",
  882. 4044: "PHP 7.4.4",
  883. 4045: "Prolog SWI-Prolog 8.0.3",
  884. 4046: "Python PyPy2 7.3.0",
  885. 4047: "Python3 PyPy3 7.3.0",
  886. 4048: "Racket 7.6",
  887. 4049: "Ruby 2.7.1",
  888. 4050: "Rust 1.42.0",
  889. 4051: "Scala 2.13.1",
  890. 4052: "Java OpenJDK 1.8.0",
  891. 4053: "Scheme Gauche 0.9.9",
  892. 4054: "ML MLton 20130715",
  893. 4055: "Swift 5.2.1",
  894. 4056: "Text cat 8.28",
  895. 4057: "TypeScript 3.8",
  896. 4058: "Basic .NET Core 3.1.101",
  897. 4059: "Zsh 5.4.2",
  898. 4060: "COBOL Fixed OpenCOBOL 1.1.0",
  899. 4061: "COBOL Free OpenCOBOL 1.1.0",
  900. 4062: "Brainfuck bf 20041219",
  901. 4063: "Ada Ada2012 GNAT 9.2.1",
  902. 4064: "Unlambda 2.0.0",
  903. 4065: "Cython 0.29.16",
  904. 4066: "Sed 4.4",
  905. 4067: "Vim 8.2.0460",
  906. // newjudge-2308
  907. 5001: "C++ 20 gcc 12.2",
  908. 5002: "Go 1.20.6",
  909. 5003: "C# 11.0 .NET 7.0.7",
  910. 5004: "Kotlin 1.8.20",
  911. 5005: "Java OpenJDK 17",
  912. 5006: "Nim 1.6.14",
  913. 5007: "V 0.4",
  914. 5008: "Zig 0.10.1",
  915. 5009: "JavaScript Node.js 18.16.1",
  916. 5010: "JavaScript Deno 1.35.1",
  917. 5011: "R GNU R 4.2.1",
  918. 5012: "D DMD 2.104.0",
  919. 5013: "D LDC 1.32.2",
  920. 5014: "Swift 5.8.1",
  921. 5015: "Dart 3.0.5",
  922. 5016: "PHP 8.2.8",
  923. 5017: "C GCC 12.2.0",
  924. 5018: "Ruby 3.2.2",
  925. 5019: "Crystal 1.9.1",
  926. 5020: "Brainfuck bf 20041219",
  927. 5021: "F# 7.0 .NET 7.0.7",
  928. 5022: "Julia 1.9.2",
  929. 5023: "Bash 5.2.2",
  930. 5024: "Text cat 8.32",
  931. 5025: "Haskell GHC 9.4.5",
  932. 5026: "Fortran GNU Fortran 12.2",
  933. 5027: "Lua LuaJIT 2.1.0-beta3",
  934. 5028: "C++ 23 gcc 12.2",
  935. 5029: "CommonLisp SBCL 2.3.6",
  936. 5030: "COBOL Free GnuCOBOL 3.1.2",
  937. 5031: "C++ 23 Clang 16.0.5",
  938. 5032: "Zsh Zsh 5.9",
  939. 5033: "SageMath SageMath 9.5",
  940. 5034: "Sed GNU sed 4.8",
  941. 5035: "bc bc 1.07.1",
  942. 5036: "dc dc 1.07.1",
  943. 5037: "Perl perl 5.34",
  944. 5038: "AWK GNU Awk 5.0.1",
  945. 5039: "なでしこ cnako3 3.4.20",
  946. 5040: "Assembly x64 NASM 2.15.05",
  947. 5041: "Pascal fpc 3.2.2",
  948. 5042: "C# 11.0 AOT .NET 7.0.7",
  949. 5043: "Lua Lua 5.4.6",
  950. 5044: "Prolog SWI-Prolog 9.0.4",
  951. 5045: "PowerShell PowerShell 7.3.1",
  952. 5046: "Scheme Gauche 0.9.12",
  953. 5047: "Scala 3.3.0 Scala Native 0.4.14",
  954. 5048: "Visual Basic 16.9 .NET 7.0.7",
  955. 5049: "Forth gforth 0.7.3",
  956. 5050: "Clojure babashka 1.3.181",
  957. 5051: "Erlang Erlang 26.0.2",
  958. 5052: "TypeScript 5.1 Deno 1.35.1",
  959. 5053: "C++ 17 gcc 12.2",
  960. 5054: "Rust rustc 1.70.0",
  961. 5055: "Python3 CPython 3.11.4",
  962. 5056: "Scala Dotty 3.3.0",
  963. 5057: "Koka koka 2.4.0",
  964. 5058: "TypeScript 5.1 Node.js 18.16.1",
  965. 5059: "OCaml ocamlopt 5.0.0",
  966. 5060: "Raku Rakudo 2023.06",
  967. 5061: "Vim vim 9.0.0242",
  968. 5062: "Emacs Lisp Native Compile GNU Emacs 28.2",
  969. 5063: "Python3 Mambaforge / CPython 3.10.10",
  970. 5064: "Clojure clojure 1.11.1",
  971. 5065: "プロデル mono版プロデル 1.9.1182",
  972. 5066: "ECLiPSe ECLiPSe 7.1_13",
  973. 5067: "Nibbles literate form nibbles 1.01",
  974. 5068: "Ada GNAT 12.2",
  975. 5069: "jq jq 1.6",
  976. 5070: "Cyber Cyber v0.2-Latest",
  977. 5071: "Carp Carp 0.5.5",
  978. 5072: "C++ 17 Clang 16.0.5",
  979. 5073: "C++ 20 Clang 16.0.5",
  980. 5074: "LLVM IR Clang 16.0.5",
  981. 5075: "Emacs Lisp Byte Compile GNU Emacs 28.2",
  982. 5076: "Factor Factor 0.98",
  983. 5077: "D GDC 12.2",
  984. 5078: "Python3 PyPy 3.10-v7.3.12",
  985. 5079: "Whitespace whitespacers 1.0.0",
  986. 5080: "><> fishr 0.1.0",
  987. 5081: "ReasonML reason 3.9.0",
  988. 5082: "Python Cython 0.29.34",
  989. 5083: "Octave GNU Octave 8.2.0",
  990. 5084: "Haxe JVM Haxe 4.3.1",
  991. 5085: "Elixir Elixir 1.15.2",
  992. 5086: "Mercury Mercury 22.01.6",
  993. 5087: "Seed7 Seed7 3.2.1",
  994. 5088: "Emacs Lisp No Compile GNU Emacs 28.2",
  995. 5089: "Unison Unison M5b",
  996. 5090: "COBOL GnuCOBOLFixed 3.1.2",
  997. };
  998. const languageId = new ObservableValue(unsafeWindow.$("#select-lang select.current").val());
  999. unsafeWindow.$("#select-lang select").change(() => {
  1000. languageId.value = unsafeWindow.$("#select-lang select.current").val();
  1001. });
  1002. const language = languageId.map(lang => langMap[lang]);
  1003. const isTestCasesHere = /^\/contests\/[^\/]+\/tasks\//.test(location.pathname);
  1004. const taskSelector = doc.querySelector("#select-task");
  1005. function getTaskURI() {
  1006. if (taskSelector)
  1007. return `${location.origin}/contests/${unsafeWindow.contestScreenName}/tasks/${taskSelector.value}`;
  1008. return `${location.origin}${location.pathname}`;
  1009. }
  1010. const testcasesCache = {};
  1011. if (taskSelector) {
  1012. const doFetchTestCases = async () => {
  1013. console.log(`Fetching test cases...: ${getTaskURI()}`);
  1014. const taskURI = getTaskURI();
  1015. const load = !(taskURI in testcasesCache) || testcasesCache[taskURI].state == "error";
  1016. if (!load)
  1017. return;
  1018. try {
  1019. testcasesCache[taskURI] = { state: "loading" };
  1020. const testcases = await fetchTestCases(taskURI);
  1021. testcasesCache[taskURI] = { testcases, state: "loaded" };
  1022. }
  1023. catch (e) {
  1024. testcasesCache[taskURI] = { state: "error" };
  1025. }
  1026. };
  1027. unsafeWindow.$("#select-task").change(doFetchTestCases);
  1028. doFetchTestCases();
  1029. }
  1030. async function fetchTestCases(taskUrl) {
  1031. const html = await fetch(taskUrl).then(res => res.text());
  1032. const taskDoc = new DOMParser().parseFromString(html, "text/html");
  1033. return getTestCases(taskDoc);
  1034. }
  1035. function getTestCases(doc) {
  1036. const selectors = [
  1037. ["#task-statement p+pre.literal-block", ".section"],
  1038. ["#task-statement pre.source-code-for-copy", ".part"],
  1039. ["#task-statement .lang>*:nth-child(1) .div-btn-copy+pre", ".part"],
  1040. ["#task-statement .div-btn-copy+pre", ".part"],
  1041. ["#task-statement>.part pre.linenums", ".part"],
  1042. ["#task-statement>.part section>pre", ".part"],
  1043. ["#task-statement>.part:not(.io-style)>h3+section>pre", ".part"],
  1044. ["#task-statement pre", ".part"],
  1045. ];
  1046. for (const [selector, closestSelector] of selectors) {
  1047. let e = [...doc.querySelectorAll(selector)];
  1048. e = e.filter(e => {
  1049. if (e.closest(".io-style"))
  1050. return false; // practice2
  1051. if (e.querySelector("var"))
  1052. return false;
  1053. return true;
  1054. });
  1055. if (e.length == 0)
  1056. continue;
  1057. return pairs(e).map(([input, output], index) => {
  1058. const container = input.closest(closestSelector) || input.parentElement;
  1059. return {
  1060. selector,
  1061. title: `Sample ${index + 1}`,
  1062. input: input.textContent,
  1063. output: output.textContent,
  1064. anchor: container.querySelector(".btn-copy") || container.querySelector("h1,h2,h3,h4,h5,h6"),
  1065. };
  1066. });
  1067. }
  1068. { // maximum_cup_2018_d
  1069. let e = [...doc.querySelectorAll("#task-statement .div-btn-copy+pre")];
  1070. e = e.filter(f => !f.childElementCount);
  1071. if (e.length) {
  1072. return pairs(e).map(([input, output], index) => ({
  1073. selector: "#task-statement .div-btn-copy+pre",
  1074. title: `Sample ${index + 1}`,
  1075. input: input.textContent,
  1076. output: output.textContent,
  1077. anchor: (input.closest(".part") || input.parentElement).querySelector(".btn-copy"),
  1078. }));
  1079. }
  1080. }
  1081. return [];
  1082. }
  1083. const atcoder = {
  1084. name: "AtCoder",
  1085. language,
  1086. langMap,
  1087. get sourceCode() {
  1088. const $ = unsafeWindow.document.querySelector.bind(unsafeWindow.document);
  1089. if (typeof unsafeWindow["ace"] != "undefined") {
  1090. if (!$(".btn-toggle-editor").classList.contains("active")) {
  1091. return unsafeWindow["ace"].edit($("#editor")).getValue();
  1092. }
  1093. else {
  1094. return $("#plain-textarea").value;
  1095. }
  1096. }
  1097. else {
  1098. return unsafeWindow.getSourceCode();
  1099. }
  1100. },
  1101. set sourceCode(sourceCode) {
  1102. const $ = unsafeWindow.document.querySelector.bind(unsafeWindow.document);
  1103. if (typeof unsafeWindow["ace"] != "undefined") {
  1104. unsafeWindow["ace"].edit($("#editor")).setValue(sourceCode);
  1105. $("#plain-textarea").value = sourceCode;
  1106. }
  1107. else {
  1108. doc.querySelector(".plain-textarea").value = sourceCode;
  1109. unsafeWindow.$(".editor").data("editor").doc.setValue(sourceCode);
  1110. }
  1111. },
  1112. submit() {
  1113. doc.querySelector("#submit").click();
  1114. },
  1115. get testButtonContainer() {
  1116. return doc.querySelector("#submit").parentElement;
  1117. },
  1118. get sideButtonContainer() {
  1119. return doc.querySelector(".editor-buttons");
  1120. },
  1121. get bottomMenuContainer() {
  1122. return doc.getElementById("main-div");
  1123. },
  1124. get resultListContainer() {
  1125. return doc.querySelector(".form-code-submit");
  1126. },
  1127. get testCases() {
  1128. const taskURI = getTaskURI();
  1129. if (taskURI in testcasesCache && testcasesCache[taskURI].state == "loaded")
  1130. return testcasesCache[taskURI].testcases;
  1131. if (isTestCasesHere) {
  1132. const testcases = getTestCases(doc);
  1133. testcasesCache[taskURI] = { testcases, state: "loaded" };
  1134. return testcases;
  1135. }
  1136. else {
  1137. console.error("AtCoder Easy Test v2: Test cases are still not loaded");
  1138. return [];
  1139. }
  1140. },
  1141. get jQuery() {
  1142. return unsafeWindow["jQuery"];
  1143. },
  1144. get taskURI() {
  1145. return getTaskURI();
  1146. },
  1147. };
  1148. return atcoder;
  1149. }
  1150.  
  1151. async function init$4() {
  1152. if (location.host != "yukicoder.me")
  1153. throw "Not yukicoder";
  1154. const $ = unsafeWindow.$;
  1155. const doc = unsafeWindow.document;
  1156. const editor = unsafeWindow.ace.edit("rich_source");
  1157. const eSourceObject = $("#source");
  1158. const eLang = $("#lang");
  1159. const eSamples = $(".sample");
  1160. const langMap = {
  1161. "cpp14": "C++ C++14 GCC 11.1.0 + Boost 1.77.0",
  1162. "cpp17": "C++ C++17 GCC 11.1.0 + Boost 1.77.0",
  1163. "cpp-clang": "C++ C++17 Clang 10.0.0 + Boost 1.76.0",
  1164. "cpp23": "C++ C++11 GCC 8.4.1",
  1165. "c11": "C++ C++11 GCC 11.1.0",
  1166. "c": "C C90 GCC 8.4.1",
  1167. "java8": "Java Java16 OpenJDK 16.0.1",
  1168. "csharp": "C# CSC 3.9.0",
  1169. "csharp_mono": "C# Mono 6.12.0.147",
  1170. "csharp_dotnet": "C# .NET 5.0",
  1171. "perl": "Perl 5.26.3",
  1172. "raku": "Raku Rakudo v2021-07-2-g74d7ff771",
  1173. "php": "PHP 7.2.24",
  1174. "php7": "PHP 8.0.8",
  1175. "python3": "Python3 3.9.6 + numpy 1.14.5 + scipy 1.1.0",
  1176. "pypy2": "Python PyPy2 7.3.5",
  1177. "pypy3": "Python3 PyPy3 7.3.5",
  1178. "ruby": "Ruby 3.0.2p107",
  1179. "d": "D DMD 2.097.1",
  1180. "go": "Go 1.16.6",
  1181. "haskell": "Haskell 8.10.5",
  1182. "scala": "Scala 2.13.6",
  1183. "nim": "Nim 1.4.8",
  1184. "rust": "Rust 1.53.0",
  1185. "kotlin": "Kotlin 1.5.21",
  1186. "scheme": "Scheme Gauche 0.9.10",
  1187. "crystal": "Crystal 1.1.1",
  1188. "swift": "Swift 5.4.2",
  1189. "ocaml": "OCaml 4.12.0",
  1190. "clojure": "Clojure 1.10.2.790",
  1191. "fsharp": "F# 5.0",
  1192. "elixir": "Elixir 1.7.4",
  1193. "lua": "Lua LuaJIT 2.0.5",
  1194. "fortran": "Fortran gFortran 8.4.1",
  1195. "node": "JavaScript Node.js 15.5.0",
  1196. "typescript": "TypeScript 4.3.5",
  1197. "lisp": "Lisp Common Lisp sbcl 2.1.6",
  1198. "sml": "ML Standard ML MLton 20180207-6",
  1199. "kuin": "Kuin KuinC++ v.2021.7.17",
  1200. "vim": "Vim v8.2",
  1201. "sh": "Bash 4.4.19",
  1202. "nasm": "Assembler nasm 2.13.03",
  1203. "clay": "cLay 20210917-1",
  1204. "bf": "Brainfuck BFI 1.1",
  1205. "Whitespace": "Whitespace 0.3",
  1206. "text": "Text cat 8.3",
  1207. };
  1208. // place anchor elements
  1209. for (const btnCopyInput of doc.querySelectorAll(".copy-sample-input")) {
  1210. btnCopyInput.parentElement.insertBefore(newElement("span", { className: "atcoder-easy-test-anchor" }), btnCopyInput);
  1211. }
  1212. const language = new ObservableValue(langMap[eLang.val()]);
  1213. eLang.on("change", () => {
  1214. language.value = langMap[eLang.val()];
  1215. });
  1216. return {
  1217. name: "yukicoder",
  1218. language,
  1219. get sourceCode() {
  1220. if (eSourceObject.is(":visible"))
  1221. return eSourceObject.val();
  1222. return editor.getSession().getValue();
  1223. },
  1224. set sourceCode(sourceCode) {
  1225. eSourceObject.val(sourceCode);
  1226. editor.getSession().setValue(sourceCode);
  1227. },
  1228. submit() {
  1229. doc.querySelector(`#submit_form input[type="submit"]`).click();
  1230. },
  1231. get testButtonContainer() {
  1232. return doc.querySelector("#submit_form");
  1233. },
  1234. get sideButtonContainer() {
  1235. return doc.querySelector("#toggle_source_editor").parentElement;
  1236. },
  1237. get bottomMenuContainer() {
  1238. return doc.body;
  1239. },
  1240. get resultListContainer() {
  1241. return doc.querySelector("#content");
  1242. },
  1243. get testCases() {
  1244. const testCases = [];
  1245. let sampleId = 1;
  1246. for (let i = 0; i < eSamples.length; i++) {
  1247. const eSample = eSamples.eq(i);
  1248. const [eInput, eOutput] = eSample.find("pre");
  1249. testCases.push({
  1250. title: `Sample ${sampleId++}`,
  1251. input: eInput.textContent,
  1252. output: eOutput.textContent,
  1253. anchor: eSample.find(".atcoder-easy-test-anchor")[0],
  1254. });
  1255. }
  1256. return testCases;
  1257. },
  1258. get jQuery() {
  1259. return $;
  1260. },
  1261. get taskURI() {
  1262. return location.href;
  1263. },
  1264. };
  1265. }
  1266.  
  1267. class Editor {
  1268. _element;
  1269. constructor(lang) {
  1270. this._element = document.createElement("textarea");
  1271. this._element.style.fontFamily = "monospace";
  1272. this._element.style.width = "100%";
  1273. this._element.style.minHeight = "5em";
  1274. }
  1275. get element() {
  1276. return this._element;
  1277. }
  1278. get sourceCode() {
  1279. return this._element.value;
  1280. }
  1281. set sourceCode(sourceCode) {
  1282. this._element.value = sourceCode;
  1283. }
  1284. setLanguage(lang) {
  1285. }
  1286. }
  1287.  
  1288. var langMap = {
  1289. 3: "Delphi 7",
  1290. 4: "Pascal Free Pascal 3.0.2",
  1291. 6: "PHP 7.2.13",
  1292. 7: "Python 2.7.18",
  1293. 9: "C# Mono 6.8",
  1294. 12: "Haskell GHC 8.10.1",
  1295. 13: "Perl 5.20.1",
  1296. 19: "OCaml 4.02.1",
  1297. 20: "Scala 2.12.8",
  1298. 28: "D DMD32 v2.091.0",
  1299. 31: "Python3 3.8.10",
  1300. 32: "Go 1.15.6",
  1301. 34: "JavaScript V8 4.8.0",
  1302. 36: "Java 1.8.0_241",
  1303. 40: "Python PyPy2 2.7 (7.3.0)",
  1304. 41: "Python3 PyPy3 3.7 (7.3.0)",
  1305. 43: "C C11 GCC 5.1.0",
  1306. 48: "Kotlin 1.5.31",
  1307. 49: "Rust 1.49.0",
  1308. 50: "C++ C++14 G++ 6.4.0",
  1309. 51: "Pascal PascalABC.NET 3.4.1",
  1310. 52: "C++ C++17 Clang++",
  1311. 54: "C++ C++17 G++ 7.3.0",
  1312. 55: "JavaScript Node.js 12.6.3",
  1313. 59: "C++ Microsoft Visual C++ 2017",
  1314. 60: "Java 11.0.6",
  1315. 61: "C++ C++17 9.2.0 (64 bit, msys 2)",
  1316. 65: "C# 8, .NET Core 3.1",
  1317. 67: "Ruby 3.0.0",
  1318. 70: "Python3 PyPy 3.7 (7.3.5, 64bit)",
  1319. 72: "Kotlin 1.5.31",
  1320. 73: "C++ GNU G++ 11.2.0 (64 bit, winlibs)",
  1321. 89: "C++ GNU G++20 13.2 (64 bit, winlibs)",
  1322. };
  1323.  
  1324. config.registerFlag("site.codeforces.showEditor", true, "Show Editor in Codeforces Problem Page");
  1325. async function init$3() {
  1326. if (location.host != "codeforces.com")
  1327. throw "not Codeforces";
  1328. //TODO: m1.codeforces.com, m2.codeforces.com, m3.codeforces.com に対応する
  1329. const doc = unsafeWindow.document;
  1330. const eLang = doc.querySelector("select[name='programTypeId']");
  1331. doc.head.appendChild(newElement("link", {
  1332. rel: "stylesheet",
  1333. href: "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css",
  1334. }));
  1335. doc.head.appendChild(newElement("style", {
  1336. textContent: `
  1337. .atcoder-easy-test-btn-run-case {
  1338. float: right;
  1339. line-height: 1.1rem;
  1340. }
  1341. `,
  1342. }));
  1343. const eButtons = newElement("span");
  1344. doc.querySelector(".submitForm").appendChild(eButtons);
  1345. await loadScript("https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js");
  1346. const jQuery = unsafeWindow["jQuery"].noConflict();
  1347. unsafeWindow["jQuery"] = unsafeWindow["$"];
  1348. unsafeWindow["jQuery11"] = jQuery;
  1349. await loadScript("https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js", null, { jQuery, $: jQuery });
  1350. const language = new ObservableValue(langMap[eLang.value]);
  1351. eLang.addEventListener("change", () => {
  1352. language.value = langMap[eLang.value];
  1353. });
  1354. let _sourceCode = "";
  1355. const eFile = doc.querySelector(".submitForm").elements["sourceFile"];
  1356. eFile.addEventListener("change", async () => {
  1357. if (eFile.files[0]) {
  1358. _sourceCode = await eFile.files[0].text();
  1359. if (editor)
  1360. editor.sourceCode = _sourceCode;
  1361. }
  1362. });
  1363. let editor = null;
  1364. let waitCfFastSubmitCount = 0;
  1365. const waitCfFastSubmit = setInterval(() => {
  1366. if (document.getElementById("editor")) {
  1367. // cf-fast-submit
  1368. if (editor && editor.element)
  1369. editor.element.style.display = "none";
  1370. // 言語セレクトを同期させる
  1371. const eLang2 = doc.querySelector(".submit-form select[name='programTypeId']");
  1372. if (eLang2) {
  1373. eLang.addEventListener("change", () => {
  1374. eLang2.value = eLang.value;
  1375. });
  1376. eLang2.addEventListener("change", () => {
  1377. eLang.value = eLang2.value;
  1378. language.value = langMap[eLang.value];
  1379. });
  1380. }
  1381. // TODO: 選択されたファイルをどうかする
  1382. // エディタを使う
  1383. const aceEditor = unsafeWindow["ace"].edit("editor");
  1384. editor = {
  1385. get sourceCode() {
  1386. return aceEditor.getValue();
  1387. },
  1388. set sourceCode(sourceCode) {
  1389. aceEditor.setValue(sourceCode);
  1390. },
  1391. setLanguage(lang) { },
  1392. };
  1393. // ボタンを追加する
  1394. const buttonContainer = doc.querySelector(".submit-form .submit").parentElement;
  1395. buttonContainer.appendChild(newElement("button", {
  1396. type: "button",
  1397. className: "btn btn-info",
  1398. textContent: "Test & Submit",
  1399. onclick: () => events.trig("testAndSubmit"),
  1400. }));
  1401. buttonContainer.appendChild(newElement("button", {
  1402. type: "button",
  1403. className: "btn btn-default",
  1404. textContent: "Test All Samples",
  1405. onclick: () => events.trig("testAllSamples"),
  1406. }));
  1407. clearInterval(waitCfFastSubmit);
  1408. }
  1409. else {
  1410. waitCfFastSubmitCount++;
  1411. if (waitCfFastSubmitCount >= 100)
  1412. clearInterval(waitCfFastSubmit);
  1413. }
  1414. }, 100);
  1415. if (config.get("site.codeforces.showEditor", true)) {
  1416. editor = new Editor(langMap[eLang.value].split(" ")[0]);
  1417. doc.getElementById("pageContent").appendChild(editor.element);
  1418. language.addListener(lang => {
  1419. editor.setLanguage(lang);
  1420. });
  1421. }
  1422. return {
  1423. name: "Codeforces",
  1424. language,
  1425. get sourceCode() {
  1426. if (editor)
  1427. return editor.sourceCode;
  1428. return _sourceCode;
  1429. },
  1430. set sourceCode(sourceCode) {
  1431. const container = new DataTransfer();
  1432. container.items.add(new File([sourceCode], "prog.txt", { type: "text/plain" }));
  1433. const eFile = doc.querySelector(".submitForm").elements["sourceFile"];
  1434. eFile.files = container.files;
  1435. _sourceCode = sourceCode;
  1436. if (editor)
  1437. editor.sourceCode = sourceCode;
  1438. },
  1439. submit() {
  1440. if (editor)
  1441. _sourceCode = editor.sourceCode;
  1442. this.sourceCode = _sourceCode;
  1443. doc.querySelector(`.submitForm .submit`).click();
  1444. },
  1445. get testButtonContainer() {
  1446. return eButtons;
  1447. },
  1448. get sideButtonContainer() {
  1449. return eButtons;
  1450. },
  1451. get bottomMenuContainer() {
  1452. return doc.body;
  1453. },
  1454. get resultListContainer() {
  1455. return doc.querySelector("#pageContent");
  1456. },
  1457. get testCases() {
  1458. const testcases = [];
  1459. let num = 1;
  1460. for (const eSampleTest of doc.querySelectorAll(".sample-test")) {
  1461. const inputs = eSampleTest.querySelectorAll(".input pre");
  1462. const outputs = eSampleTest.querySelectorAll(".output pre");
  1463. const anchors = eSampleTest.querySelectorAll(".input .title .input-output-copier");
  1464. const count = Math.min(inputs.length, outputs.length, anchors.length);
  1465. for (let i = 0; i < count; i++) {
  1466. let inputText = "";
  1467. for (const node of inputs[i].childNodes) {
  1468. inputText += node.textContent;
  1469. if (node.nodeType == node.ELEMENT_NODE && node.tagName == "DIV") {
  1470. inputText += "\n";
  1471. }
  1472. }
  1473. testcases.push({
  1474. title: `Sample ${num++}`,
  1475. input: inputText,
  1476. output: outputs[i].textContent,
  1477. anchor: anchors[i],
  1478. });
  1479. }
  1480. }
  1481. return testcases;
  1482. },
  1483. get jQuery() {
  1484. return jQuery;
  1485. },
  1486. get taskURI() {
  1487. return location.href;
  1488. },
  1489. };
  1490. }
  1491.  
  1492. config.registerFlag("site.codeforcesMobile.showEditor", true, "Show Editor in Mobile Codeforces (m[1-3].codeforces.com) Problem Page");
  1493. async function init$2() {
  1494. if (!/^m[1-3]\.codeforces\.com$/.test(location.host))
  1495. throw "not Codeforces Mobile";
  1496. const url = /\/contest\/(\d+)\/problem\/([^/]+)/.exec(location.pathname);
  1497. const contestId = url[1];
  1498. const problemId = url[2];
  1499. const doc = unsafeWindow.document;
  1500. const main = doc.querySelector("main");
  1501. doc.head.appendChild(newElement("link", {
  1502. rel: "stylesheet",
  1503. href: "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css",
  1504. }));
  1505. await loadScript("https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js");
  1506. const language = new ObservableValue("");
  1507. let submit = () => { };
  1508. let getSourceCode = () => "";
  1509. let setSourceCode = (_) => { };
  1510. // make Editor
  1511. if (config.get("site.codeforcesMobile.showEditor", true)) {
  1512. const frame = newElement("iframe", {
  1513. src: `/contest/${contestId}/submit`,
  1514. style: {
  1515. display: "none",
  1516. },
  1517. });
  1518. doc.body.appendChild(frame);
  1519. await new Promise(done => frame.onload = done);
  1520. const fdoc = frame.contentDocument;
  1521. const form = fdoc.querySelector("._SubmitPage_submitForm");
  1522. form.elements["problemIndex"].value = problemId;
  1523. form.elements["problemIndex"].readonly = true;
  1524. form.elements["programTypeId"].addEventListener("change", function () {
  1525. language.value = langMap[this.value];
  1526. });
  1527. for (const row of form.children) {
  1528. if (row.tagName != "DIV")
  1529. continue;
  1530. row.classList.add("form-group");
  1531. const control = row.querySelector("*[name]");
  1532. if (control)
  1533. control.classList.add("form-control");
  1534. }
  1535. form.parentElement.removeChild(form);
  1536. main.appendChild(form);
  1537. submit = () => form.submit();
  1538. getSourceCode = () => form.elements["source"].value;
  1539. setSourceCode = sourceCode => {
  1540. form.elements["source"].value = sourceCode;
  1541. };
  1542. }
  1543. return {
  1544. name: "Codeforces",
  1545. language,
  1546. get sourceCode() {
  1547. return getSourceCode();
  1548. },
  1549. set sourceCode(sourceCode) {
  1550. setSourceCode(sourceCode);
  1551. },
  1552. submit,
  1553. get testButtonContainer() {
  1554. return main;
  1555. },
  1556. get sideButtonContainer() {
  1557. return main;
  1558. },
  1559. get bottomMenuContainer() {
  1560. return doc.body;
  1561. },
  1562. get resultListContainer() {
  1563. return main;
  1564. },
  1565. get testCases() {
  1566. const testcases = [];
  1567. let index = 1;
  1568. for (const container of doc.querySelectorAll(".sample-test")) {
  1569. const input = container.querySelector(".input pre.content").textContent;
  1570. const output = container.querySelector(".output pre.content").textContent;
  1571. const anchor = container.querySelector(".input .title");
  1572. testcases.push({
  1573. input, output, anchor,
  1574. title: `Sample ${index++}`,
  1575. });
  1576. }
  1577. return testcases;
  1578. },
  1579. get jQuery() {
  1580. return unsafeWindow["jQuery"];
  1581. },
  1582. get taskURI() {
  1583. return location.href;
  1584. },
  1585. };
  1586. }
  1587.  
  1588. async function init$1() {
  1589. if (location.host != "greatest.deepsurf.us" && !location.href.match(/433152-atcoder-easy-test-v2/))
  1590. throw "Not about page";
  1591. const doc = unsafeWindow.document;
  1592. await loadScript("https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js");
  1593. const jQuery = unsafeWindow["jQuery"];
  1594. await loadScript("https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js", null, { jQuery, $: jQuery });
  1595. const e = newElement("div");
  1596. doc.getElementById("install-area").appendChild(newElement("button", {
  1597. type: "button",
  1598. textContent: "Open config",
  1599. onclick: () => settings.open(),
  1600. }));
  1601. return {
  1602. name: "About Page",
  1603. language: new ObservableValue(""),
  1604. get sourceCode() { return ""; },
  1605. set sourceCode(sourceCode) { },
  1606. submit() { },
  1607. get testButtonContainer() { return e; },
  1608. get sideButtonContainer() { return e; },
  1609. get bottomMenuContainer() { return e; },
  1610. get resultListContainer() { return e; },
  1611. get testCases() { return []; },
  1612. get jQuery() { return jQuery; },
  1613. get taskURI() { return ""; },
  1614. };
  1615. }
  1616.  
  1617. // 設定ページが開けなくなるのを避ける
  1618. const inits = [init$1()];
  1619. config.registerFlag("site.atcoder", true, "Use AtCoder Easy Test in AtCoder");
  1620. if (config.get("site.atcoder", true))
  1621. inits.push(init$5());
  1622. config.registerFlag("site.yukicoder", true, "Use AtCoder Easy Test in yukicoder");
  1623. if (config.get("site.yukicoder", true))
  1624. inits.push(init$4());
  1625. config.registerFlag("site.codeforces", true, "Use AtCoder Easy Test in Codeforces");
  1626. if (config.get("site.codeforces", true))
  1627. inits.push(init$3());
  1628. config.registerFlag("site.codeforcesMobile", true, "Use AtCoder Easy Test in Codeforces Mobile (m[1-3].codeforces.com)");
  1629. if (config.get("site.codeforcesMobile", true))
  1630. inits.push(init$2());
  1631. const site = Promise.any(inits);
  1632. site.catch(() => {
  1633. for (const promise of inits) {
  1634. promise.catch(console.error);
  1635. }
  1636. });
  1637.  
  1638. const runners = {
  1639. "C GCC 9.3.0 Wandbox": new WandboxRunner("gcc-9.3.0-c", "C (GCC 9.3.0)", { "compiler-option-raw": "-march=native\n-std=gnu11\n-O2\n-DONLINE_JUDGE\n-lm" }),
  1640. "C C17 Clang paiza.io": new PaizaIORunner("c", "C (C17 / Clang)"),
  1641. "C++ GCC 13.2.0 + Boost 1.83.0 + ACL Wandbox": new WandboxCppRunner("gcc-13.2.0", "C++ (GCC 13.2.0) + ACL", { "compiler-option-raw": "-march=native\n-std=gnu++2a\n-Wall\n-Wextra\n-O2\n-DONLINE_JUDGE\n-I/opt/wandbox/boost-1.83.0-gcc-13.2.0/include\n-I." }),
  1642. "C++ GCC 10.2.0 + Boost 1.73.0 + ACL Wandbox": new WandboxCppRunner("gcc-10.2.0", "C++ (GCC 10.2.0) + ACL", { "compiler-option-raw": "-march=native\n-std=gnu++17\n-Wall\n-Wextra\n-O2\n-DONLINE_JUDGE\n-I/opt/wandbox/boost-1.75.0-gcc-10.2.0/include\n-I." }),
  1643. "C++ GCC 9.3.0 + Boost 1.73.0 + ACL Wandbox": new WandboxCppRunner("gcc-9.3.0", "C++ (GCC 9.3.0) + ACL", { "compiler-option-raw": "-march=native\n-std=gnu++17\n-Wall\n-Wextra\n-O2\n-DONLINE_JUDGE\n-I/opt/wandbox/boost-1.75.0-gcc-9.3.0/include\n-I." }),
  1644. "C++ Clang 10.0.1 + ACL Wandbox": new WandboxCppRunner("clang-10.0.1", "C++ (Clang 10.0.1) + ACL", { "compiler-option-raw": "-march=native\n-std=c++17\n-stdlib=libc++\n-Wall\n-O2\n-DNDEBUG\n-DONLINE_JUDGE\n-I/opt/wandbox/boost-1.75.0-clang-10.0.1/include\n-I." }),
  1645. "Python3 CPython paiza.io": new PaizaIORunner("python3", "Python3"),
  1646. "Python3 Brython": brythonRunner,
  1647. "Python3 Pyodide": pyodideRunner,
  1648. "Bash paiza.io": new PaizaIORunner("bash", "Bash"),
  1649. "Bash 5.0.17 Wandbox": new WandboxRunner("bash", "Bash (5.0.17(1)-release)"),
  1650. "C# .NET Core 3.1.407 Wandbox": new WandboxRunner("dotnetcore-3.1.407", "C# (.NET Core 3.1.407)"),
  1651. "C# Mono-mcs 6.12.0.122 Wandbox": new WandboxRunner("mono-6.12.0.122", "C# (Mono-mcs 6.12.0.122)"),
  1652. "Clojure paiza.io": new PaizaIORunner("clojure", "Clojure"),
  1653. "Crystal 0.36.1 Wandbox": new WandboxRunner("crystal-0.36.1", "Crystal (0.36.1)"),
  1654. "D LDC paiza.io": new PaizaIORunner("d", "D (LDC)"),
  1655. "D DMD 2.093.1": new WandboxRunner("dmd-2.093.1", "D (DMD 2.093.1)"),
  1656. "D LDC 1.23.0": new WandboxRunner("ldc-1.23.0", "D (LDC 1.23.0)"),
  1657. "Erlang paiza.io": new PaizaIORunner("erlang", "Erlang"),
  1658. "Erlang 22.3.4.16": new WandboxRunner("erlang-22.3.4.16", "Erlang (22.3.4.16)"),
  1659. "Elixir 1.10.4": new WandboxRunner("elixir-1.10.4", "Elixir (1.10.4)"),
  1660. "Elixir paiza.io": new PaizaIORunner("elixir", "Elixir"),
  1661. "F# Interactive paiza.io": new PaizaIORunner("fsharp", "F# (Interactive)"),
  1662. "Go 1.14.15 Wandbox": new WandboxRunner("go-1.14.15", "Go (1.14.15)"),
  1663. "Haskell GHC 8.8.4 Wandbox": new WandboxRunner("ghc-8.8.4", "Haskell (GHC 8.8.4)"),
  1664. "Haskell paiza.io": new PaizaIORunner("haskell", "Haskell"),
  1665. "Java openjdk-jdk-15.0.3+2 Wandbox": new WandboxRunner("openjdk-jdk-15.0.3+2", "Java (JDK 15.0.3+2)"),
  1666. "Java openjdk-jdk-14.0.2+12 Wandbox": new WandboxRunner("openjdk-jdk-14.0.2+12", "Java (JDK 14.0.2+12)"),
  1667. "JavaScript paiza.io": new PaizaIORunner("javascript", "JavaScript"),
  1668. "JavaScript Node.js 12.22.1 Wandbox": new WandboxRunner("nodejs-12.22.1", "JavaScript (Node.js 12.22.1)"),
  1669. "Julia 1.6.1 Wandbox": new WandboxRunner("julia-1.6.1", "Julia (1.6.1)"),
  1670. "Kotlin paiza.io": new PaizaIORunner("kotlin", "Kotlin"),
  1671. "Lua 5.3.6 Wandbox": new WandboxRunner("lua-5.3.6", "Lua (Lua 5.3.6)"),
  1672. "Lua LuaJIT 2.0.5 Wandbox": new WandboxRunner("luajit-2.0.5", "Lua (LuaJIT 2.0.5)"),
  1673. "Nim 1.0.10 Wandbox": new WandboxRunner("nim-1.0.10", "Nim (1.0.10)"),
  1674. "Objective-C paiza.io": new PaizaIORunner("objective-c", "Objective-C"),
  1675. "OCaml 4.10.2 Wandbox": new WandboxRunner("ocaml-4.10.2", "OCaml (4.10.2)"),
  1676. "Pascal FPC 3.0.4 Wandbox": new WandboxRunner("fpc-3.0.4", "Pascal (FPC 3.0.4)"),
  1677. "Perl paiza.io": new PaizaIORunner("perl", "Perl"),
  1678. "Perl 5.30.3 Wandbox": new WandboxRunner("perl-5.30.3", "Perl (5.30.3)"),
  1679. "PHP paiza.io": new PaizaIORunner("php", "PHP"),
  1680. "PHP 7.4.16 Wandbox": new WandboxRunner("php-7.4.16", "PHP (7.4.16)"),
  1681. "Python PyPy 7.3.4 Wandbox": new WandboxRunner("pypy-2.7-v7.3.4", "PyPy2 (7.3.4)"),
  1682. "Python3 PyPy3 7.3.4 Wandbox": new WandboxRunner("pypy-3.7-v7.3.4", "PyPy3 (7.3.4)"),
  1683. "Ruby paiza.io": new PaizaIORunner("ruby", "Ruby"),
  1684. "Ruby 3.2.2 Wandbox": new WandboxRunner("ruby-3.2.2", "Ruby (3.2.2)"),
  1685. "Ruby 3.1.4 Wandbox": new WandboxRunner("ruby-3.1.4", "Ruby (3.1.4)"),
  1686. "Ruby 3.0.6 Wandbox": new WandboxRunner("ruby-3.0.6", "Ruby (3.0.6)"),
  1687. "Ruby 2.7.8 Wandbox": new WandboxRunner("ruby-2.7.8", "Ruby (2.7.8)"),
  1688. "Rust 1.42.0 AtCoder": new AtCoderRunner("4050", "Rust (1.42.0)"),
  1689. "Rust 1.50.0 Wandbox": new WandboxRunner("rust-1.50.0", "Rust (1.50.0)"),
  1690. "Rust paiza.io": new PaizaIORunner("rust", "Rust"),
  1691. "Scala paiza": new PaizaIORunner("scala", "Scala"),
  1692. "Scala 2.13.5 Wandbox": new WandboxRunner("scala-2.13.5", "Scala (2.13.5)"),
  1693. "Scheme paiza.io": new PaizaIORunner("scheme", "Scheme"),
  1694. "Swift paiza.io": new PaizaIORunner("swift", "Swift"),
  1695. "Swift 5.3.3 Wandbox": new WandboxRunner("swift-5.3.3", "Swift (5.3.3)"),
  1696. "TypeScript typescript-3.9.9 nodejs 14.16.1 Wandbox": new WandboxRunner("typescript-3.9.9 nodejs 14.16.1", "TypeScript (3.9.9)"),
  1697. "Text local": new CustomRunner("Text", async (sourceCode, input) => {
  1698. return {
  1699. status: "OK",
  1700. exitCode: "0",
  1701. input,
  1702. output: sourceCode,
  1703. };
  1704. }),
  1705. "Basic Visual Basic paiza.io": new PaizaIORunner("vb", "Visual Basic"),
  1706. "COBOL Free paiza.io": new PaizaIORunner("cobol", "COBOL - Free"),
  1707. "COBOL Fixed OpenCOBOL 1.1.0 AtCoder": new AtCoderRunner("4060", "COBOL - Fixed (OpenCOBOL 1.1.0)"),
  1708. "COBOL Free OpenCOBOL 1.1.0 AtCoder": new AtCoderRunner("4061", "COBOL - Free (OpenCOBOL 1.1.0)"),
  1709. };
  1710. site.then(site => {
  1711. if (site.name == "AtCoder") {
  1712. // AtCoderRunner がない場合は、追加する
  1713. for (const [languageId, descriptor] of Object.entries(site.langMap)) {
  1714. const m = descriptor.match(/([^ ]+)(.*)/);
  1715. if (m) {
  1716. const name = `${m[1]} ${m[2].slice(1)} AtCoder`;
  1717. runners[name] = new AtCoderRunner(languageId, descriptor);
  1718. }
  1719. }
  1720. }
  1721. });
  1722. console.info("AtCoder Easy Test: codeRunner OK");
  1723. config.registerCount("codeRunner.maxRetry", 3, "Max count of retry when IE (Internal Error)");
  1724. var codeRunner = {
  1725. // 指定した環境でコードを実行する
  1726. async run(runnerId, sourceCode, input, expectedOutput, options = { trim: true, split: true }) {
  1727. // CodeRunner が存在しない言語ID
  1728. if (!(runnerId in runners))
  1729. return Promise.reject("Language not supported");
  1730. // 最後に実行したコードを保存
  1731. if (sourceCode.length > 0)
  1732. site.then(site => codeSaver.save(site.taskURI, sourceCode));
  1733. // 実行
  1734. const maxRetry = config.get("codeRunner.maxRetry", 3);
  1735. for (let retry = 0; retry < maxRetry; retry++) {
  1736. try {
  1737. const result = await runners[runnerId].test(sourceCode, input, expectedOutput, options);
  1738. const lang = runnerId.split(" ")[0];
  1739. if (result.status == "IE") {
  1740. console.error(result);
  1741. const runnerIds = Object.keys(runners).filter(runnerId => runnerId.split(" ")[0] == lang);
  1742. const index = runnerIds.indexOf(runnerId);
  1743. runnerId = runnerIds[(index + 1) % runnerIds.length];
  1744. continue;
  1745. }
  1746. return result;
  1747. }
  1748. catch (e) {
  1749. console.error(e);
  1750. }
  1751. }
  1752. },
  1753. // 環境の名前の一覧を取得する
  1754. // @return runnerIdとラベルのペアの配列
  1755. async getEnvironment(languageId) {
  1756. const langs = similarLangs(languageId, Object.keys(runners));
  1757. if (langs.length == 0)
  1758. throw `Undefined language: ${languageId}`;
  1759. return langs.map(runnerId => [runnerId, runners[runnerId].label]);
  1760. },
  1761. };
  1762.  
  1763. var hBottomMenu = "<div id=\"bottom-menu-wrapper\" class=\"navbar navbar-default navbar-fixed-bottom\">\n <div class=\"container\">\n <div class=\"navbar-header\">\n <button id=\"bottom-menu-key\" type=\"button\" class=\"navbar-toggle collapsed glyphicon glyphicon-menu-down\" data-toggle=\"collapse\" data-target=\"#bottom-menu\"></button>\n </div>\n <div id=\"bottom-menu\" class=\"collapse navbar-collapse\">\n <ul id=\"bottom-menu-tabs\" class=\"nav nav-tabs\"></ul>\n <div id=\"bottom-menu-contents\" class=\"tab-content\"></div>\n </div>\n </div>\n</div>";
  1764.  
  1765. var hStyle$1 = "<style>\n#bottom-menu-wrapper {\n background: transparent !important;\n border: none !important;\n pointer-events: none;\n padding: 0;\n}\n\n#bottom-menu-wrapper>.container {\n position: absolute;\n bottom: 0;\n width: 100%;\n padding: 0;\n}\n\n#bottom-menu-wrapper>.container>.navbar-header {\n float: none;\n}\n\n#bottom-menu-key {\n display: block;\n float: none;\n margin: 0 auto;\n padding: 10px 3em;\n border-radius: 5px 5px 0 0;\n background: #000;\n opacity: 0.5;\n color: #FFF;\n cursor: pointer;\n pointer-events: auto;\n text-align: center;\n}\n\n@media screen and (max-width: 767px) {\n #bottom-menu-key {\n opacity: 0.25;\n }\n}\n\n#bottom-menu-key.collapsed:before {\n content: \"\\e260\";\n}\n\n#bottom-menu-tabs {\n padding: 3px 0 0 10px;\n cursor: n-resize;\n}\n\n#bottom-menu-tabs a {\n pointer-events: auto;\n}\n\n#bottom-menu {\n pointer-events: auto;\n background: rgba(0, 0, 0, 0.8);\n color: #fff;\n max-height: unset;\n}\n\n#bottom-menu.collapse:not(.in) {\n display: none !important;\n}\n\n#bottom-menu-tabs>li>a {\n background: rgba(150, 150, 150, 0.5);\n color: #000;\n border: solid 1px #ccc;\n filter: brightness(0.75);\n}\n\n#bottom-menu-tabs>li>a:hover {\n background: rgba(150, 150, 150, 0.5);\n border: solid 1px #ccc;\n color: #111;\n filter: brightness(0.9);\n}\n\n#bottom-menu-tabs>li.active>a {\n background: #eee;\n border: solid 1px #ccc;\n color: #333;\n filter: none;\n}\n\n.bottom-menu-btn-close {\n font-size: 8pt;\n vertical-align: baseline;\n padding: 0 0 0 6px;\n margin-right: -6px;\n}\n\n#bottom-menu-contents {\n padding: 5px 15px;\n max-height: 50vh;\n overflow-y: auto;\n}\n\n#bottom-menu-contents .panel {\n color: #333;\n}\n</style>";
  1766.  
  1767. async function init() {
  1768. const site$1 = await site;
  1769. const style = html2element(hStyle$1);
  1770. const bottomMenu = html2element(hBottomMenu);
  1771. unsafeWindow.document.head.appendChild(style);
  1772. site$1.bottomMenuContainer.appendChild(bottomMenu);
  1773. const bottomMenuKey = bottomMenu.querySelector("#bottom-menu-key");
  1774. const bottomMenuTabs = bottomMenu.querySelector("#bottom-menu-tabs");
  1775. const bottomMenuContents = bottomMenu.querySelector("#bottom-menu-contents");
  1776. // メニューのリサイズ
  1777. {
  1778. let resizeStart = null;
  1779. const onStart = (event) => {
  1780. const target = event.target;
  1781. const pageY = event.pageY;
  1782. if (target.id != "bottom-menu-tabs")
  1783. return;
  1784. resizeStart = { y: pageY, height: bottomMenuContents.getBoundingClientRect().height };
  1785. };
  1786. const onMove = (event) => {
  1787. if (!resizeStart)
  1788. return;
  1789. event.preventDefault();
  1790. bottomMenuContents.style.height = `${resizeStart.height - (event.pageY - resizeStart.y)}px`;
  1791. };
  1792. const onEnd = () => {
  1793. resizeStart = null;
  1794. };
  1795. bottomMenuTabs.addEventListener("mousedown", onStart);
  1796. bottomMenuTabs.addEventListener("mousemove", onMove);
  1797. bottomMenuTabs.addEventListener("mouseup", onEnd);
  1798. bottomMenuTabs.addEventListener("mouseleave", onEnd);
  1799. }
  1800. let tabs = new Set();
  1801. let selectedTab = null;
  1802. /** 下メニューの操作
  1803. * 下メニューはいくつかのタブからなる。タブはそれぞれ tabId, ラベル, 中身を持っている。
  1804. */
  1805. const menuController = {
  1806. /** タブを選択 */
  1807. selectTab(tabId) {
  1808. const tab = site$1.jQuery(`#bottom-menu-tab-${tabId}`);
  1809. if (tab && tab[0]) {
  1810. tab.tab("show"); // Bootstrap 3
  1811. selectedTab = tabId;
  1812. }
  1813. },
  1814. /** 下メニューにタブを追加する */
  1815. addTab(tabId, tabLabel, paneContent, options = {}) {
  1816. console.log(`AtCoder Easy Test: addTab: ${tabLabel} (${tabId})`, paneContent);
  1817. // タブを追加
  1818. const tab = document.createElement("a");
  1819. tab.textContent = tabLabel;
  1820. tab.id = `bottom-menu-tab-${tabId}`;
  1821. tab.href = "#";
  1822. tab.dataset.id = tabId;
  1823. tab.dataset.target = `#bottom-menu-pane-${tabId}`;
  1824. tab.dataset.toggle = "tab";
  1825. tab.addEventListener("click", event => {
  1826. event.preventDefault();
  1827. menuController.selectTab(tabId);
  1828. });
  1829. tabs.add(tab);
  1830. const tabLi = document.createElement("li");
  1831. tabLi.appendChild(tab);
  1832. bottomMenuTabs.appendChild(tabLi);
  1833. // 内容を追加
  1834. const pane = document.createElement("div");
  1835. pane.className = "tab-pane";
  1836. pane.id = `bottom-menu-pane-${tabId}`;
  1837. pane.appendChild(paneContent);
  1838. bottomMenuContents.appendChild(pane);
  1839. const controller = {
  1840. get id() {
  1841. return tabId;
  1842. },
  1843. close() {
  1844. bottomMenuTabs.removeChild(tabLi);
  1845. bottomMenuContents.removeChild(pane);
  1846. tabs.delete(tab);
  1847. if (selectedTab == tabId) {
  1848. selectedTab = null;
  1849. if (tabs.size > 0) {
  1850. menuController.selectTab(tabs.values().next().value.dataset.id);
  1851. }
  1852. }
  1853. },
  1854. show() {
  1855. menuController.show();
  1856. menuController.selectTab(tabId);
  1857. },
  1858. set color(color) {
  1859. tab.style.backgroundColor = color;
  1860. },
  1861. };
  1862. // 閉じるボタン
  1863. if (options.closeButton) {
  1864. const btn = document.createElement("a");
  1865. btn.className = "bottom-menu-btn-close btn btn-link glyphicon glyphicon-remove";
  1866. btn.addEventListener("click", () => {
  1867. controller.close();
  1868. });
  1869. tab.appendChild(btn);
  1870. }
  1871. // 選択されているタブがなければ選択
  1872. if (!selectedTab)
  1873. menuController.selectTab(tabId);
  1874. return controller;
  1875. },
  1876. /** 下メニューを表示する */
  1877. show() {
  1878. if (bottomMenuKey.classList.contains("collapsed"))
  1879. bottomMenuKey.click();
  1880. },
  1881. /** 下メニューの表示/非表示を切り替える */
  1882. toggle() {
  1883. bottomMenuKey.click();
  1884. },
  1885. };
  1886. console.info("AtCoder Easy Test: bottomMenu OK");
  1887. return menuController;
  1888. }
  1889.  
  1890. var hRowTemplate = "<div class=\"atcoder-easy-test-cases-row alert alert-dismissible\">\n <button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-label=\"close\">\n <span aria-hidden=\"true\">×</span>\n </button>\n <div class=\"progress\">\n <div class=\"progress-bar\" style=\"width: 0%;\">0 / 0</div>\n </div>\n <div class=\"atcoder-easy-test-cases-row-date\" style=\"font-family: monospace; text-align: right; position: absolute; right: 1em;\"></div>\n</div>";
  1891.  
  1892. class ResultRow {
  1893. _tabs;
  1894. _element;
  1895. _promise;
  1896. constructor(pairs) {
  1897. this._tabs = pairs.map(([_, tab]) => tab);
  1898. this._element = html2element(hRowTemplate);
  1899. this._element.querySelector(".close").addEventListener("click", () => this.remove());
  1900. {
  1901. const date = new Date();
  1902. const h = date.getHours().toString().padStart(2, "0");
  1903. const m = date.getMinutes().toString().padStart(2, "0");
  1904. const s = date.getSeconds().toString().padStart(2, "0");
  1905. this._element.querySelector(".atcoder-easy-test-cases-row-date").textContent = `${h}:${m}:${s}`;
  1906. }
  1907. const numCases = pairs.length;
  1908. let numFinished = 0;
  1909. let numAccepted = 0;
  1910. const progressBar = this._element.querySelector(".progress-bar");
  1911. progressBar.textContent = `${numFinished} / ${numCases}`;
  1912. this._promise = Promise.all(pairs.map(([pResult, tab]) => {
  1913. const button = html2element(`<div class="label label-default" style="margin: 3px; cursor: pointer;">WJ</div>`);
  1914. button.addEventListener("click", async () => {
  1915. (await tab).show();
  1916. });
  1917. this._element.appendChild(button);
  1918. return pResult.then(result => {
  1919. button.textContent = result.status;
  1920. if (result.status == "AC") {
  1921. button.classList.add("label-success");
  1922. }
  1923. else if (result.status != "OK") {
  1924. button.classList.add("label-warning");
  1925. }
  1926. numFinished++;
  1927. if (result.status == "AC")
  1928. numAccepted++;
  1929. progressBar.textContent = `${numFinished} / ${numCases}`;
  1930. progressBar.style.width = `${100 * numFinished / numCases}%`;
  1931. if (numFinished == numCases) {
  1932. if (numAccepted == numCases)
  1933. this._element.classList.add("alert-success");
  1934. else
  1935. this._element.classList.add("alert-warning");
  1936. }
  1937. }).catch(reason => {
  1938. button.textContent = "IE";
  1939. button.classList.add("label-danger");
  1940. console.error(reason);
  1941. });
  1942. }));
  1943. }
  1944. get element() {
  1945. return this._element;
  1946. }
  1947. onFinish(listener) {
  1948. this._promise.then(listener);
  1949. }
  1950. remove() {
  1951. for (const pTab of this._tabs)
  1952. pTab.then(tab => tab.close());
  1953. const parent = this._element.parentElement;
  1954. if (parent)
  1955. parent.removeChild(this._element);
  1956. }
  1957. }
  1958.  
  1959. var hResultList = "<div class=\"row\"></div>";
  1960.  
  1961. const eResultList = html2element(hResultList);
  1962. site.then(site => site.resultListContainer.appendChild(eResultList));
  1963. const resultList = {
  1964. addResult(pairs) {
  1965. const result = new ResultRow(pairs);
  1966. eResultList.insertBefore(result.element, eResultList.firstChild);
  1967. return result;
  1968. },
  1969. };
  1970.  
  1971. const version = {
  1972. currentProperty: new ObservableValue("2.11.14"),
  1973. get current() {
  1974. return this.currentProperty.value;
  1975. },
  1976. latestProperty: new ObservableValue(config.get("version.latest", "2.11.14")),
  1977. get latest() {
  1978. return this.latestProperty.value;
  1979. },
  1980. lastCheckProperty: new ObservableValue(config.get("version.lastCheck", 0)),
  1981. get lastCheck() {
  1982. return this.lastCheckProperty.value;
  1983. },
  1984. get hasUpdate() {
  1985. return this.compare(this.current, this.latest) < 0;
  1986. },
  1987. compare(a, b) {
  1988. const x = a.split(".").map((s) => parseInt(s, 10));
  1989. const y = b.split(".").map((s) => parseInt(s, 10));
  1990. for (let i = 0; i < 3; i++) {
  1991. if (x[i] < y[i]) {
  1992. return -1;
  1993. }
  1994. else if (x[i] > y[i]) {
  1995. return 1;
  1996. }
  1997. }
  1998. return 0;
  1999. },
  2000. async checkUpdate(force = false) {
  2001. const now = Date.now();
  2002. if (!force && now - version.lastCheck < config.get("version.checkInterval", aDay)) {
  2003. return this.current;
  2004. }
  2005. const packageJson = await fetch("https://raw.githubusercontent.com/magurofly/atcoder-easy-test/main/v2/package.json").then(r => r.json());
  2006. console.log(packageJson);
  2007. const latest = packageJson["version"];
  2008. this.latestProperty.value = latest;
  2009. config.set("version.latest", latest);
  2010. this.lastCheckProperty.value = now;
  2011. config.set("version.lastCheck", now);
  2012. return latest;
  2013. },
  2014. };
  2015. // 更新チェック
  2016. const aDay = 24 * 60 * 60 * 1e3;
  2017. config.registerCount("version.checkInterval", aDay, "Interval [ms] of checking for new version");
  2018. config.get("version.checkInterval", aDay);
  2019. setInterval(() => {
  2020. version.checkUpdate(false);
  2021. }, 60e3);
  2022. settings.add("version", (win) => {
  2023. const root = newElement("div");
  2024. const text = win.document.createTextNode.bind(win.document);
  2025. const textAuto = (property) => {
  2026. const t = text(property.value);
  2027. property.addListener(value => {
  2028. t.textContent = value;
  2029. });
  2030. return t;
  2031. };
  2032. const tCurrent = textAuto(version.currentProperty);
  2033. const tLatest = textAuto(version.latestProperty);
  2034. const tLastCheck = textAuto(version.lastCheckProperty.map(time => new Date(time).toLocaleString()));
  2035. root.appendChild(newElement("p", {}, [
  2036. text("AtCoder Easy Test v"),
  2037. tCurrent,
  2038. ]));
  2039. const updateButton = newElement("a", {
  2040. className: "btn btn-info",
  2041. textContent: "Install",
  2042. href: "https://github.com/magurofly/atcoder-easy-test/raw/main/v2/atcoder-easy-test.user.js",
  2043. target: "_blank",
  2044. });
  2045. const showButton = () => {
  2046. if (version.hasUpdate)
  2047. updateButton.style.display = "inline";
  2048. else
  2049. updateButton.style.display = "none";
  2050. };
  2051. showButton();
  2052. version.lastCheckProperty.addListener(showButton);
  2053. root.appendChild(newElement("p", {}, [
  2054. text("Latest: v"),
  2055. tLatest,
  2056. text(" (Last Check: "),
  2057. tLastCheck,
  2058. text(") "),
  2059. updateButton,
  2060. ]));
  2061. root.appendChild(newElement("p", {}, [
  2062. newElement("a", {
  2063. className: "btn btn-primary",
  2064. textContent: "Check Update",
  2065. onclick() {
  2066. version.checkUpdate(true);
  2067. },
  2068. }),
  2069. ]));
  2070. return root;
  2071. });
  2072.  
  2073. var hTabTemplate = "<div class=\"atcoder-easy-test-result container\">\n <div class=\"row\">\n <div class=\"atcoder-easy-test-result-col-input col-xs-12\" data-if-expected-output=\"col-sm-6 col-sm-push-6\">\n <div class=\"form-group\">\n <label class=\"control-label col-xs-12\">\n Standard Input\n <div class=\"col-xs-12\">\n <textarea class=\"atcoder-easy-test-result-input form-control\" rows=\"3\" readonly=\"readonly\"></textarea>\n </div>\n </label>\n </div>\n </div>\n <div class=\"atcoder-easy-test-result-col-expected-output col-xs-12 col-sm-6 hidden\" data-if-expected-output=\"!hidden col-sm-pull-6\">\n <div class=\"form-group\">\n <label class=\"control-label col-xs-12\">\n Expected Output\n <div class=\"col-xs-12\">\n <textarea class=\"atcoder-easy-test-result-expected-output form-control\" rows=\"3\" readonly=\"readonly\"></textarea>\n </div>\n </label>\n </div>\n </div>\n </div>\n <div class=\"row\"><div class=\"col-sm-6 col-sm-offset-3\">\n <div class=\"panel panel-default\">\n <table class=\"table table-condensed\">\n <tbody>\n <tr>\n <th class=\"text-center\">Exit Code</th>\n <th class=\"text-center\">Exec Time</th>\n <th class=\"text-center\">Memory</th>\n </tr>\n <tr>\n <td class=\"atcoder-easy-test-result-exit-code text-center\"></td>\n <td class=\"atcoder-easy-test-result-exec-time text-center\"></td>\n <td class=\"atcoder-easy-test-result-memory text-center\"></td>\n </tr>\n </tbody>\n </table>\n </div>\n </div></div>\n <div class=\"row\">\n <div class=\"atcoder-easy-test-result-col-output col-xs-12\" data-if-error=\"col-md-6\">\n <div class=\"form-group\">\n <label class=\"control-label col-xs-12\">\n Standard Output\n <div class=\"col-xs-12\">\n <textarea class=\"atcoder-easy-test-result-output form-control\" rows=\"5\" readonly=\"readonly\"></textarea>\n </div>\n </label>\n </div>\n </div>\n <div class=\"atcoder-easy-test-result-col-error col-xs-12 col-md-6 hidden\" data-if-error=\"!hidden\">\n <div class=\"form-group\">\n <label class=\"control-label col-xs-12\">\n Standard Error\n <div class=\"col-xs-12\">\n <textarea class=\"atcoder-easy-test-result-error form-control\" rows=\"5\" readonly=\"readonly\"></textarea>\n </div>\n </label>\n </div>\n </div>\n </div>\n</div>";
  2074.  
  2075. function setClassFromData(element, name) {
  2076. const classes = element.dataset[name].split(/\s+/);
  2077. for (let className of classes) {
  2078. let flag = true;
  2079. if (className[0] == "!") {
  2080. className = className.slice(1);
  2081. flag = false;
  2082. }
  2083. element.classList.toggle(className, flag);
  2084. }
  2085. }
  2086. class ResultTabContent {
  2087. _title;
  2088. _uid;
  2089. _element;
  2090. _result;
  2091. constructor() {
  2092. this._uid = Date.now().toString(16) + Math.floor(Math.random() * 256).toString(16);
  2093. this._result = null;
  2094. this._element = html2element(hTabTemplate);
  2095. this._element.id = `atcoder-easy-test-result-${this._uid}`;
  2096. }
  2097. set result(result) {
  2098. this._result = result;
  2099. if (result.status == "AC") {
  2100. this.outputStyle.backgroundColor = "#dff0d8";
  2101. }
  2102. else if (result.status != "OK") {
  2103. this.outputStyle.backgroundColor = "#fcf8e3";
  2104. }
  2105. this.input = result.input;
  2106. if ("expectedOutput" in result)
  2107. this.expectedOutput = result.expectedOutput;
  2108. this.exitCode = result.exitCode;
  2109. if ("execTime" in result)
  2110. this.execTime = `${result.execTime} ms`;
  2111. if ("memory" in result)
  2112. this.memory = `${result.memory} KB`;
  2113. if ("output" in result)
  2114. this.output = result.output;
  2115. if (result.error)
  2116. this.error = result.error;
  2117. }
  2118. get result() {
  2119. return this._result;
  2120. }
  2121. get uid() {
  2122. return this._uid;
  2123. }
  2124. get element() {
  2125. return this._element;
  2126. }
  2127. set title(title) {
  2128. this._title = title;
  2129. }
  2130. get title() {
  2131. return this._title;
  2132. }
  2133. set input(input) {
  2134. this._get("input").value = input;
  2135. }
  2136. get inputStyle() {
  2137. return this._get("input").style;
  2138. }
  2139. set expectedOutput(output) {
  2140. this._get("expected-output").value = output;
  2141. setClassFromData(this._get("col-input"), "ifExpectedOutput");
  2142. setClassFromData(this._get("col-expected-output"), "ifExpectedOutput");
  2143. }
  2144. get expectedOutputStyle() {
  2145. return this._get("expected-output").style;
  2146. }
  2147. set output(output) {
  2148. this._get("output").value = output;
  2149. }
  2150. get outputStyle() {
  2151. return this._get("output").style;
  2152. }
  2153. set error(error) {
  2154. this._get("error").value = error;
  2155. setClassFromData(this._get("col-output"), "ifError");
  2156. setClassFromData(this._get("col-error"), "ifError");
  2157. }
  2158. set exitCode(code) {
  2159. const element = this._get("exit-code");
  2160. element.textContent = code;
  2161. const isSuccess = code == "0";
  2162. element.classList.toggle("bg-success", isSuccess);
  2163. element.classList.toggle("bg-danger", !isSuccess);
  2164. }
  2165. set execTime(time) {
  2166. this._get("exec-time").textContent = time;
  2167. }
  2168. set memory(memory) {
  2169. this._get("memory").textContent = memory;
  2170. }
  2171. _get(name) {
  2172. return this._element.querySelector(`.atcoder-easy-test-result-${name}`);
  2173. }
  2174. }
  2175.  
  2176. var hRoot = "<form id=\"atcoder-easy-test-container\" class=\"form-horizontal\">\n <div class=\"row\">\n <div class=\"col-xs-12 col-lg-8\">\n <div class=\"form-group\">\n <label class=\"control-label col-sm-2\">Test Environment</label>\n <div class=\"col-sm-10\">\n <select class=\"form-control\" id=\"atcoder-easy-test-language\" style=\"width: 100% !important\"></select>\n </div>\n </div>\n <div class=\"form-group\">\n <label class=\"control-label col-sm-2\" for=\"atcoder-easy-test-input\">Standard Input</label>\n <div class=\"col-sm-10\">\n <textarea id=\"atcoder-easy-test-input\" name=\"input\" class=\"form-control\" rows=\"3\"></textarea>\n </div>\n </div>\n </div>\n <div class=\"col-xs-12 col-lg-4\">\n <details close>\n <summary>Expected Output</summary>\n <div class=\"form-group\">\n <label class=\"control-label col-sm-2\" for=\"atcoder-easy-test-allowable-error-check\">Allowable Error</label>\n <div class=\"col-sm-10\">\n <div class=\"input-group\">\n <span class=\"input-group-addon\">\n <input id=\"atcoder-easy-test-allowable-error-check\" type=\"checkbox\" checked=\"checked\">\n </span>\n <input id=\"atcoder-easy-test-allowable-error\" type=\"text\" class=\"form-control\" value=\"1e-6\">\n </div>\n </div>\n </div>\n <div class=\"form-group\">\n <label class=\"control-label col-sm-2\" for=\"atcoder-easy-test-output\">Expected Output</label>\n <div class=\"col-sm-10\">\n <textarea id=\"atcoder-easy-test-output\" name=\"output\" class=\"form-control\" rows=\"3\"></textarea>\n </div>\n </div>\n </details>\n </div>\n <div class=\"col-xs-12 col-md-6\">\n <div class=\"col-xs-11 col-xs-offset=1\">\n <div class=\"form-group\">\n <a id=\"atcoder-easy-test-run\" class=\"btn btn-primary\">Run</a>\n </div>\n </div>\n </div>\n <div class=\"col-xs-12 col-md-6\">\n <div class=\"col-xs-11 col-xs-offset=1\">\n <div class=\"form-group text-right\">\n <small>AtCoder Easy Test v<span id=\"atcoder-easy-test-version\"></span></small>\n <a id=\"atcoder-easy-test-setting\" class=\"btn btn-xs btn-default\">Setting</a>\n </div>\n </div>\n </div>\n </div>\n <style>\n #atcoder-easy-test-language {\n border: none;\n background: transparent;\n font: inherit;\n color: #fff;\n }\n #atcoder-easy-test-language option {\n border: none;\n color: #333;\n font: inherit;\n }\n </style>\n</form>";
  2177.  
  2178. var hStyle = "<style>\n.atcoder-easy-test-result textarea {\n font-family: monospace;\n font-weight: normal;\n}\n</style>";
  2179.  
  2180. var hRunButton = "<button type=\"button\" class=\"btn btn-primary btn-sm atcoder-easy-test-btn-run-case\" style=\"vertical-align: top; margin-left: 0.5em\">Run</button>";
  2181.  
  2182. var hTestAndSubmit = "<button type=\"button\" id=\"atcoder-easy-test-btn-test-and-submit\" class=\"btn btn-info btn\" style=\"margin-left: 1rem\" title=\"Ctrl+Enter\" data-toggle=\"tooltip\">Test &amp; Submit</button>";
  2183.  
  2184. var hTestAllSamples = "<button type=\"button\" id=\"atcoder-easy-test-btn-test-all\" class=\"btn btn-default btn-sm\" style=\"margin-left: 1rem\" title=\"Alt+Enter\" data-toggle=\"tooltip\">Test All Samples</button>";
  2185.  
  2186. (async () => {
  2187. const site$1 = await site;
  2188. const doc = unsafeWindow.document;
  2189. // init bottomMenu
  2190. const pBottomMenu = init();
  2191. pBottomMenu.then(bottomMenu => {
  2192. unsafeWindow.bottomMenu = bottomMenu;
  2193. });
  2194. await doneOrFail(pBottomMenu);
  2195. // external interfaces
  2196. unsafeWindow.codeRunner = codeRunner;
  2197. doc.head.appendChild(html2element(hStyle));
  2198. // interface
  2199. const atCoderEasyTest = {
  2200. version,
  2201. site: site$1,
  2202. config,
  2203. codeSaver,
  2204. enableButtons() {
  2205. events.trig("enable");
  2206. },
  2207. disableButtons() {
  2208. events.trig("disable");
  2209. },
  2210. runCount: 0,
  2211. runTest(title, language, sourceCode, input, output = null, options = { trim: true, split: true, }) {
  2212. this.disableButtons();
  2213. const content = new ResultTabContent();
  2214. const pTab = pBottomMenu.then(bottomMenu => bottomMenu.addTab("easy-test-result-" + content.uid, `#${++this.runCount} ${title}`, content.element, { active: true, closeButton: true }));
  2215. const pResult = codeRunner.run(language, sourceCode, input, output, options);
  2216. pResult.then(result => {
  2217. content.result = result;
  2218. if (result.status == "AC") {
  2219. pTab.then(tab => tab.color = "#dff0d8");
  2220. }
  2221. else if (result.status != "OK") {
  2222. pTab.then(tab => tab.color = "#fcf8e3");
  2223. }
  2224. }).finally(() => {
  2225. this.enableButtons();
  2226. });
  2227. return [pResult, pTab];
  2228. }
  2229. };
  2230. unsafeWindow.atCoderEasyTest = atCoderEasyTest;
  2231. // place "Easy Test" tab
  2232. {
  2233. // declare const hRoot: string;
  2234. const root = html2element(hRoot);
  2235. const E = (id) => root.querySelector(`#atcoder-easy-test-${id}`);
  2236. const eLanguage = E("language");
  2237. const eInput = E("input");
  2238. const eAllowableErrorCheck = E("allowable-error-check");
  2239. const eAllowableError = E("allowable-error");
  2240. const eOutput = E("output");
  2241. const eRun = E("run");
  2242. const eSetting = E("setting");
  2243. const eVersion = E("version");
  2244. eVersion.textContent = atCoderEasyTest.version.current;
  2245. events.on("enable", () => {
  2246. eRun.classList.remove("disabled");
  2247. });
  2248. events.on("disable", () => {
  2249. eRun.classList.add("disabled");
  2250. });
  2251. eSetting.addEventListener("click", () => {
  2252. settings.open();
  2253. });
  2254. // バージョン確認
  2255. {
  2256. let button = null;
  2257. const showButton = () => {
  2258. if (!version.hasUpdate)
  2259. return;
  2260. if (button) {
  2261. button.textContent = `Update to v${version.latest}`;
  2262. return;
  2263. }
  2264. console.info(`AtCoder Easy Test: New version available: v${version}`);
  2265. button = newElement("a", {
  2266. href: "https://github.com/magurofly/atcoder-easy-test/raw/main/v2/atcoder-easy-test.user.js",
  2267. target: "_blank",
  2268. className: "btn btn-xs btn-info",
  2269. textContent: `Update to v${version.latest}`,
  2270. });
  2271. eVersion.insertAdjacentElement("afterend", button);
  2272. };
  2273. version.latestProperty.addListener(showButton);
  2274. showButton();
  2275. }
  2276. // 言語選択関係
  2277. {
  2278. async function onEnvChange() {
  2279. const langSelection = config.get("langSelection", {});
  2280. langSelection[site$1.language.value] = eLanguage.value;
  2281. config.set("langSelection", langSelection);
  2282. config.save();
  2283. }
  2284. if (unsafeWindow["jQuery"] && unsafeWindow["jQuery"].fn.select2) {
  2285. unsafeWindow["jQuery"](eLanguage).on("change", onEnvChange);
  2286. }
  2287. else {
  2288. eLanguage.addEventListener("change", onEnvChange);
  2289. }
  2290. async function setLanguage() {
  2291. const languageId = site$1.language.value;
  2292. while (eLanguage.firstChild)
  2293. eLanguage.removeChild(eLanguage.firstChild);
  2294. try {
  2295. if (!languageId)
  2296. throw new Error("AtCoder Easy Test: language not set");
  2297. const langs = await codeRunner.getEnvironment(languageId);
  2298. console.log(`AtCoder Easy Test: language = ${langs[1]} (${langs[0]})`);
  2299. // add <option>
  2300. for (const [languageId, label] of langs) {
  2301. const option = document.createElement("option");
  2302. option.value = languageId;
  2303. option.textContent = label;
  2304. eLanguage.appendChild(option);
  2305. }
  2306. // load
  2307. const langSelection = config.get("langSelection", {});
  2308. if (languageId in langSelection) {
  2309. const prev = langSelection[languageId];
  2310. if (langs.some(([lang, _]) => lang == prev)) {
  2311. eLanguage.value = prev;
  2312. }
  2313. }
  2314. events.trig("enable");
  2315. }
  2316. catch (error) {
  2317. console.log(`AtCoder Easy Test: language = ? (${languageId})`);
  2318. console.error(error);
  2319. const option = document.createElement("option");
  2320. option.className = "fg-danger";
  2321. option.textContent = error;
  2322. eLanguage.appendChild(option);
  2323. events.trig("disable");
  2324. }
  2325. }
  2326. site$1.language.addListener(() => setLanguage());
  2327. eAllowableError.disabled = !eAllowableErrorCheck.checked;
  2328. eAllowableErrorCheck.addEventListener("change", event => {
  2329. eAllowableError.disabled = !eAllowableErrorCheck.checked;
  2330. });
  2331. }
  2332. // テスト実行
  2333. function runTest(title, input, output = null, options = {}) {
  2334. const opts = Object.assign({ trim: true, split: true, }, options);
  2335. if (eAllowableErrorCheck.checked) {
  2336. opts.allowableError = parseFloat(eAllowableError.value);
  2337. }
  2338. return atCoderEasyTest.runTest(title, eLanguage.value, site$1.sourceCode, input, output, opts);
  2339. }
  2340. function runAllCases(testcases) {
  2341. const runGroupId = uuid();
  2342. const pairs = testcases.map(testcase => runTest(testcase.title, testcase.input, testcase.output, { runGroupId }));
  2343. resultList.addResult(pairs);
  2344. return Promise.all(pairs.map(([pResult, _]) => pResult.then(result => {
  2345. if (result.status == "AC")
  2346. return Promise.resolve(result);
  2347. else
  2348. return Promise.reject(result);
  2349. })));
  2350. }
  2351. eRun.addEventListener("click", _ => {
  2352. const title = "Run";
  2353. const input = eInput.value;
  2354. const output = eOutput.value;
  2355. runTest(title, input, output || null);
  2356. });
  2357. await doneOrFail(pBottomMenu.then(bottomMenu => bottomMenu.addTab("easy-test", "Easy Test", root)));
  2358. // place "Run" button on each sample
  2359. for (const testCase of site$1.testCases) {
  2360. const eRunButton = html2element(hRunButton);
  2361. eRunButton.addEventListener("click", async () => {
  2362. const [pResult, pTab] = runTest(testCase.title, testCase.input, testCase.output);
  2363. await pResult;
  2364. (await pTab).show();
  2365. });
  2366. testCase.anchor.insertAdjacentElement("afterend", eRunButton);
  2367. events.on("disable", () => {
  2368. eRunButton.classList.add("disabled");
  2369. });
  2370. events.on("enable", () => {
  2371. eRunButton.classList.remove("disabled");
  2372. });
  2373. }
  2374. // place "Test & Submit" button
  2375. {
  2376. const button = html2element(hTestAndSubmit);
  2377. site$1.testButtonContainer.appendChild(button);
  2378. const testAndSubmit = async () => {
  2379. await runAllCases(site$1.testCases);
  2380. site$1.submit();
  2381. };
  2382. button.addEventListener("click", testAndSubmit);
  2383. events.on("testAndSubmit", testAndSubmit);
  2384. events.on("disable", () => button.classList.add("disabled"));
  2385. events.on("enable", () => button.classList.remove("disabled"));
  2386. }
  2387. // place "Test All Samples" button
  2388. {
  2389. const button = html2element(hTestAllSamples);
  2390. site$1.testButtonContainer.appendChild(button);
  2391. const testAllSamples = () => runAllCases(site$1.testCases);
  2392. button.addEventListener("click", testAllSamples);
  2393. events.on("testAllSamples", testAllSamples);
  2394. events.on("disable", () => button.classList.add("disabled"));
  2395. events.on("enable", () => button.classList.remove("disabled"));
  2396. }
  2397. }
  2398. // place "Restore Last Play" button
  2399. try {
  2400. const restoreButton = doc.createElement("a");
  2401. restoreButton.className = "btn btn-danger btn-sm";
  2402. restoreButton.textContent = "Restore Last Play";
  2403. restoreButton.addEventListener("click", async () => {
  2404. try {
  2405. const lastCode = await codeSaver.restore(site$1.taskURI);
  2406. if (site$1.sourceCode.length == 0 || confirm("Your current code will be replaced. Are you sure?")) {
  2407. site$1.sourceCode = lastCode;
  2408. }
  2409. }
  2410. catch (reason) {
  2411. alert(reason);
  2412. }
  2413. });
  2414. site$1.sideButtonContainer.appendChild(restoreButton);
  2415. }
  2416. catch (e) {
  2417. console.error(e);
  2418. }
  2419. // キーボードショートカット
  2420. config.registerFlag("ui.useKeyboardShortcut", true, "Use Keyboard Shortcuts");
  2421. unsafeWindow.addEventListener("keydown", (event) => {
  2422. if (config.get("ui.useKeyboardShortcut", true)) {
  2423. if (event.key == "Enter" && event.ctrlKey) {
  2424. events.trig("testAndSubmit");
  2425. }
  2426. else if (event.key == "Enter" && event.altKey) {
  2427. events.trig("testAllSamples");
  2428. }
  2429. else if (event.key == "Escape" && event.altKey) {
  2430. pBottomMenu.then(bottomMenu => bottomMenu.toggle());
  2431. }
  2432. }
  2433. });
  2434. })();
  2435. })();