AtCoder Easy Test v2

Make testing sample cases easy

Ekde 2021/11/20. Vidu La ĝisdata versio.

  1. // ==UserScript==
  2. // @name AtCoder Easy Test v2
  3. // @namespace https://atcoder.jp/
  4. // @version 2.9.6
  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://yukicoder.me/problems/no/*
  11. // @match https://yukicoder.me/problems/*
  12. // @match http://codeforces.com/contest/*/problem/*
  13. // @match http://codeforces.com/gym/*/problem/*
  14. // @match http://codeforces.com/problemset/problem/*
  15. // @match http://codeforces.com/group/*/contest/*/problem/*
  16. // @match http://*.contest.codeforces.com/group/*/contest/*/problem/*
  17. // @match https://codeforces.com/contest/*/problem/*
  18. // @match https://codeforces.com/gym/*/problem/*
  19. // @match https://codeforces.com/problemset/problem/*
  20. // @match https://codeforces.com/group/*/contest/*/problem/*
  21. // @match https://*.contest.codeforces.com/group/*/contest/*/problem/*
  22. // @match https://m1.codeforces.com/contest/*/problem/*
  23. // @match https://m2.codeforces.com/contest/*/problem/*
  24. // @match https://m3.codeforces.com/contest/*/problem/*
  25. // @match https://greatest.deepsurf.us/*/scripts/433152-atcoder-easy-test-v2
  26. // @grant unsafeWindow
  27. // @grant GM_getValue
  28. // @grant GM_setValue
  29. // ==/UserScript==
  30. (function() {
  31. function buildParams(data) {
  32. return Object.entries(data).map(([key, value]) => encodeURIComponent(key) + "=" + encodeURIComponent(value)).join("&");
  33. }
  34. function sleep(ms) {
  35. return new Promise(done => setTimeout(done, ms));
  36. }
  37. function doneOrFail(p) {
  38. return p.then(() => Promise.resolve(), () => Promise.resolve());
  39. }
  40. function html2element(html) {
  41. const template = document.createElement("template");
  42. template.innerHTML = html;
  43. return template.content.firstChild;
  44. }
  45. function newElement(tagName, attrs = {}, children = []) {
  46. const e = document.createElement(tagName);
  47. for (const [key, value] of Object.entries(attrs)) {
  48. if (key == "style") {
  49. for (const [propKey, propValue] of Object.entries(value)) {
  50. e.style[propKey] = propValue;
  51. }
  52. }
  53. else {
  54. e[key] = value;
  55. }
  56. }
  57. for (const child of children) {
  58. e.appendChild(child);
  59. }
  60. return e;
  61. }
  62. function uuid() {
  63. return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".
  64. replace(/x/g, () => "0123456789abcdef"[Math.random() * 16 | 0]).
  65. replace(/y/g, () => "89ab"[Math.random() * 4 | 0]);
  66. }
  67. async function loadScript(src, ctx = null, env = {}) {
  68. const js = await fetch(src).then(res => res.text());
  69. const keys = [];
  70. const values = [];
  71. for (const [key, value] of Object.entries(env)) {
  72. keys.push(key);
  73. values.push(value);
  74. }
  75. unsafeWindow["Function"](keys.join(), js).apply(ctx, values);
  76. }
  77. const eventListeners = {};
  78. const events = {
  79. on(name, listener) {
  80. const listeners = (name in eventListeners ? eventListeners[name] : eventListeners[name] = []);
  81. listeners.push(listener);
  82. },
  83. trig(name) {
  84. if (name in eventListeners) {
  85. for (const listener of eventListeners[name])
  86. listener();
  87. }
  88. },
  89. };
  90. function compareVersion(a, b) {
  91. const x = a.split(".").map((s) => parseInt(s, 10));
  92. const y = b.split(".").map((s) => parseInt(s, 10));
  93. for (let i = 0; i < 3; i++) {
  94. if (x[i] < y[i]) {
  95. return -1;
  96. }
  97. else if (x[i] > y[i]) {
  98. return 1;
  99. }
  100. }
  101. return 0;
  102. }
  103. class ObservableValue {
  104. _value;
  105. _listeners;
  106. constructor(value) {
  107. this._value = value;
  108. this._listeners = new Set();
  109. }
  110. get value() {
  111. return this._value;
  112. }
  113. set value(value) {
  114. this._value = value;
  115. for (const listener of this._listeners)
  116. listener(value);
  117. }
  118. addListener(listener) {
  119. this._listeners.add(listener);
  120. listener(this._value);
  121. }
  122. removeListener(listener) {
  123. this._listeners.delete(listener);
  124. }
  125. map(f) {
  126. const y = new ObservableValue(f(this.value));
  127. this.addListener(x => {
  128. y.value = f(x);
  129. });
  130. return y;
  131. }
  132. }
  133.  
  134. 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\">\n <div class=\"panel panel-default\">\n <div class=\"panel-heading\">config</div>\n <div class=\"panel-body\">\n <form id=\"options\" class=\"form-horizontal\">\n </form>\n </div>\n </div>\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>";
  135.  
  136. const options = [];
  137. let data = {};
  138. function toString() {
  139. return JSON.stringify(data);
  140. }
  141. function save() {
  142. GM_setValue("config", toString());
  143. }
  144. function load() {
  145. data = JSON.parse(GM_getValue("config") || "{}");
  146. }
  147. function reset() {
  148. data = {};
  149. save();
  150. }
  151. load();
  152. const config = {
  153. getString(key, defaultValue = "") {
  154. if (!(key in data))
  155. config.setString(key, defaultValue);
  156. return data[key];
  157. },
  158. setString(key, value) {
  159. data[key] = value;
  160. save();
  161. },
  162. has(key) {
  163. return key in data;
  164. },
  165. get(key, defaultValue = null) {
  166. if (!(key in data))
  167. config.set(key, defaultValue);
  168. return JSON.parse(data[key]);
  169. },
  170. set(key, value) {
  171. config.setString(key, JSON.stringify(value));
  172. },
  173. save,
  174. load,
  175. toString,
  176. reset,
  177. /** 設定ページを開く
  178. * クリックなどのイベント時にしか正しく実行できない
  179. */
  180. open() {
  181. const win = window.open("about:blank");
  182. const doc = win.document;
  183. doc.open();
  184. doc.write(hPage);
  185. doc.close();
  186. const root = doc.getElementById("options");
  187. options.sort((a, b) => {
  188. const x = a.key.split(".");
  189. const y = b.key.split(".");
  190. return x < y ? -1 : x > y ? 1 : 0;
  191. });
  192. for (const { type, key, defaultValue, description } of options) {
  193. const id = uuid();
  194. const control = newElement("div", { className: "col-sm-3 text-center" });
  195. const group = newElement("div", { className: "form-group" }, [
  196. control,
  197. newElement("label", {
  198. className: "col-sm-3",
  199. htmlFor: id,
  200. textContent: key,
  201. style: {
  202. fontFamily: "monospace",
  203. },
  204. }),
  205. newElement("label", {
  206. className: "col-sm-6",
  207. htmlFor: id,
  208. textContent: description,
  209. }),
  210. ]);
  211. root.appendChild(group);
  212. switch (type) {
  213. case "flag": {
  214. control.appendChild(newElement("input", {
  215. id,
  216. type: "checkbox",
  217. checked: config.get(key, defaultValue),
  218. onchange() {
  219. config.set(key, this.checked);
  220. },
  221. }));
  222. break;
  223. }
  224. case "count": {
  225. control.appendChild(newElement("input", {
  226. id,
  227. type: "number",
  228. min: "0",
  229. value: config.get(key, defaultValue),
  230. onchange() {
  231. config.set(key, +this.value);
  232. },
  233. }));
  234. break;
  235. }
  236. default:
  237. throw new TypeError(`AtCoderEasyTest.setting: undefined option type ${type} for ${key}`);
  238. }
  239. }
  240. root.appendChild(newElement("button", {
  241. className: "btn btn-danger",
  242. textContent: "Reset",
  243. type: "button",
  244. onclick() {
  245. if (win.confirm("Configuration data will be cleared. Are you sure?")) {
  246. config.reset();
  247. }
  248. },
  249. }));
  250. },
  251. /** 設定項目を登録 */
  252. registerFlag(key, defaultValue, description) {
  253. options.push({
  254. type: "flag",
  255. key,
  256. defaultValue,
  257. description,
  258. });
  259. },
  260. registerCount(key, defaultValue, description) {
  261. options.push({
  262. type: "count",
  263. key,
  264. defaultValue,
  265. description,
  266. });
  267. },
  268. };
  269.  
  270. config.registerCount("codeSaver.limit", 10, "Max number to save codes");
  271. const codeSaver = {
  272. get() {
  273. // `json` は、ソースコード文字列またはJSON文字列
  274. let json = unsafeWindow.localStorage.AtCoderEasyTest$lastCode;
  275. let data = [];
  276. try {
  277. if (typeof json == "string") {
  278. data.push(...JSON.parse(json));
  279. }
  280. else {
  281. data = [];
  282. }
  283. }
  284. catch (e) {
  285. data.push({
  286. path: unsafeWindow.localStorage.AtCoderEasyTset$lastPage,
  287. code: json,
  288. });
  289. }
  290. return data;
  291. },
  292. set(data) {
  293. unsafeWindow.localStorage.AtCoderEasyTest$lastCode = JSON.stringify(data);
  294. },
  295. save(code) {
  296. let data = codeSaver.get();
  297. const idx = data.findIndex(({ path }) => path == location.pathname);
  298. if (idx != -1)
  299. data.splice(idx, idx + 1);
  300. data.push({
  301. path: location.pathname,
  302. code,
  303. });
  304. while (data.length > config.get("codeSaver.limit", 10))
  305. data.shift();
  306. codeSaver.set(data);
  307. },
  308. restore() {
  309. const data = codeSaver.get();
  310. const idx = data.findIndex(({ path }) => path == location.pathname);
  311. if (idx == -1 || !(data[idx] instanceof Object))
  312. return Promise.reject(`No saved code found for ${location.pathname}`);
  313. return Promise.resolve(data[idx].code);
  314. }
  315. };
  316.  
  317. function similarLangs(targetLang, candidateLangs) {
  318. const [targetName, targetDetail] = targetLang.split(" ", 2);
  319. const selectedLangs = candidateLangs.filter(candidateLang => {
  320. const [name, _] = candidateLang.split(" ", 2);
  321. return name == targetName;
  322. }).map(candidateLang => {
  323. const [_, detail] = candidateLang.split(" ", 2);
  324. return [candidateLang, similarity(detail, targetDetail)];
  325. });
  326. return selectedLangs.sort((a, b) => a[1] - b[1]).map(([lang, _]) => lang);
  327. }
  328. function similarity(s, t) {
  329. const n = s.length, m = t.length;
  330. let dp = new Array(m + 1).fill(0);
  331. for (let i = 0; i < n; i++) {
  332. const dp2 = new Array(m + 1).fill(0);
  333. for (let j = 0; j < m; j++) {
  334. const cost = (s.charCodeAt(i) - t.charCodeAt(j)) ** 2;
  335. dp2[j + 1] = Math.min(dp[j] + cost, dp[j + 1] + cost * 0.25, dp2[j] + cost * 0.25);
  336. }
  337. dp = dp2;
  338. }
  339. return dp[m];
  340. }
  341.  
  342. class CodeRunner {
  343. get label() {
  344. return this._label;
  345. }
  346. constructor(label, site) {
  347. this._label = `${label} [${site}]`;
  348. }
  349. async test(sourceCode, input, expectedOutput, options) {
  350. let result = { status: "IE", input };
  351. try {
  352. result = await this.run(sourceCode, input);
  353. }
  354. catch (e) {
  355. result.error = e.toString();
  356. return result;
  357. }
  358. if (expectedOutput != null)
  359. result.expectedOutput = expectedOutput;
  360. if (result.status != "OK" || typeof expectedOutput != "string")
  361. return result;
  362. let output = result.output || "";
  363. if (options.trim) {
  364. expectedOutput = expectedOutput.trim();
  365. output = output.trim();
  366. }
  367. let equals = (x, y) => x === y;
  368. if (options.allowableError) {
  369. const floatPattern = /^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$/;
  370. const superEquals = equals;
  371. equals = (x, y) => {
  372. if (floatPattern.test(x) && floatPattern.test(y))
  373. return Math.abs(parseFloat(x) - parseFloat(y)) <= options.allowableError;
  374. return superEquals(x, y);
  375. };
  376. }
  377. if (options.split) {
  378. const superEquals = equals;
  379. equals = (x, y) => {
  380. const xs = x.split(/\s+/);
  381. const ys = y.split(/\s+/);
  382. if (xs.length != ys.length)
  383. return false;
  384. const len = xs.length;
  385. for (let i = 0; i < len; i++) {
  386. if (!superEquals(xs[i], ys[i]))
  387. return false;
  388. }
  389. return true;
  390. };
  391. }
  392. result.status = equals(output, expectedOutput) ? "AC" : "WA";
  393. return result;
  394. }
  395. }
  396.  
  397. class CustomRunner extends CodeRunner {
  398. run;
  399. constructor(label, run) {
  400. super(label, "Browser");
  401. this.run = run;
  402. }
  403. }
  404.  
  405. let waitAtCoderCustomTest = Promise.resolve();
  406. const AtCoderCustomTestBase = location.href.replace(/\/tasks\/.+$/, "/custom_test");
  407. const AtCoderCustomTestResultAPI = AtCoderCustomTestBase + "/json?reload=true";
  408. const AtCoderCustomTestSubmitAPI = AtCoderCustomTestBase + "/submit/json";
  409. class AtCoderRunner extends CodeRunner {
  410. languageId;
  411. constructor(languageId, label) {
  412. super(label, "AtCoder");
  413. this.languageId = languageId;
  414. }
  415. async run(sourceCode, input) {
  416. const promise = this.submit(sourceCode, input);
  417. waitAtCoderCustomTest = promise;
  418. return await promise;
  419. }
  420. async submit(sourceCode, input) {
  421. try {
  422. await waitAtCoderCustomTest;
  423. }
  424. catch (error) {
  425. console.error(error);
  426. }
  427. const error = await fetch(AtCoderCustomTestSubmitAPI, {
  428. method: "POST",
  429. credentials: "include",
  430. headers: {
  431. "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
  432. },
  433. body: buildParams({
  434. "data.LanguageId": String(this.languageId),
  435. sourceCode,
  436. input,
  437. csrf_token: unsafeWindow.csrfToken,
  438. }),
  439. }).then(r => r.text());
  440. if (error) {
  441. throw new Error(error);
  442. }
  443. await sleep(100);
  444. for (;;) {
  445. const data = await fetch(AtCoderCustomTestResultAPI, {
  446. method: "GET",
  447. credentials: "include",
  448. }).then(r => r.json());
  449. if (!("Result" in data))
  450. continue;
  451. const result = data.Result;
  452. if ("Interval" in data) {
  453. await sleep(data.Interval);
  454. continue;
  455. }
  456. return {
  457. status: (result.ExitCode == 0) ? "OK" : (result.TimeConsumption == -1) ? "CE" : "RE",
  458. exitCode: result.ExitCode,
  459. execTime: result.TimeConsumption,
  460. memory: result.MemoryConsumption,
  461. input,
  462. output: data.Stdout,
  463. error: data.Stderr,
  464. };
  465. }
  466. }
  467. }
  468.  
  469. class PaizaIORunner extends CodeRunner {
  470. name;
  471. constructor(name, label) {
  472. super(label, "PaizaIO");
  473. this.name = name;
  474. }
  475. async run(sourceCode, input) {
  476. let id, status, error;
  477. try {
  478. const res = await fetch("https://api.paiza.io/runners/create?" + buildParams({
  479. source_code: sourceCode,
  480. language: this.name,
  481. input,
  482. longpoll: "true",
  483. longpoll_timeout: "10",
  484. api_key: "guest",
  485. }), {
  486. method: "POST",
  487. mode: "cors",
  488. }).then(r => r.json());
  489. id = res.id;
  490. status = res.status;
  491. error = res.error;
  492. }
  493. catch (error) {
  494. return {
  495. status: "IE",
  496. input,
  497. error: String(error),
  498. };
  499. }
  500. while (status == "running") {
  501. const res = await fetch("https://api.paiza.io/runners/get_status?" + buildParams({
  502. id,
  503. api_key: "guest",
  504. }), {
  505. mode: "cors",
  506. }).then(res => res.json());
  507. status = res.status;
  508. error = res.error;
  509. }
  510. const res = await fetch("https://api.paiza.io/runners/get_details?" + buildParams({
  511. id,
  512. api_key: "guest",
  513. }), {
  514. mode: "cors",
  515. }).then(r => r.json());
  516. const result = {
  517. status: "OK",
  518. exitCode: String(res.exit_code),
  519. execTime: +res.time * 1e3,
  520. memory: +res.memory * 1e-3,
  521. input,
  522. };
  523. if (res.build_result == "failure") {
  524. result.status = "CE";
  525. result.exitCode = res.build_exit_code;
  526. result.output = res.build_stdout;
  527. result.error = res.build_stderr;
  528. }
  529. else {
  530. result.status = (res.result == "timeout") ? "TLE" : (res.result == "failure") ? "RE" : "OK";
  531. result.exitCode = res.exit_code;
  532. result.output = res.stdout;
  533. result.error = res.stderr;
  534. }
  535. return result;
  536. }
  537. }
  538.  
  539. class WandboxRunner extends CodeRunner {
  540. name;
  541. options;
  542. constructor(name, label, options = {}) {
  543. super(label, "Wandbox");
  544. this.name = name;
  545. this.options = options;
  546. }
  547. getOptions(sourceCode, input) {
  548. if (typeof this.options == "function")
  549. return this.options(sourceCode, input);
  550. return this.options;
  551. }
  552. run(sourceCode, input) {
  553. const options = this.getOptions(sourceCode, input);
  554. return this.request(Object.assign({
  555. compiler: this.name,
  556. code: sourceCode,
  557. stdin: input,
  558. }, options));
  559. }
  560. async request(body) {
  561. const startTime = Date.now();
  562. let res;
  563. try {
  564. res = await fetch("https://wandbox.org/api/compile.json", {
  565. method: "POST",
  566. mode: "cors",
  567. headers: {
  568. "Content-Type": "application/json",
  569. },
  570. body: JSON.stringify(body),
  571. }).then(r => r.json());
  572. }
  573. catch (error) {
  574. console.error(error);
  575. return {
  576. status: "IE",
  577. input: body.stdin,
  578. error: String(error),
  579. };
  580. }
  581. const endTime = Date.now();
  582. const result = {
  583. status: "OK",
  584. exitCode: String(res.status),
  585. execTime: endTime - startTime,
  586. input: body.stdin,
  587. output: String(res.program_output || ""),
  588. error: String(res.program_error || ""),
  589. };
  590. // 正常終了以外の場合
  591. if (res.status != 0) {
  592. if (res.signal) {
  593. result.exitCode += ` (${res.signal})`;
  594. }
  595. result.output = String(res.compiler_output || "") + String(result.output || "");
  596. result.error = String(res.compiler_error || "") + String(result.error || "");
  597. if (res.compiler_output || res.compiler_error) {
  598. result.status = "CE";
  599. }
  600. else {
  601. result.status = "RE";
  602. }
  603. }
  604. return result;
  605. }
  606. }
  607.  
  608. class WandboxCppRunner extends WandboxRunner {
  609. async run(sourceCode, input) {
  610. // ACL を結合する
  611. const ACLBase = "https://cdn.jsdelivr.net/gh/atcoder/ac-library/";
  612. const files = new Map();
  613. const includeHeader = async (source) => {
  614. const pattern = /^#\s*include\s*[<"]atcoder\/([^>"]+)[>"]/gm;
  615. const loaded = [];
  616. let match;
  617. while (match = pattern.exec(source)) {
  618. const file = "atcoder/" + match[1];
  619. if (files.has(file))
  620. continue;
  621. files.set(file, null);
  622. loaded.push([file, fetch(ACLBase + file, { mode: "cors", cache: "force-cache", }).then(r => r.text())]);
  623. }
  624. const included = await Promise.all(loaded.map(async ([file, r]) => {
  625. const source = await r;
  626. files.set(file, source);
  627. return source;
  628. }));
  629. for (const source of included) {
  630. await includeHeader(source);
  631. }
  632. };
  633. await includeHeader(sourceCode);
  634. const codes = [];
  635. for (const [file, code] of files) {
  636. codes.push({ file, code, });
  637. }
  638. const options = this.getOptions(sourceCode, input);
  639. return await this.request(Object.assign({
  640. compiler: this.name,
  641. code: sourceCode,
  642. stdin: input,
  643. codes,
  644. "compiler-option-raw": "-I.",
  645. }, options));
  646. }
  647. }
  648.  
  649. let brythonRunnerLoaded = false;
  650. const brythonRunner = new CustomRunner("Brython", async (sourceCode, input) => {
  651. if (!brythonRunnerLoaded) {
  652. // BrythonRunner を読み込む
  653. await new Promise((resolve) => {
  654. const script = document.createElement("script");
  655. script.src = "https://cdn.jsdelivr.net/gh/pythonpad/brython-runner/lib/brython-runner.bundle.js";
  656. script.onload = () => {
  657. brythonRunnerLoaded = true;
  658. resolve(null);
  659. };
  660. document.head.appendChild(script);
  661. });
  662. }
  663. let stdout = "";
  664. let stderr = "";
  665. let stdinOffset = 0;
  666. const BrythonRunner = unsafeWindow.BrythonRunner;
  667. const runner = new BrythonRunner({
  668. stdout: { write(content) { stdout += content; }, flush() { } },
  669. stderr: { write(content) { stderr += content; }, flush() { } },
  670. stdin: { async readline() {
  671. let index = input.indexOf("\n", stdinOffset) + 1;
  672. if (index == 0)
  673. index = input.length;
  674. const text = input.slice(stdinOffset, index);
  675. stdinOffset = index;
  676. return text;
  677. } },
  678. });
  679. const timeStart = Date.now();
  680. await runner.runCode(sourceCode);
  681. const timeEnd = Date.now();
  682. return {
  683. status: "OK",
  684. exitCode: "0",
  685. execTime: (timeEnd - timeStart),
  686. input,
  687. output: stdout,
  688. error: stderr,
  689. };
  690. });
  691.  
  692. async function loadPyodide() {
  693. const script = await fetch("https://cdn.jsdelivr.net/pyodide/v0.18.1/full/pyodide.js").then(res => res.text());
  694. unsafeWindow["Function"](script)();
  695. const pyodide = await unsafeWindow["loadPyodide"]({
  696. indexURL: "https://cdn.jsdelivr.net/pyodide/v0.18.1/full/",
  697. });
  698. await pyodide.runPythonAsync(`
  699. import contextlib, io, platform
  700. class __redirect_stdin(contextlib._RedirectStream):
  701. _stream = "stdin"
  702. `);
  703. return pyodide;
  704. }
  705. let _pyodide = Promise.reject("Pyodide is not yet loaded");
  706. let _serial = Promise.resolve();
  707. const pyodideRunner = new CustomRunner("Pyodide", (sourceCode, input) => new Promise((resolve, reject) => {
  708. _serial = _serial.finally(async () => {
  709. const pyodide = await (_pyodide = _pyodide.catch(loadPyodide));
  710. const code = `
  711. def __run():
  712. global __stdout, __stderr, __stdin, __code
  713. with __redirect_stdin(io.StringIO(__stdin)):
  714. with contextlib.redirect_stdout(io.StringIO()) as __stdout:
  715. with contextlib.redirect_stderr(io.StringIO()) as __stderr:
  716. try:
  717. pass
  718. ` + sourceCode.split("\n").map(line => " " + line).join("\n") + `
  719. except SystemExit as e:
  720. __code = e.code
  721. `;
  722. let status = "OK";
  723. let exitCode = "0";
  724. let stdout = "";
  725. let stderr = "";
  726. let startTime = -Infinity;
  727. let endTime = Infinity;
  728. pyodide.globals.__stdin = input;
  729. try {
  730. pyodide.globals.__code = null;
  731. await pyodide.loadPackagesFromImports(code);
  732. await pyodide.runPythonAsync(code);
  733. startTime = Date.now();
  734. pyodide.runPython("__run()");
  735. endTime = Date.now();
  736. stdout += pyodide.globals.__stdout.getvalue();
  737. stderr += pyodide.globals.__stderr.getvalue();
  738. if (typeof pyodide.globals.__code == "number") {
  739. exitCode = String(pyodide.globals.__code);
  740. if (pyodide.globals.__code != 0)
  741. status = "RE";
  742. }
  743. }
  744. catch (error) {
  745. status = "RE";
  746. exitCode = "-1";
  747. stderr += error.toString();
  748. }
  749. resolve({
  750. status,
  751. exitCode,
  752. execTime: (endTime - startTime),
  753. input,
  754. output: stdout,
  755. error: stderr,
  756. });
  757. });
  758. }));
  759.  
  760. function pairs(list) {
  761. const pairs = [];
  762. const len = list.length >> 1;
  763. for (let i = 0; i < len; i++)
  764. pairs.push([list[i * 2], list[i * 2 + 1]]);
  765. return pairs;
  766. }
  767. async function init$5() {
  768. if (location.host != "atcoder.jp")
  769. throw "Not AtCoder";
  770. const doc = unsafeWindow.document;
  771. const eLanguage = unsafeWindow.$("#select-lang>select");
  772. const langMap = {
  773. 4001: "C GCC 9.2.1",
  774. 4002: "C Clang 10.0.0",
  775. 4003: "C++ GCC 9.2.1",
  776. 4004: "C++ Clang 10.0.0",
  777. 4005: "Java OpenJDK 11.0.6",
  778. 4006: "Python3 CPython 3.8.2",
  779. 4007: "Bash 5.0.11",
  780. 4008: "bc 1.07.1",
  781. 4009: "Awk GNU Awk 4.1.4",
  782. 4010: "C# .NET Core 3.1.201",
  783. 4011: "C# Mono-mcs 6.8.0.105",
  784. 4012: "C# Mono-csc 3.5.0",
  785. 4013: "Clojure 1.10.1.536",
  786. 4014: "Crystal 0.33.0",
  787. 4015: "D DMD 2.091.0",
  788. 4016: "D GDC 9.2.1",
  789. 4017: "D LDC 1.20.1",
  790. 4018: "Dart 2.7.2",
  791. 4019: "dc 1.4.1",
  792. 4020: "Erlang 22.3",
  793. 4021: "Elixir 1.10.2",
  794. 4022: "F# .NET Core 3.1.201",
  795. 4023: "F# Mono 10.2.3",
  796. 4024: "Forth gforth 0.7.3",
  797. 4025: "Fortran GNU Fortran 9.2.1",
  798. 4026: "Go 1.14.1",
  799. 4027: "Haskell GHC 8.8.3",
  800. 4028: "Haxe 4.0.3",
  801. 4029: "Haxe 4.0.3",
  802. 4030: "JavaScript Node.js 12.16.1",
  803. 4031: "Julia 1.4.0",
  804. 4032: "Kotlin 1.3.71",
  805. 4033: "Lua Lua 5.3.5",
  806. 4034: "Lua LuaJIT 2.1.0",
  807. 4035: "Dash 0.5.8",
  808. 4036: "Nim 1.0.6",
  809. 4037: "Objective-C Clang 10.0.0",
  810. 4038: "Lisp SBCL 2.0.3",
  811. 4039: "OCaml 4.10.0",
  812. 4040: "Octave 5.2.0",
  813. 4041: "Pascal FPC 3.0.4",
  814. 4042: "Perl 5.26.1",
  815. 4043: "Raku Rakudo 2020.02.1",
  816. 4044: "PHP 7.4.4",
  817. 4045: "Prolog SWI-Prolog 8.0.3",
  818. 4046: "Python PyPy2 7.3.0",
  819. 4047: "Python3 PyPy3 7.3.0",
  820. 4048: "Racket 7.6",
  821. 4049: "Ruby 2.7.1",
  822. 4050: "Rust 1.42.0",
  823. 4051: "Scala 2.13.1",
  824. 4052: "Java OpenJDK 1.8.0",
  825. 4053: "Scheme Gauche 0.9.9",
  826. 4054: "ML MLton 20130715",
  827. 4055: "Swift 5.2.1",
  828. 4056: "Text cat 8.28",
  829. 4057: "TypeScript 3.8",
  830. 4058: "Basic .NET Core 3.1.101",
  831. 4059: "Zsh 5.4.2",
  832. 4060: "COBOL Fixed OpenCOBOL 1.1.0",
  833. 4061: "COBOL Free OpenCOBOL 1.1.0",
  834. 4062: "Brainfuck bf 20041219",
  835. 4063: "Ada Ada2012 GNAT 9.2.1",
  836. 4064: "Unlambda 2.0.0",
  837. 4065: "Cython 0.29.16",
  838. 4066: "Sed 4.4",
  839. 4067: "Vim 8.2.0460",
  840. };
  841. const languageId = new ObservableValue(eLanguage.val());
  842. eLanguage.change(() => {
  843. languageId.value = eLanguage.val();
  844. });
  845. const language = languageId.map(lang => langMap[lang]);
  846. function getTestCases() {
  847. const selectors = [
  848. ["#task-statement p+pre.literal-block", ".section"],
  849. ["#task-statement pre.source-code-for-copy", ".part"],
  850. ["#task-statement .lang>*:nth-child(1) .div-btn-copy+pre", ".part"],
  851. ["#task-statement .div-btn-copy+pre", ".part"],
  852. ["#task-statement>.part pre.linenums", ".part"],
  853. ["#task-statement>.part:not(.io-style)>h3+section>pre", ".part"],
  854. ["#task-statement pre", ".part"],
  855. ];
  856. for (const [selector, closestSelector] of selectors) {
  857. let e = [...doc.querySelectorAll(selector)];
  858. e = e.filter(e => !e.closest(".io-style")); // practice2
  859. if (e.length == 0)
  860. continue;
  861. return pairs(e).map(([input, output], index) => {
  862. const container = input.closest(closestSelector) || input.parentElement;
  863. return {
  864. selector,
  865. title: `Sample ${index + 1}`,
  866. input: input.textContent,
  867. output: output.textContent,
  868. anchor: container.querySelector(".btn-copy") || container.querySelector("h1,h2,h3,h4,h5,h6"),
  869. };
  870. });
  871. }
  872. { // maximum_cup_2018_d
  873. let e = [...doc.querySelectorAll("#task-statement .div-btn-copy+pre")];
  874. e = e.filter(f => !f.childElementCount);
  875. if (e.length) {
  876. return pairs(e).map(([input, output], index) => ({
  877. selector: "#task-statement .div-btn-copy+pre",
  878. title: `Sample ${index + 1}`,
  879. input: input.textContent,
  880. output: output.textContent,
  881. anchor: (input.closest(".part") || input.parentElement).querySelector(".btn-copy"),
  882. }));
  883. }
  884. }
  885. return [];
  886. }
  887. return {
  888. name: "AtCoder",
  889. language,
  890. get sourceCode() {
  891. return unsafeWindow.getSourceCode();
  892. },
  893. set sourceCode(sourceCode) {
  894. doc.querySelector(".plain-textarea").value = sourceCode;
  895. unsafeWindow.$(".editor").data("editor").doc.setValue(sourceCode);
  896. },
  897. submit() {
  898. doc.querySelector("#submit").click();
  899. },
  900. get testButtonContainer() {
  901. return doc.querySelector("#submit").parentElement;
  902. },
  903. get sideButtonContainer() {
  904. return doc.querySelector(".editor-buttons");
  905. },
  906. get bottomMenuContainer() {
  907. return doc.getElementById("main-div");
  908. },
  909. get resultListContainer() {
  910. return doc.querySelector(".form-code-submit");
  911. },
  912. get testCases() {
  913. return getTestCases();
  914. },
  915. get jQuery() {
  916. return unsafeWindow["jQuery"];
  917. },
  918. };
  919. }
  920.  
  921. async function init$4() {
  922. if (location.host != "yukicoder.me")
  923. throw "Not yukicoder";
  924. const $ = unsafeWindow.$;
  925. const doc = unsafeWindow.document;
  926. const editor = unsafeWindow.ace.edit("rich_source");
  927. const eSourceObject = $("#source");
  928. const eLang = $("#lang");
  929. const eSamples = $(".sample");
  930. const langMap = {
  931. "cpp14": "C++ C++14 GCC 11.1.0 + Boost 1.77.0",
  932. "cpp17": "C++ C++17 GCC 11.1.0 + Boost 1.77.0",
  933. "cpp-clang": "C++ C++17 Clang 10.0.0 + Boost 1.76.0",
  934. "cpp23": "C++ C++11 GCC 8.4.1",
  935. "c11": "C++ C++11 GCC 11.1.0",
  936. "c": "C C90 GCC 8.4.1",
  937. "java8": "Java Java16 OpenJDK 16.0.1",
  938. "csharp": "C# CSC 3.9.0",
  939. "csharp_mono": "C# Mono 6.12.0.147",
  940. "csharp_dotnet": "C# .NET 5.0",
  941. "perl": "Perl 5.26.3",
  942. "raku": "Raku Rakudo v2021-07-2-g74d7ff771",
  943. "php": "PHP 7.2.24",
  944. "php7": "PHP 8.0.8",
  945. "python3": "Python3 3.9.6 + numpy 1.14.5 + scipy 1.1.0",
  946. "pypy2": "Python PyPy2 7.3.5",
  947. "pypy3": "Python3 PyPy3 7.3.5",
  948. "ruby": "Ruby 3.0.2p107",
  949. "d": "D DMD 2.097.1",
  950. "go": "Go 1.16.6",
  951. "haskell": "Haskell 8.10.5",
  952. "scala": "Scala 2.13.6",
  953. "nim": "Nim 1.4.8",
  954. "rust": "Rust 1.53.0",
  955. "kotlin": "Kotlin 1.5.21",
  956. "scheme": "Scheme Gauche 0.9.10",
  957. "crystal": "Crystal 1.1.1",
  958. "swift": "Swift 5.4.2",
  959. "ocaml": "OCaml 4.12.0",
  960. "clojure": "Clojure 1.10.2.790",
  961. "fsharp": "F# 5.0",
  962. "elixir": "Elixir 1.7.4",
  963. "lua": "Lua LuaJIT 2.0.5",
  964. "fortran": "Fortran gFortran 8.4.1",
  965. "node": "JavaScript Node.js 15.5.0",
  966. "typescript": "TypeScript 4.3.5",
  967. "lisp": "Lisp Common Lisp sbcl 2.1.6",
  968. "sml": "ML Standard ML MLton 20180207-6",
  969. "kuin": "Kuin KuinC++ v.2021.7.17",
  970. "vim": "Vim v8.2",
  971. "sh": "Bash 4.4.19",
  972. "nasm": "Assembler nasm 2.13.03",
  973. "clay": "cLay 20210917-1",
  974. "bf": "Brainfuck BFI 1.1",
  975. "Whitespace": "Whitespace 0.3",
  976. "text": "Text cat 8.3",
  977. };
  978. const language = new ObservableValue(langMap[eLang.val()]);
  979. eLang.on("change", () => {
  980. language.value = langMap[eLang.val()];
  981. });
  982. return {
  983. name: "yukicoder",
  984. language,
  985. get sourceCode() {
  986. if (eSourceObject.is(":visible"))
  987. return eSourceObject.val();
  988. return editor.getSession().getValue();
  989. },
  990. set sourceCode(sourceCode) {
  991. eSourceObject.val(sourceCode);
  992. editor.getSession().setValue(sourceCode);
  993. },
  994. submit() {
  995. doc.querySelector(`#submit_form input[type="submit"]`).click();
  996. },
  997. get testButtonContainer() {
  998. return doc.querySelector("#submit_form");
  999. },
  1000. get sideButtonContainer() {
  1001. return doc.querySelector("#toggle_source_editor").parentElement;
  1002. },
  1003. get bottomMenuContainer() {
  1004. return doc.body;
  1005. },
  1006. get resultListContainer() {
  1007. return doc.querySelector("#content");
  1008. },
  1009. get testCases() {
  1010. const testCases = [];
  1011. let sampleId = 1;
  1012. for (let i = 0; i < eSamples.length; i++) {
  1013. const eSample = eSamples.eq(i);
  1014. const [eInput, eOutput] = eSample.find("pre");
  1015. const anchorContainer = $(`<span>`);
  1016. const anchor = $(`<span>`);
  1017. anchorContainer.append(anchor);
  1018. eSample.find("h6").eq(0).appendTo(anchorContainer);
  1019. anchorContainer.insertAfter(eSample.find("button").eq(0));
  1020. testCases.push({
  1021. title: `Sample ${sampleId++}`,
  1022. input: eInput.textContent,
  1023. output: eOutput.textContent,
  1024. anchor: anchor[0],
  1025. });
  1026. }
  1027. return testCases;
  1028. },
  1029. get jQuery() {
  1030. return $;
  1031. },
  1032. };
  1033. }
  1034.  
  1035. class Editor {
  1036. _element;
  1037. constructor(lang) {
  1038. this._element = document.createElement("textarea");
  1039. this._element.style.fontFamily = "monospace";
  1040. this._element.style.width = "100%";
  1041. this._element.style.minHeight = "5em";
  1042. }
  1043. get element() {
  1044. return this._element;
  1045. }
  1046. get sourceCode() {
  1047. return this._element.value;
  1048. }
  1049. set sourceCode(sourceCode) {
  1050. this._element.value = sourceCode;
  1051. }
  1052. setLanguage(lang) {
  1053. }
  1054. }
  1055.  
  1056. var langMap = {
  1057. 3: "Delphi 7",
  1058. 4: "Pascal Free Pascal 3.0.2",
  1059. 6: "PHP 7.2.13",
  1060. 7: "Python 2.7.18",
  1061. 9: "C# Mono 6.8",
  1062. 12: "Haskell GHC 8.10.1",
  1063. 13: "Perl 5.20.1",
  1064. 19: "OCaml 4.02.1",
  1065. 20: "Scala 2.12.8",
  1066. 28: "D DMD32 v2.091.0",
  1067. 31: "Python3 3.8.10",
  1068. 32: "Go 1.15.6",
  1069. 34: "JavaScript V8 4.8.0",
  1070. 36: "Java 1.8.0_241",
  1071. 40: "Python PyPy2 2.7 (7.3.0)",
  1072. 41: "Python3 PyPy3 3.7 (7.3.0)",
  1073. 43: "C C11 GCC 5.1.0",
  1074. 48: "Kotlin 1.5.31",
  1075. 49: "Rust 1.49.0",
  1076. 50: "C++ C++14 G++ 6.4.0",
  1077. 51: "Pascal PascalABC.NET 3.4.1",
  1078. 52: "C++ C++17 Clang++",
  1079. 54: "C++ C++17 G++ 7.3.0",
  1080. 55: "JavaScript Node.js 12.6.3",
  1081. 59: "C++ Microsoft Visual C++ 2017",
  1082. 60: "Java 11.0.6",
  1083. 61: "C++ C++17 9.2.0 (64 bit, msys 2)",
  1084. 65: "C# 8, .NET Core 3.1",
  1085. 67: "Ruby 3.0.0",
  1086. 70: "Python3 PyPy 3.7 (7.3.5, 64bit)",
  1087. 72: "Kotlin 1.5.31",
  1088. 73: "C++ GNU G++ 11.2.0 (64 bit, winlibs)",
  1089. };
  1090.  
  1091. config.registerFlag("site.codeforces.showEditor", true, "Show Editor in Codeforces Problem Page");
  1092. async function init$3() {
  1093. if (location.host != "codeforces.com")
  1094. throw "not Codeforces";
  1095. //TODO: m1.codeforces.com, m2.codeforces.com, m3.codeforces.com に対応する
  1096. const doc = unsafeWindow.document;
  1097. const eLang = doc.querySelector("select[name='programTypeId']");
  1098. doc.head.appendChild(newElement("link", {
  1099. rel: "stylesheet",
  1100. href: "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css",
  1101. }));
  1102. const eButtons = newElement("span");
  1103. doc.querySelector(".submitForm").appendChild(eButtons);
  1104. await loadScript("https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js");
  1105. const jQuery = unsafeWindow["jQuery"].noConflict();
  1106. unsafeWindow["jQuery"] = unsafeWindow["$"];
  1107. unsafeWindow["jQuery11"] = jQuery;
  1108. await loadScript("https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js", null, { jQuery, $: jQuery });
  1109. const language = new ObservableValue(langMap[eLang.value]);
  1110. eLang.addEventListener("change", () => {
  1111. language.value = langMap[eLang.value];
  1112. });
  1113. let _sourceCode = "";
  1114. const eFile = doc.querySelector(".submitForm").elements["sourceFile"];
  1115. eFile.addEventListener("change", async () => {
  1116. if (eFile.files[0]) {
  1117. _sourceCode = await eFile.files[0].text();
  1118. if (editor)
  1119. editor.sourceCode = _sourceCode;
  1120. }
  1121. });
  1122. let editor = null;
  1123. let waitCfFastSubmitCount = 0;
  1124. const waitCfFastSubmit = setInterval(() => {
  1125. if (document.getElementById("editor")) {
  1126. // cf-fast-submit
  1127. if (editor && editor.element)
  1128. editor.element.style.display = "none";
  1129. // 言語セレクトを同期させる
  1130. const eLang2 = doc.querySelector(".submit-form select[name='programTypeId']");
  1131. if (eLang2) {
  1132. eLang.addEventListener("change", () => {
  1133. eLang2.value = eLang.value;
  1134. });
  1135. eLang2.addEventListener("change", () => {
  1136. eLang.value = eLang2.value;
  1137. language.value = langMap[eLang.value];
  1138. });
  1139. }
  1140. // TODO: 選択されたファイルをどうかする
  1141. // エディタを使う
  1142. const aceEditor = unsafeWindow["ace"].edit("editor");
  1143. editor = {
  1144. get sourceCode() {
  1145. return aceEditor.getValue();
  1146. },
  1147. set sourceCode(sourceCode) {
  1148. aceEditor.setValue(sourceCode);
  1149. },
  1150. setLanguage(lang) { },
  1151. };
  1152. // ボタンを追加する
  1153. const buttonContainer = doc.querySelector(".submit-form .submit").parentElement;
  1154. buttonContainer.appendChild(newElement("a", {
  1155. className: "btn btn-info",
  1156. textContent: "Test & Submit",
  1157. onclick: () => events.trig("testAndSubmit"),
  1158. }));
  1159. buttonContainer.appendChild(newElement("a", {
  1160. className: "btn btn-default",
  1161. textContent: "Test All Samples",
  1162. onclick: () => events.trig("testAllSamples"),
  1163. }));
  1164. clearInterval(waitCfFastSubmit);
  1165. }
  1166. else {
  1167. waitCfFastSubmitCount++;
  1168. if (waitCfFastSubmitCount >= 100)
  1169. clearInterval(waitCfFastSubmit);
  1170. }
  1171. }, 100);
  1172. if (config.get("site.codeforces.showEditor", true)) {
  1173. editor = new Editor(langMap[eLang.value].split(" ")[0]);
  1174. doc.getElementById("pageContent").appendChild(editor.element);
  1175. language.addListener(lang => {
  1176. editor.setLanguage(lang);
  1177. });
  1178. }
  1179. return {
  1180. name: "Codeforces",
  1181. language,
  1182. get sourceCode() {
  1183. if (editor)
  1184. return editor.sourceCode;
  1185. return _sourceCode;
  1186. },
  1187. set sourceCode(sourceCode) {
  1188. const container = new DataTransfer();
  1189. container.items.add(new File([sourceCode], "prog.txt", { type: "text/plain" }));
  1190. const eFile = doc.querySelector(".submitForm").elements["sourceFile"];
  1191. eFile.files = container.files;
  1192. _sourceCode = sourceCode;
  1193. if (editor)
  1194. editor.sourceCode = sourceCode;
  1195. },
  1196. submit() {
  1197. if (editor)
  1198. _sourceCode = editor.sourceCode;
  1199. this.sourceCode = _sourceCode;
  1200. doc.querySelector(`.submitForm .submit`).click();
  1201. },
  1202. get testButtonContainer() {
  1203. return eButtons;
  1204. },
  1205. get sideButtonContainer() {
  1206. return eButtons;
  1207. },
  1208. get bottomMenuContainer() {
  1209. return doc.body;
  1210. },
  1211. get resultListContainer() {
  1212. return doc.querySelector("#pageContent");
  1213. },
  1214. get testCases() {
  1215. return [...doc.querySelectorAll(".sample-test")].map((e, i) => ({
  1216. title: `Sample ${i + 1}`,
  1217. input: e.querySelector(".input pre").textContent,
  1218. output: e.querySelector(".output pre").textContent,
  1219. anchor: e.querySelector(".input .title"),
  1220. }));
  1221. },
  1222. get jQuery() {
  1223. return jQuery;
  1224. },
  1225. };
  1226. }
  1227.  
  1228. config.registerFlag("site.codeforcesMobile.showEditor", true, "Show Editor in Mobile Codeforces (m[1-3].codeforces.com) Problem Page");
  1229. async function init$2() {
  1230. if (!/^m[1-3]\.codeforces\.com$/.test(location.host))
  1231. throw "not Codeforces Mobile";
  1232. const url = /\/contest\/(\d+)\/problem\/([^/]+)/.exec(location.pathname);
  1233. const contestId = url[1];
  1234. const problemId = url[2];
  1235. const doc = unsafeWindow.document;
  1236. const main = doc.querySelector("main");
  1237. doc.head.appendChild(newElement("link", {
  1238. rel: "stylesheet",
  1239. href: "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css",
  1240. }));
  1241. await loadScript("https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js");
  1242. const language = new ObservableValue("");
  1243. let submit = () => { };
  1244. let getSourceCode = () => "";
  1245. let setSourceCode = (_) => { };
  1246. // make Editor
  1247. if (config.get("site.codeforcesMobile.showEditor", true)) {
  1248. const frame = newElement("iframe", {
  1249. src: `/contest/${contestId}/submit`,
  1250. style: {
  1251. display: "none",
  1252. },
  1253. });
  1254. doc.body.appendChild(frame);
  1255. await new Promise(done => frame.onload = done);
  1256. const fdoc = frame.contentDocument;
  1257. const form = fdoc.querySelector("._SubmitPage_submitForm");
  1258. form.elements["problemIndex"].value = problemId;
  1259. form.elements["problemIndex"].readonly = true;
  1260. form.elements["programTypeId"].addEventListener("change", function () {
  1261. language.value = langMap[this.value];
  1262. });
  1263. for (const row of form.children) {
  1264. if (row.tagName != "DIV")
  1265. continue;
  1266. row.classList.add("form-group");
  1267. const control = row.querySelector("*[name]");
  1268. if (control)
  1269. control.classList.add("form-control");
  1270. }
  1271. form.parentElement.removeChild(form);
  1272. main.appendChild(form);
  1273. submit = () => form.submit();
  1274. getSourceCode = () => form.elements["source"].value;
  1275. setSourceCode = sourceCode => {
  1276. form.elements["source"].value = sourceCode;
  1277. };
  1278. }
  1279. return {
  1280. name: "Codeforces",
  1281. language,
  1282. get sourceCode() {
  1283. return getSourceCode();
  1284. },
  1285. set sourceCode(sourceCode) {
  1286. setSourceCode(sourceCode);
  1287. },
  1288. submit,
  1289. get testButtonContainer() {
  1290. return main;
  1291. },
  1292. get sideButtonContainer() {
  1293. return main;
  1294. },
  1295. get bottomMenuContainer() {
  1296. return doc.body;
  1297. },
  1298. get resultListContainer() {
  1299. return main;
  1300. },
  1301. get testCases() {
  1302. const testcases = [];
  1303. let index = 1;
  1304. for (const container of doc.querySelectorAll(".sample-test")) {
  1305. const input = container.querySelector(".input pre.content").textContent;
  1306. const output = container.querySelector(".output pre.content").textContent;
  1307. const anchor = container.querySelector(".input .title");
  1308. testcases.push({
  1309. input, output, anchor,
  1310. title: `Sample ${index++}`,
  1311. });
  1312. }
  1313. return testcases;
  1314. },
  1315. get jQuery() {
  1316. return unsafeWindow["jQuery"];
  1317. },
  1318. };
  1319. }
  1320.  
  1321. async function init$1() {
  1322. if (location.host != "greatest.deepsurf.us" && !location.href.match(/433152-atcoder-easy-test-v2/))
  1323. throw "Not about page";
  1324. const doc = unsafeWindow.document;
  1325. await loadScript("https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js");
  1326. const jQuery = unsafeWindow["jQuery"];
  1327. await loadScript("https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js", null, { jQuery, $: jQuery });
  1328. const e = newElement("div");
  1329. doc.getElementById("install-area").appendChild(newElement("button", {
  1330. type: "button",
  1331. textContent: "Open config",
  1332. onclick: () => config.open(),
  1333. }));
  1334. return {
  1335. name: "About Page",
  1336. language: new ObservableValue(""),
  1337. get sourceCode() { return ""; },
  1338. set sourceCode(sourceCode) { },
  1339. submit() { },
  1340. get testButtonContainer() { return e; },
  1341. get sideButtonContainer() { return e; },
  1342. get bottomMenuContainer() { return e; },
  1343. get resultListContainer() { return e; },
  1344. get testCases() { return []; },
  1345. get jQuery() { return jQuery; },
  1346. };
  1347. }
  1348.  
  1349. // 設定ページが開けなくなるのを避ける
  1350. const inits = [init$1()];
  1351. config.registerFlag("site.atcoder", true, "Use AtCoder Easy Test in AtCoder");
  1352. if (config.get("site.atcoder", true))
  1353. inits.push(init$5());
  1354. config.registerFlag("site.yukicoder", true, "Use AtCoder Easy Test in yukicoder");
  1355. if (config.get("site.yukicoder", true))
  1356. inits.push(init$4());
  1357. config.registerFlag("site.codeforces", true, "Use AtCoder Easy Test in Codeforces");
  1358. if (config.get("site.codeforces", true))
  1359. inits.push(init$3());
  1360. config.registerFlag("site.codeforcesMobile", true, "Use AtCoder Easy Test in Codeforces Mobile (m[1-3].codeforces.com)");
  1361. if (config.get("site.codeforcesMobile", true))
  1362. inits.push(init$2());
  1363. var pSite = Promise.any(inits);
  1364.  
  1365. const runners = {
  1366. "C GCC 10.1.0 Wandbox": new WandboxRunner("gcc-10.1.0-c", "C (GCC 10.1.0)"),
  1367. "C C17 Clang 10.0.0 paiza.io": new PaizaIORunner("c", "C (C17 / Clang 10.0.0)"),
  1368. "C++ GCC 10.1.0 + Boost 1.73.0 + ACL Wandbox": new WandboxCppRunner("gcc-10.1.0", "C++ (GCC 10.1.0) + ACL", { options: "warning,boost-1.73.0-gcc-9.2.0,gnu++17" }),
  1369. "C++ Clang 10.0.0 + ACL Wandbox": new WandboxCppRunner("clang-10.0.0", "C++ (Clang 10.0.0) + ACL", { options: "warning,boost-nothing-clang-10.0.0,c++17" }),
  1370. "Python3 CPython 3.8.2 paiza.io": new PaizaIORunner("python3", "Python (3.8.2)"),
  1371. "Python3 Brython": brythonRunner,
  1372. "Python3 Pyodide": pyodideRunner,
  1373. "Bash 5.0.17 paiza.io": new PaizaIORunner("bash", "Bash (5.0.17)"),
  1374. "C# .NET Core 6.0.100-alpha.1.20562.2 Wandbox": new WandboxRunner("csharp", "C# (.NET Core 6.0.100-alpha.1.20562.2)"),
  1375. "C# Mono-mcs HEAD Wandbox": new WandboxRunner("mono-head", "C# (Mono-mcs HEAD)"),
  1376. "Clojure 1.10.1-1 paiza.io": new PaizaIORunner("clojure", "Clojure (1.10.1-1)"),
  1377. "D LDC 1.23.0 paiza.io": new PaizaIORunner("d", "D (LDC 1.23.0)"),
  1378. "Erlang 10.6.4 paiza.io": new PaizaIORunner("erlang", "Erlang (10.6.4)"),
  1379. "Elixir 1.10.4 paiza.io": new PaizaIORunner("elixir", "Elixir (1.10.4)"),
  1380. "F# Interactive 4.0 paiza.io": new PaizaIORunner("fsharp", "F# (Interactive 4.0)"),
  1381. "Go 1.14.1 Wandbox": new WandboxRunner("go-1.14.1", "Go (1.14.1)"),
  1382. "Haskell GHC 8.4.2 Wandbox": new WandboxRunner("ghc-8.4.2", "Haskell (GHC 8.4.2)"),
  1383. "Haskell GHC 8.6.5 paiza.io": new PaizaIORunner("haskell", "Haskell (GHC 8.6.5)"),
  1384. "Java 1.8.0 jdk8u121-b13 Wandbox": new WandboxRunner("openjdk-jdk8u121-b13", "Java (1.8.0_121 jdk8u121-b13)"),
  1385. "Java 11 jdk-11+28 Wandbox": new WandboxRunner("openjdk-jdk-11+28", "Java (11)"),
  1386. "JavaScript Node.js paiza.io": new PaizaIORunner("javascript", "JavaScript (Node.js 12.18.3)"),
  1387. "Kotlin 1.4.0 paiza.io": new PaizaIORunner("kotlin", "Kotlin (1.4.0)"),
  1388. "Lua 5.3.4 Wandbox": new WandboxRunner("lua-5.3.4", "Lua (Lua 5.3.4)"),
  1389. "Lua LuaJIT HEAD Wandbox": new WandboxRunner("luajit-head", "Lua (LuaJIT HEAD)"),
  1390. "Nim 1.0.6 Wandbox": new WandboxRunner("nim-1.0.6", "Nim (1.0.6)"),
  1391. "Objective-C Clang 10.0.0 paiza.io": new PaizaIORunner("objective-c", "Objective-C (Clang 10.0.0)"),
  1392. "OCaml HEAD Wandbox": new WandboxRunner("ocaml-head", "OCaml (HEAD)"),
  1393. "Pascal FPC 3.0.2 Wandbox": new WandboxRunner("fpc-3.0.2", "Pascal (FPC 3.0.2)"),
  1394. "Perl 5.30.0 paiza.io": new PaizaIORunner("perl", "Perl (5.30.0)"),
  1395. "PHP 7.4.10 paiza.io": new PaizaIORunner("php", "PHP (7.4.10)"),
  1396. "PHP 7.3.3 Wandbox": new WandboxRunner("php-7.3.3", "PHP (7.3.3)"),
  1397. "Python PyPy HEAD Wandbox": new WandboxRunner("pypy-head", "PyPy2 (HEAD)"),
  1398. "Python3 PyPy3 7.2.0 Wandbox": new WandboxRunner("pypy-7.2.0-3", "PyPy3 (7.2.0)"),
  1399. "Ruby 2.7.1 paiza.io": new PaizaIORunner("ruby", "Ruby (2.7.1)"),
  1400. "Ruby HEAD Wandbox": new WandboxRunner("ruby-head", "Ruby (HEAD)"),
  1401. "Ruby 2.7.1 Wandbox": new WandboxRunner("ruby-2.7.1", "Ruby (2.7.1)"),
  1402. "Rust 1.42.0 AtCoder": new AtCoderRunner("4050", "Rust (1.42.0)"),
  1403. "Rust HEAD Wandbox": new WandboxRunner("rust-head", "Rust (HEAD)"),
  1404. "Rust 1.43.0 paiza.io": new PaizaIORunner("rust", "Rust (1.43.0)"),
  1405. "Scala 2.13.3 paiza": new PaizaIORunner("scala", "Scala (2.13.3)"),
  1406. "Scheme Gauche 0.9.6 paiza.io": new PaizaIORunner("scheme", "Scheme (Gauche 0.9.6)"),
  1407. "Swift 5.2.5 paiza.io": new PaizaIORunner("swift", "Swift (5.2.5)"),
  1408. "Text local": new CustomRunner("Text", async (sourceCode, input) => {
  1409. return {
  1410. status: "OK",
  1411. exitCode: "0",
  1412. input,
  1413. output: sourceCode,
  1414. };
  1415. }),
  1416. "Basic Visual Basic .NET Core 4.0.1 paiza.io": new PaizaIORunner("vb", "Visual Basic (.NET Core 4.0.1)"),
  1417. "COBOL Free OpenCOBOL 2.2.0 paiza.io": new PaizaIORunner("cobol", "COBOL - Free (OpenCOBOL 2.2.0)"),
  1418. "COBOL Fixed OpenCOBOL 1.1.0 AtCoder": new AtCoderRunner("4060", "COBOL - Fixed (OpenCOBOL 1.1.0)"),
  1419. "COBOL Free OpenCOBOL 1.1.0 AtCoder": new AtCoderRunner("4061", "COBOL - Free (OpenCOBOL 1.1.0)"),
  1420. "C++ GCC 9.2.0 + ACL Wandbox": new WandboxCppRunner("gcc-9.2.0", "C++ (GCC 9.2.0) + ACL"),
  1421. };
  1422. pSite.then(site => {
  1423. if (site.name == "AtCoder") {
  1424. // AtCoderRunner がない場合は、追加する
  1425. for (const e of document.querySelectorAll("#select-lang option[value]")) {
  1426. const m = e.textContent.match(/([^ ]+) \(([^)]+)\)/);
  1427. if (m) {
  1428. const name = `${m[1]} ${m[2]} AtCoder`;
  1429. const languageId = e.value;
  1430. runners[name] = new AtCoderRunner(languageId, e.textContent);
  1431. }
  1432. }
  1433. }
  1434. });
  1435. console.info("AtCoder Easy Test: codeRunner OK");
  1436. config.registerCount("codeRunner.maxRetry", 3, "Max count of retry when IE (Internal Error)");
  1437. var codeRunner = {
  1438. // 指定した環境でコードを実行する
  1439. async run(runnerId, sourceCode, input, expectedOutput, options = { trim: true, split: true }) {
  1440. // CodeRunner が存在しない言語ID
  1441. if (!(runnerId in runners))
  1442. return Promise.reject("Language not supported");
  1443. // 最後に実行したコードを保存
  1444. if (sourceCode.length > 0)
  1445. codeSaver.save(sourceCode);
  1446. // 実行
  1447. const maxRetry = config.get("codeRunner.maxRetry", 3);
  1448. for (let retry = 0; retry < maxRetry; retry++) {
  1449. try {
  1450. const result = await runners[runnerId].test(sourceCode, input, expectedOutput, options);
  1451. const lang = runnerId.split(" ")[0];
  1452. if (result.status == "IE") {
  1453. console.error(result);
  1454. const runnerIds = Object.keys(runners).filter(runnerId => runnerId.split(" ")[0] == lang);
  1455. const index = runnerIds.indexOf(runnerId);
  1456. runnerId = runnerIds[(index + 1) % runnerIds.length];
  1457. continue;
  1458. }
  1459. return result;
  1460. }
  1461. catch (e) {
  1462. console.error(e);
  1463. }
  1464. }
  1465. },
  1466. // 環境の名前の一覧を取得する
  1467. // @return runnerIdとラベルのペアの配列
  1468. async getEnvironment(languageId) {
  1469. const langs = similarLangs(languageId, Object.keys(runners));
  1470. if (langs.length == 0)
  1471. throw `Undefined language: ${languageId}`;
  1472. return langs.map(runnerId => [runnerId, runners[runnerId].label]);
  1473. },
  1474. };
  1475.  
  1476. 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>";
  1477.  
  1478. 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>";
  1479.  
  1480. async function init() {
  1481. const site = await pSite;
  1482. const style = html2element(hStyle$1);
  1483. const bottomMenu = html2element(hBottomMenu);
  1484. unsafeWindow.document.head.appendChild(style);
  1485. site.bottomMenuContainer.appendChild(bottomMenu);
  1486. const bottomMenuKey = bottomMenu.querySelector("#bottom-menu-key");
  1487. const bottomMenuTabs = bottomMenu.querySelector("#bottom-menu-tabs");
  1488. const bottomMenuContents = bottomMenu.querySelector("#bottom-menu-contents");
  1489. // メニューのリサイズ
  1490. {
  1491. let resizeStart = null;
  1492. const onStart = (event) => {
  1493. const target = event.target;
  1494. const pageY = event.pageY;
  1495. if (target.id != "bottom-menu-tabs")
  1496. return;
  1497. resizeStart = { y: pageY, height: bottomMenuContents.getBoundingClientRect().height };
  1498. };
  1499. const onMove = (event) => {
  1500. if (!resizeStart)
  1501. return;
  1502. event.preventDefault();
  1503. bottomMenuContents.style.height = `${resizeStart.height - (event.pageY - resizeStart.y)}px`;
  1504. };
  1505. const onEnd = () => {
  1506. resizeStart = null;
  1507. };
  1508. bottomMenuTabs.addEventListener("mousedown", onStart);
  1509. bottomMenuTabs.addEventListener("mousemove", onMove);
  1510. bottomMenuTabs.addEventListener("mouseup", onEnd);
  1511. bottomMenuTabs.addEventListener("mouseleave", onEnd);
  1512. }
  1513. let tabs = new Set();
  1514. let selectedTab = null;
  1515. /** 下メニューの操作 */
  1516. const menuController = {
  1517. /** タブを選択 */
  1518. selectTab(tabId) {
  1519. const tab = site.jQuery(`#bottom-menu-tab-${tabId}`);
  1520. if (tab && tab[0]) {
  1521. tab.tab("show"); // Bootstrap 3
  1522. selectedTab = tabId;
  1523. }
  1524. },
  1525. /** 下メニューにタブを追加する */
  1526. addTab(tabId, tabLabel, paneContent, options = {}) {
  1527. console.log(`AtCoder Easy Test: addTab: ${tabLabel} (${tabId})`, paneContent);
  1528. // タブを追加
  1529. const tab = document.createElement("a");
  1530. tab.textContent = tabLabel;
  1531. tab.id = `bottom-menu-tab-${tabId}`;
  1532. tab.href = "#";
  1533. tab.dataset.target = `#bottom-menu-pane-${tabId}`;
  1534. tab.dataset.toggle = "tab";
  1535. tab.addEventListener("click", event => {
  1536. event.preventDefault();
  1537. menuController.selectTab(tabId);
  1538. });
  1539. const tabLi = document.createElement("li");
  1540. tabLi.appendChild(tab);
  1541. bottomMenuTabs.appendChild(tabLi);
  1542. // 内容を追加
  1543. const pane = document.createElement("div");
  1544. pane.className = "tab-pane";
  1545. pane.id = `bottom-menu-pane-${tabId}`;
  1546. pane.appendChild(paneContent);
  1547. bottomMenuContents.appendChild(pane);
  1548. const controller = {
  1549. get id() {
  1550. return tabId;
  1551. },
  1552. close() {
  1553. bottomMenuTabs.removeChild(tabLi);
  1554. bottomMenuContents.removeChild(pane);
  1555. tabs.delete(tab);
  1556. if (selectedTab == tabId) {
  1557. selectedTab = null;
  1558. if (tabs.size > 0) {
  1559. menuController.selectTab(tabs.values().next().value.id);
  1560. }
  1561. }
  1562. },
  1563. show() {
  1564. menuController.show();
  1565. menuController.selectTab(tabId);
  1566. },
  1567. set color(color) {
  1568. tab.style.backgroundColor = color;
  1569. },
  1570. };
  1571. // 閉じるボタン
  1572. if (options.closeButton) {
  1573. const btn = document.createElement("a");
  1574. btn.className = "bottom-menu-btn-close btn btn-link glyphicon glyphicon-remove";
  1575. btn.addEventListener("click", () => {
  1576. controller.close();
  1577. });
  1578. tab.appendChild(btn);
  1579. }
  1580. // 選択されているタブがなければ選択
  1581. if (!selectedTab)
  1582. menuController.selectTab(tabId);
  1583. return controller;
  1584. },
  1585. /** 下メニューを表示する */
  1586. show() {
  1587. if (bottomMenuKey.classList.contains("collapsed"))
  1588. bottomMenuKey.click();
  1589. },
  1590. /** 下メニューの表示/非表示を切り替える */
  1591. toggle() {
  1592. bottomMenuKey.click();
  1593. },
  1594. };
  1595. console.info("AtCoder Easy Test: bottomMenu OK");
  1596. return menuController;
  1597. }
  1598.  
  1599. 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>";
  1600.  
  1601. class ResultRow {
  1602. _tabs;
  1603. _element;
  1604. _promise;
  1605. constructor(pairs) {
  1606. this._tabs = pairs.map(([_, tab]) => tab);
  1607. this._element = html2element(hRowTemplate);
  1608. this._element.querySelector(".close").addEventListener("click", () => this.remove());
  1609. {
  1610. const date = new Date();
  1611. const h = date.getHours().toString().padStart(2, "0");
  1612. const m = date.getMinutes().toString().padStart(2, "0");
  1613. const s = date.getSeconds().toString().padStart(2, "0");
  1614. this._element.querySelector(".atcoder-easy-test-cases-row-date").textContent = `${h}:${m}:${s}`;
  1615. }
  1616. const numCases = pairs.length;
  1617. let numFinished = 0;
  1618. let numAccepted = 0;
  1619. const progressBar = this._element.querySelector(".progress-bar");
  1620. progressBar.textContent = `${numFinished} / ${numCases}`;
  1621. this._promise = Promise.all(pairs.map(([pResult, tab]) => {
  1622. const button = html2element(`<div class="label label-default" style="margin: 3px; cursor: pointer;">WJ</div>`);
  1623. button.addEventListener("click", async () => {
  1624. (await tab).show();
  1625. });
  1626. this._element.appendChild(button);
  1627. return pResult.then(result => {
  1628. button.textContent = result.status;
  1629. if (result.status == "AC") {
  1630. button.classList.add("label-success");
  1631. }
  1632. else if (result.status != "OK") {
  1633. button.classList.add("label-warning");
  1634. }
  1635. numFinished++;
  1636. if (result.status == "AC")
  1637. numAccepted++;
  1638. progressBar.textContent = `${numFinished} / ${numCases}`;
  1639. progressBar.style.width = `${100 * numFinished / numCases}%`;
  1640. if (numFinished == numCases) {
  1641. if (numAccepted == numCases)
  1642. this._element.classList.add("alert-success");
  1643. else
  1644. this._element.classList.add("alert-warning");
  1645. }
  1646. }).catch(reason => {
  1647. button.textContent = "IE";
  1648. button.classList.add("label-danger");
  1649. console.error(reason);
  1650. });
  1651. }));
  1652. }
  1653. get element() {
  1654. return this._element;
  1655. }
  1656. onFinish(listener) {
  1657. this._promise.then(listener);
  1658. }
  1659. remove() {
  1660. for (const pTab of this._tabs)
  1661. pTab.then(tab => tab.close());
  1662. const parent = this._element.parentElement;
  1663. if (parent)
  1664. parent.removeChild(this._element);
  1665. }
  1666. }
  1667.  
  1668. var hResultList = "<div class=\"row\"></div>";
  1669.  
  1670. const eResultList = html2element(hResultList);
  1671. pSite.then(site => site.resultListContainer.appendChild(eResultList));
  1672. const resultList = {
  1673. addResult(pairs) {
  1674. const result = new ResultRow(pairs);
  1675. eResultList.insertBefore(result.element, eResultList.firstChild);
  1676. return result;
  1677. },
  1678. };
  1679.  
  1680. 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>";
  1681.  
  1682. function setClassFromData(element, name) {
  1683. const classes = element.dataset[name].split(/\s+/);
  1684. for (let className of classes) {
  1685. let flag = true;
  1686. if (className[0] == "!") {
  1687. className = className.slice(1);
  1688. flag = false;
  1689. }
  1690. element.classList.toggle(className, flag);
  1691. }
  1692. }
  1693. class ResultTabContent {
  1694. _title;
  1695. _uid;
  1696. _element;
  1697. _result;
  1698. constructor() {
  1699. this._uid = Date.now().toString(16);
  1700. this._result = null;
  1701. this._element = html2element(hTabTemplate);
  1702. this._element.id = `atcoder-easy-test-result-${this._uid}`;
  1703. }
  1704. set result(result) {
  1705. this._result = result;
  1706. if (result.status == "AC") {
  1707. this.outputStyle.backgroundColor = "#dff0d8";
  1708. }
  1709. else if (result.status != "OK") {
  1710. this.outputStyle.backgroundColor = "#fcf8e3";
  1711. }
  1712. this.input = result.input;
  1713. if ("expectedOutput" in result)
  1714. this.expectedOutput = result.expectedOutput;
  1715. this.exitCode = result.exitCode;
  1716. if ("execTime" in result)
  1717. this.execTime = `${result.execTime} ms`;
  1718. if ("memory" in result)
  1719. this.memory = `${result.memory} KB`;
  1720. if ("output" in result)
  1721. this.output = result.output;
  1722. if (result.error)
  1723. this.error = result.error;
  1724. }
  1725. get result() {
  1726. return this._result;
  1727. }
  1728. get uid() {
  1729. return this._uid;
  1730. }
  1731. get element() {
  1732. return this._element;
  1733. }
  1734. set title(title) {
  1735. this._title = title;
  1736. }
  1737. get title() {
  1738. return this._title;
  1739. }
  1740. set input(input) {
  1741. this._get("input").value = input;
  1742. }
  1743. get inputStyle() {
  1744. return this._get("input").style;
  1745. }
  1746. set expectedOutput(output) {
  1747. this._get("expected-output").value = output;
  1748. setClassFromData(this._get("col-input"), "ifExpectedOutput");
  1749. setClassFromData(this._get("col-expected-output"), "ifExpectedOutput");
  1750. }
  1751. get expectedOutputStyle() {
  1752. return this._get("expected-output").style;
  1753. }
  1754. set output(output) {
  1755. this._get("output").value = output;
  1756. }
  1757. get outputStyle() {
  1758. return this._get("output").style;
  1759. }
  1760. set error(error) {
  1761. this._get("error").value = error;
  1762. setClassFromData(this._get("col-output"), "ifError");
  1763. setClassFromData(this._get("col-error"), "ifError");
  1764. }
  1765. set exitCode(code) {
  1766. const element = this._get("exit-code");
  1767. element.textContent = code;
  1768. const isSuccess = code == "0";
  1769. element.classList.toggle("bg-success", isSuccess);
  1770. element.classList.toggle("bg-danger", !isSuccess);
  1771. }
  1772. set execTime(time) {
  1773. this._get("exec-time").textContent = time;
  1774. }
  1775. set memory(memory) {
  1776. this._get("memory").textContent = memory;
  1777. }
  1778. _get(name) {
  1779. return this._element.querySelector(`.atcoder-easy-test-result-${name}`);
  1780. }
  1781. }
  1782.  
  1783. 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\"></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>";
  1784.  
  1785. var hStyle = "<style>\n.atcoder-easy-test-result textarea {\n font-family: monospace;\n font-weight: normal;\n}\n</style>";
  1786.  
  1787. var hRunButton = "<a class=\"btn btn-primary btn-sm\" style=\"vertical-align: top; margin-left: 0.5em\">Run</a>";
  1788.  
  1789. var hTestAndSubmit = "<a 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</a>";
  1790.  
  1791. var hTestAllSamples = "<a 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</a>";
  1792.  
  1793. (async () => {
  1794. const site = await pSite;
  1795. const doc = unsafeWindow.document;
  1796. // init bottomMenu
  1797. const pBottomMenu = init();
  1798. pBottomMenu.then(bottomMenu => {
  1799. unsafeWindow.bottomMenu = bottomMenu;
  1800. });
  1801. await doneOrFail(pBottomMenu);
  1802. // external interfaces
  1803. unsafeWindow.codeRunner = codeRunner;
  1804. doc.head.appendChild(html2element(hStyle));
  1805. // interface
  1806. const atCoderEasyTest = {
  1807. version: "2.9.6",
  1808. site,
  1809. config,
  1810. codeSaver,
  1811. enableButtons() {
  1812. events.trig("enable");
  1813. },
  1814. disableButtons() {
  1815. events.trig("disable");
  1816. },
  1817. runCount: 0,
  1818. runTest(title, language, sourceCode, input, output = null, options = { trim: true, split: true, }) {
  1819. this.disableButtons();
  1820. const content = new ResultTabContent();
  1821. const pTab = pBottomMenu.then(bottomMenu => bottomMenu.addTab("easy-test-result-" + content.uid, `#${++this.runCount} ${title}`, content.element, { active: true, closeButton: true }));
  1822. const pResult = codeRunner.run(language, sourceCode, input, output, options);
  1823. pResult.then(result => {
  1824. content.result = result;
  1825. if (result.status == "AC") {
  1826. pTab.then(tab => tab.color = "#dff0d8");
  1827. }
  1828. else if (result.status != "OK") {
  1829. pTab.then(tab => tab.color = "#fcf8e3");
  1830. }
  1831. }).finally(() => {
  1832. this.enableButtons();
  1833. });
  1834. return [pResult, pTab];
  1835. }
  1836. };
  1837. unsafeWindow.atCoderEasyTest = atCoderEasyTest;
  1838. // place "Easy Test" tab
  1839. {
  1840. // declare const hRoot: string;
  1841. const root = html2element(hRoot);
  1842. const E = (id) => root.querySelector(`#atcoder-easy-test-${id}`);
  1843. const eLanguage = E("language");
  1844. const eInput = E("input");
  1845. const eAllowableErrorCheck = E("allowable-error-check");
  1846. const eAllowableError = E("allowable-error");
  1847. const eOutput = E("output");
  1848. const eRun = E("run");
  1849. const eSetting = E("setting");
  1850. const eVersion = E("version");
  1851. eVersion.textContent = atCoderEasyTest.version;
  1852. events.on("enable", () => {
  1853. eRun.classList.remove("disabled");
  1854. });
  1855. events.on("disable", () => {
  1856. eRun.classList.add("disabled");
  1857. });
  1858. eSetting.addEventListener("click", () => {
  1859. config.open();
  1860. });
  1861. // バージョン確認
  1862. {
  1863. const aDay = 24 * 60 * 60 * 1e3;
  1864. config.registerCount("version.checkInterval", aDay, "Interval [ms] of checking for new version");
  1865. let isButtonShown = false;
  1866. const interval = config.get("version.checkInterval", aDay);
  1867. const showButton = (version) => {
  1868. if (isButtonShown)
  1869. return;
  1870. isButtonShown = true;
  1871. console.info(`AtCoder Easy Test: New version available: v${version}`);
  1872. eVersion.insertAdjacentElement("afterend", newElement("a", {
  1873. href: "https://github.com/magurofly/atcoder-easy-test/raw/main/v2/atcoder-easy-test.user.js",
  1874. target: "_blank",
  1875. className: "btn btn-xs btn-info",
  1876. textContent: `Update to v${version}`,
  1877. }));
  1878. };
  1879. const checkVersion = () => {
  1880. const latest = config.get("version.latest", atCoderEasyTest.version);
  1881. if (compareVersion(atCoderEasyTest.version, latest) < 0) {
  1882. showButton(latest);
  1883. }
  1884. else {
  1885. const lastCheck = config.get("version.lastCheck", 0);
  1886. const now = Date.now();
  1887. if (now - lastCheck >= interval) {
  1888. config.set("version.lastCheck", now);
  1889. fetch("https://raw.githubusercontent.com/magurofly/atcoder-easy-test/main/v2/package.json").
  1890. then(r => r.json()).
  1891. then((data) => new Promise((resolve, reject) => {
  1892. const x = compareVersion(atCoderEasyTest.version, data.version);
  1893. if (x >= 0)
  1894. resolve("OK");
  1895. else {
  1896. reject(data.version);
  1897. }
  1898. })).
  1899. catch(showButton);
  1900. }
  1901. }
  1902. };
  1903. setInterval(checkVersion, interval);
  1904. checkVersion();
  1905. }
  1906. // 言語選択関係
  1907. {
  1908. eLanguage.addEventListener("change", async () => {
  1909. const langSelection = config.get("langSelection", {});
  1910. langSelection[site.language.value] = eLanguage.value;
  1911. config.set("langSelection", langSelection);
  1912. });
  1913. async function setLanguage() {
  1914. const languageId = site.language.value;
  1915. while (eLanguage.firstChild)
  1916. eLanguage.removeChild(eLanguage.firstChild);
  1917. try {
  1918. const langs = await codeRunner.getEnvironment(languageId);
  1919. console.log(`language: ${langs[1]} (${langs[0]})`);
  1920. // add <option>
  1921. for (const [languageId, label] of langs) {
  1922. const option = document.createElement("option");
  1923. option.value = languageId;
  1924. option.textContent = label;
  1925. eLanguage.appendChild(option);
  1926. }
  1927. // load
  1928. const langSelection = config.get("langSelection", {});
  1929. if (languageId in langSelection) {
  1930. const prev = langSelection[languageId];
  1931. const [lang, _] = langs.find(([lang, label]) => lang == prev);
  1932. if (lang)
  1933. eLanguage.value = lang;
  1934. }
  1935. events.trig("enable");
  1936. }
  1937. catch (error) {
  1938. console.log(`language: ? (${languageId})`);
  1939. console.error(error);
  1940. const option = document.createElement("option");
  1941. option.className = "fg-danger";
  1942. option.textContent = error;
  1943. eLanguage.appendChild(option);
  1944. events.trig("disable");
  1945. }
  1946. }
  1947. site.language.addListener(() => setLanguage());
  1948. eAllowableError.disabled = !eAllowableErrorCheck.checked;
  1949. eAllowableErrorCheck.addEventListener("change", event => {
  1950. eAllowableError.disabled = !eAllowableErrorCheck.checked;
  1951. });
  1952. }
  1953. // テスト実行
  1954. function runTest(title, input, output = null) {
  1955. const options = { trim: true, split: true, };
  1956. if (eAllowableErrorCheck.checked) {
  1957. options.allowableError = parseFloat(eAllowableError.value);
  1958. }
  1959. return atCoderEasyTest.runTest(title, eLanguage.value, site.sourceCode, input, output, options);
  1960. }
  1961. function runAllCases(testcases) {
  1962. const pairs = testcases.map(testcase => runTest(testcase.title, testcase.input, testcase.output));
  1963. resultList.addResult(pairs);
  1964. return Promise.all(pairs.map(([pResult, _]) => pResult.then(result => {
  1965. if (result.status == "AC")
  1966. return Promise.resolve(result);
  1967. else
  1968. return Promise.reject(result);
  1969. })));
  1970. }
  1971. eRun.addEventListener("click", _ => {
  1972. const title = "Run";
  1973. const input = eInput.value;
  1974. const output = eOutput.value;
  1975. runTest(title, input, output || null);
  1976. });
  1977. await doneOrFail(pBottomMenu.then(bottomMenu => bottomMenu.addTab("easy-test", "Easy Test", root)));
  1978. // place "Run" button on each sample
  1979. for (const testCase of site.testCases) {
  1980. const eRunButton = html2element(hRunButton);
  1981. eRunButton.addEventListener("click", async () => {
  1982. const [pResult, pTab] = runTest(testCase.title, testCase.input, testCase.output);
  1983. await pResult;
  1984. (await pTab).show();
  1985. });
  1986. testCase.anchor.insertAdjacentElement("afterend", eRunButton);
  1987. events.on("disable", () => {
  1988. eRunButton.classList.add("disabled");
  1989. });
  1990. events.on("enable", () => {
  1991. eRunButton.classList.remove("disabled");
  1992. });
  1993. }
  1994. // place "Test & Submit" button
  1995. {
  1996. const button = html2element(hTestAndSubmit);
  1997. site.testButtonContainer.appendChild(button);
  1998. const testAndSubmit = async () => {
  1999. await runAllCases(site.testCases);
  2000. site.submit();
  2001. };
  2002. button.addEventListener("click", testAndSubmit);
  2003. events.on("testAndSubmit", testAndSubmit);
  2004. events.on("disable", () => button.classList.add("disabled"));
  2005. events.on("enable", () => button.classList.remove("disabled"));
  2006. }
  2007. // place "Test All Samples" button
  2008. {
  2009. const button = html2element(hTestAllSamples);
  2010. site.testButtonContainer.appendChild(button);
  2011. const testAllSamples = () => runAllCases(site.testCases);
  2012. button.addEventListener("click", testAllSamples);
  2013. events.on("testAllSamples", testAllSamples);
  2014. events.on("disable", () => button.classList.add("disabled"));
  2015. events.on("enable", () => button.classList.remove("disabled"));
  2016. }
  2017. }
  2018. // place "Restore Last Play" button
  2019. try {
  2020. const restoreButton = doc.createElement("a");
  2021. restoreButton.className = "btn btn-danger btn-sm";
  2022. restoreButton.textContent = "Restore Last Play";
  2023. restoreButton.addEventListener("click", async () => {
  2024. try {
  2025. const lastCode = await codeSaver.restore();
  2026. if (site.sourceCode.length == 0 || confirm("Your current code will be replaced. Are you sure?")) {
  2027. site.sourceCode = lastCode;
  2028. }
  2029. }
  2030. catch (reason) {
  2031. alert(reason);
  2032. }
  2033. });
  2034. site.sideButtonContainer.appendChild(restoreButton);
  2035. }
  2036. catch (e) {
  2037. console.error(e);
  2038. }
  2039. // キーボードショートカット
  2040. config.registerFlag("ui.useKeyboardShortcut", true, "Use Keyboard Shortcuts");
  2041. unsafeWindow.addEventListener("keydown", (event) => {
  2042. if (config.get("ui.useKeyboardShortcut", true)) {
  2043. if (event.key == "Enter" && event.ctrlKey) {
  2044. events.trig("testAndSubmit");
  2045. }
  2046. else if (event.key == "Enter" && event.altKey) {
  2047. events.trig("testAllSamples");
  2048. }
  2049. else if (event.key == "Escape" && event.altKey) {
  2050. pBottomMenu.then(bottomMenu => bottomMenu.toggle());
  2051. }
  2052. }
  2053. });
  2054. })();
  2055. })();