Editorials Dropdown for AtCoder

Add a drop-down list next to the editorial buttons on AtCoder problem pages.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey, Greasemonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्क्रिप्ट व्यवस्थापक एक्स्टेंशन इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्क्रिप्ट व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्टाईल व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

// ==UserScript==
// @name             Editorials Dropdown for AtCoder
// @name:ja          Editorials Dropdown for AtCoder
// @namespace        https://github.com/roumcha/browser-extensions/tree/main/src/editorials-dropdown-for-atcoder
// @version          1.1.0
// @description      Add a drop-down list next to the editorial buttons on AtCoder problem pages.
// @description:ja   AtCoder の解説ボタンの横にドロップダウンリストを追加します。
// @author           Roumcha
// @license          Creative Commons Zero v1.0 Universal
// @match            https://atcoder.jp/contests/*/tasks/*
// @grant            GM.xmlHttpRequest
// @connect          atcoder.jp
// @run-at           document-end
// ==/UserScript==

"use strict";
(() => {
  // src/editorials-dropdown-for-atcoder/editorials-dropdown.ts
  async function editorialsDropdown({
    fetchXMLDocument
  }) {
    const started = Date.now();
    let lang = getLanguage();
    const link = findEditorialsButton(document);
    if (link) {
      console.log("[EDFA] Found the target button: ", link);
    } else {
      console.log(`[EDFA] Editorials button not found.`);
      return;
    }
    const url2 = new URL(link.href);
    url2.searchParams.set("editorialLang", lang);
    const editorialsPageDoc = await fetchXMLDocument(
      url2
    ).catch((reason) => {
      console.error(`[EDFA] Failed to fetch ${link.href}: ${reason}`);
      return null;
    });
    if (editorialsPageDoc) {
      console.log(`[EDFA] Downloaded ${link.href}.`);
    } else {
      console.error(`[EDFA] ${link.href} is empty or not an XML document.`);
      return;
    }
    const content = createDropdownContent(editorialsPageDoc);
    if (content.length === 0) {
      console.error(`[EDFA] failed to generate the dropdown content.`);
      return;
    }
    const insertedElem = createDropdownAndButton(...content);
    link.after(insertedElem);
    console.log(
      `[EDFA] Successfully generated and inserted a drop-down list: `,
      insertedElem
    );
    console.log(`[EDFA] done in ${Date.now() - started} ms.`);
  }
  var translation = {
    editorial: {
      ja: "\u89E3\u8AAC",
      en: "editorial"
    },
    overallEditorial: {
      ja: "\u30B3\u30F3\u30C6\u30B9\u30C8\u5168\u4F53\u306E\u89E3\u8AAC",
      en: "overall editorial"
    }
  };
  function getLanguage() {
    const param = new URLSearchParams(location.search).get(
      "lang"
    );
    if (param) {
      console.log(`[EDFA] Found language '${param}' in the URL parameter.`);
      return param;
    }
    const cookie = document.cookie.split("; ").find((s) => s.startsWith("language="))?.split("=").at(1);
    if (cookie) {
      console.log(`[EDFA] Found language '${cookie}' in Cookie.`);
      return cookie;
    }
    const browser = navigator.language;
    if (browser == "ja") {
      console.log(`[EDFA] Loaded language '${browser}' from the browser.`);
      return "ja";
    }
    console.log(`[EDFA] Fall back to English.`);
    return "en";
  }
  function findEditorialsButton(root) {
    const res = [...root.querySelectorAll("a.btn")].filter(
      ({ textContent }) => textContent && Object.values(translation["editorial"]).includes(
        textContent.toLowerCase()
      )
    ).at(0);
    return res;
  }
  function createDropdownContent(editorialsPageDoc) {
    const res = [
      ...editorialsPageDoc.querySelectorAll(
        "#main-container > div > div:not(#contest-nav-tabs) > *"
      )
    ].filter(
      ({ tagName }) => ["ul", "h3", "p"].includes(tagName.toLowerCase())
    );
    if (res.length === 0) {
      console.error(`[EDFA] failed to find editorial lists.`);
    }
    return res;
  }
  function createDropdownAndButton(...content) {
    const res = document.createElement("span");
    res.className = "edfa-root";
    res.style.position = "relative";
    res.addEventListener("blur", () => res.classList.remove("open"));
    {
      const button = document.createElement("button");
      button.className = "edfa-button btn btn-default btn-sm";
      button.type = "button";
      button.title = "open editorials list";
      button.onclick = () => res.classList.toggle("open");
      res.append(button);
      {
        const caret = document.createElement("span");
        caret.classList.add("caret");
        button.append(caret);
      }
    }
    {
      const dropdown = document.createElement("div");
      dropdown.className = "edfa-dropdown dropdown-menu";
      dropdown.style.position = "absolute";
      dropdown.style.width = "200px";
      dropdown.style.padding = "8px";
      dropdown.style.zIndex = "998";
      dropdown.append(...content);
      res.append(dropdown);
    }
    return res;
  }

  // src/editorials-dropdown-for-atcoder/info.ts
  var title = "Editorials Dropdown for AtCoder";
  var version = "1.1.0";
  var url = "https://github.com/roumcha/browser-extensions/tree/main/src/editorials-dropdown-for-atcoder";
  var author = "Roumcha";
  var userScriptHeader = `// ==UserScript==
// @name             ${title}
// @name:ja          ${title}
// @namespace        ${url}
// @version          ${version}
// @description      Add a drop-down list next to the editorial buttons on AtCoder problem pages.
// @description:ja   AtCoder \u306E\u89E3\u8AAC\u30DC\u30BF\u30F3\u306E\u6A2A\u306B\u30C9\u30ED\u30C3\u30D7\u30C0\u30A6\u30F3\u30EA\u30B9\u30C8\u3092\u8FFD\u52A0\u3057\u307E\u3059\u3002
// @author           ${author}
// @license          Creative Commons Zero v1.0 Universal
// @match            https://atcoder.jp/contests/*/tasks/*
// @grant            GM.xmlHttpRequest
// @connect          atcoder.jp
// @run-at           document-end
// ==/UserScript==
`;

  // src/editorials-dropdown-for-atcoder/user-script.ts
  (async function() {
    console.log(`[EDFA] ${title} v${version} (UserScript) started.`);
    await editorialsDropdown({
      fetchXMLDocument: async (url2) => await GM.xmlHttpRequest({ url: url2 }).then((res) => res.responseXML)
    });
  })();
})();