您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
ツイートボタンの埋め込みテキストに情報を追加します
当前为
- // ==UserScript==
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
- // @name atcoder-hashtag-setter2
- // @namespace https://github.com/hotarunx
- // @version 2.0.0
- // @description ツイートボタンの埋め込みテキストに情報を追加します
- // @author hotarunx
- // @license MIT
- // @supportURL https://github.com/hotarunx/atcoder-hashtag-setter2/issues
- // @match https://atcoder.jp/contests/*
- // @exclude https://atcoder.jp/contests/
- // @grant none
- // ==/UserScript==
- // AtCoderの問題ページをパースする
- /**
- * URLをパースする パラメータを消す \
- * 例: in: https://atcoder.jp/contests/abc210?lang=en \
- * 例: out: (5)['https:', '', 'atcoder.jp', 'contests', 'abc210']
- */
- const parseURL = (url) => {
- // 区切り文字`/`で分割する
- // ?以降の文字列を削除してパラメータを削除する
- return url.split("/").map((x) => x.replace(/\?.*/i, ""));
- };
- const URL = parseURL(window.location.href);
- //
- /**
- * 表セル要素から、前の要素のテキストが引数と一致する要素を探す
- * 個別の提出ページで使うことを想定
- * 例: searchSubmissionInfo(["問題", "Task"])
- */
- const searchSubmissionInfo = (key) => {
- const tdTags = document.getElementsByTagName("td");
- const tdTagsArray = Array.prototype.slice.call(tdTags);
- return tdTagsArray.filter((elem) => {
- const prevElem = elem.previousElementSibling;
- const text = prevElem?.textContent;
- if (typeof text === "string")
- return key.includes(text);
- return false;
- })[0];
- };
- /** コンテストタイトル 例: AtCoder Beginner Contest 210 */
- const contestTitle = document.getElementsByClassName("contest-title")[0]?.textContent ?? "";
- /** コンテストID 例: abc210 */
- const contestID = URL[4] ?? "";
- /**
- * ページ種類 \
- * 基本的にコンテストIDの次のパス
- * ### 例外
- * 問題: task
- * 個別の提出: submission
- */
- const pageType = (() => {
- if (URL.length < 6)
- return "";
- if (URL.length >= 7 && URL[5] === "submissions" && URL[6] !== "me")
- return "submission";
- if (URL.length >= 7 && URL[5] === "tasks")
- return "task";
- return URL[5] ?? "";
- })();
- /** 問題ID 例: abc210_a */
- const taskID = (() => {
- if (pageType === "task") {
- // 問題ページでは、URLから問題IDを取り出す
- return URL[6] ?? "";
- }
- if (pageType === "submission") {
- // 個別の提出ページでは、問題リンクのURLから問題IDを取り出す
- // 提出情報の問題のURLを取得する
- const taskCell = searchSubmissionInfo(["問題", "Task"]);
- if (!taskCell)
- return "";
- const taskLink = taskCell.getElementsByTagName("a")[0];
- if (!taskLink)
- return "";
- const taskUrl = parseURL(taskLink.href);
- const taskIDParsed = taskUrl[6] ?? "";
- return taskIDParsed;
- }
- return "";
- })();
- /** 問題名 例: A - Cabbages */
- const taskTitle = (() => {
- if (pageType === "task") {
- // 問題ページでは、h2から問題名を取り出す
- return (document
- .getElementsByClassName("h2")[0]
- ?.textContent?.trim()
- .replace(/\n.*/i, "") ?? "");
- }
- if (pageType === "submission") {
- // 個別の提出ページでは、問題リンクのテキストから問題名を取り出す
- // 提出情報の問題のテキストを取得する
- const taskCell = searchSubmissionInfo(["問題", "Task"]);
- if (!taskCell)
- return "";
- const taskLink = taskCell.getElementsByTagName("a")[0];
- if (!taskLink)
- return "";
- return taskLink.textContent ?? "";
- }
- return "";
- })();
- /** 提出ユーザー 例: machikane */
- const submissionsUser = (() => {
- if (pageType !== "submission")
- return "";
- // 個別の提出ページのとき
- const userCell = searchSubmissionInfo(["ユーザ", "User"]);
- if (!userCell)
- return "";
- return userCell?.textContent?.trim() ?? "";
- })();
- /** 提出結果 例: AC */
- const judgeStatus = (() => {
- if (pageType !== "submission")
- return "";
- // 個別の提出ページのとき
- const statusCell = searchSubmissionInfo(["結果", "Status"]);
- if (!statusCell)
- return "";
- return statusCell?.textContent?.trim() ?? "";
- })();
- /** 得点 例: 100 */
- const judgeScore = (() => {
- if (pageType !== "submission")
- return 0;
- // 個別の提出ページのとき
- const scoreCell = searchSubmissionInfo(["得点", "Score"]);
- if (!scoreCell)
- return 0;
- return parseInt(scoreCell?.textContent?.trim() ?? "0", 10);
- })();
- /** 常設コンテストID一覧 */
- const permanentContestIDs = [
- "practice",
- "APG4b",
- "abs",
- "practice2",
- "typical90",
- "math-and-algorithm",
- "tessoku-book",
- ];
- /**
- * 次を判定
- * * コンテストが終了している
- * * コンテストが常設コンテスト
- */
- var isContestOver = () => {
- if (permanentContestIDs.includes(contestID))
- return true;
- return Date.now() > endTime.valueOf();
- };
- var html = "<a target=\"_blank\" href=\"\" rel=\"nofollow noopener\">\n <span class=\"a2a_svg a2a_s__default a2a_s_twitter\" style=\"background-color: rgb(230, 18, 114);\n width: 20px;\n line-height: 20px;\n height: 20px;\n background-size: 20px;\n border-radius: 3px;\n --darkreader-inline-bgcolor: #0c72b7;\" data-darkreader-inline-bgcolor=\"\">\n <svg focusable=\"false\" aria-hidden=\"true\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\">\n <path fill=\"#FFF\" d=\"M28 8.557a9.913 9.913 0 0 1-2.828.775 4.93 4.93 0 0 0 2.166-2.725 9.738 9.738 0 0 1-3.13 1.194 4.92 4.92 0 0 0-3.593-1.55 4.924 4.924 0 0 0-4.794 6.049c-4.09-.21-7.72-2.17-10.15-5.15a4.942 4.942 0 0 0-.665 2.477c0 1.71.87 3.214 2.19 4.1a4.968 4.968 0 0 1-2.23-.616v.06c0 2.39 1.7 4.38 3.952 4.83-.414.115-.85.174-1.297.174-.318 0-.626-.03-.928-.086a4.935 4.935 0 0 0 4.6 3.42 9.893 9.893 0 0 1-6.114 2.107c-.398 0-.79-.023-1.175-.068a13.953 13.953 0 0 0 7.55 2.213c9.056 0 14.01-7.507 14.01-14.013 0-.213-.005-.426-.015-.637.96-.695 1.795-1.56 2.455-2.55z\" data-darkreader-inline-fill=\"\" style=\"--darkreader-inline-fill: #e8e6e3\"></path>\n </svg> </span><span class=\"a2a_label\">Twitter2</span>\n</a>\n";
- /** ツイートボタンのHTML要素 */
- const a2akit = document.getElementsByClassName("a2a_kit")[0];
- /**
- * ツイートボタンのテキストを取得する
- */
- const getTweetButtonText = () => {
- if (!a2akit)
- return "";
- return a2akit.getAttribute("data-a2a-title");
- };
- /**
- * ツイートボタンのテキストを変更する
- */
- const setTweetButtonText = (text) => {
- if (!a2akit)
- return;
- a2akit.setAttribute("data-a2a-title", text);
- };
- const addTweetSearchButton = (text) => {
- const searchURL = `https://twitter.com/search?q=${encodeURIComponent(text)}`;
- const parser = new DOMParser();
- const doc = parser.parseFromString(html, "text/html");
- const docA = doc.getElementsByTagName("a")[0];
- docA.href = searchURL;
- a2akit.insertAdjacentElement("beforeend", docA);
- };
- (() => {
- // ネタバレ防止のため、コンテスト終了前(常設コンテストを除く)は無効とする
- if (!isContestOver())
- return;
- /** コンテストハッシュタグ 例: #AtCoder_abc210_a */
- const contestHashtag = contestID ? ` #AtCoder_${contestID}` : "";
- /** 問題ハッシュタグ 例: #AtCoder_abc210_a */
- const taskHashtag = taskID ? ` #AtCoder_${taskID}` : "";
- /** ユーザーハッシュタグ 例: #AtCoder_machikane */
- const userHashtag = userScreenName ? ` #AtCoder_${userScreenName}` : "";
- // ツイートボタンのテキストを取得する
- const textOrg = getTweetButtonText();
- if (!textOrg)
- return;
- // ツイートボタンのテキストを編集
- let text = textOrg + contestHashtag;
- if (pageType === "task") {
- // 問題ページ
- text = `${taskTitle} - ${contestTitle}${taskHashtag}${contestHashtag}${userHashtag}`;
- }
- else if (pageType === "submission") {
- // 個別の提出ページ
- text = `${taskTitle} - ${submissionsUser}の提出 - 結果 ${judgeStatus} ${judgeScore}点 - ${contestTitle}${taskHashtag}${contestHashtag}${userHashtag}`;
- }
- setTweetButtonText(text);
- // タグ検索ボタンを追加
- /** 検索タグ 問題ハッシュタグ なければコンテストハッシュタグ */
- const searchTag = taskHashtag || contestHashtag;
- addTweetSearchButton(searchTag.trim());
- })();