AtCoder Template

言語ごとにテンプレートを登録して、貼り付けの手間を減らします

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         AtCoder Template
// @namespace    http://atcoder.jp/
// @version      0.4
// @description  言語ごとにテンプレートを登録して、貼り付けの手間を減らします
// @author       magurofly
// @match        https://atcoder.jp/contests/*/tasks/*
// @match        https://atcoder.jp/contests/*/submit*
// @match        https://atcoder.jp/contests/*/custom_test*
// @grant        unsafeWindow
// @grant        GM_getValue
// @grant        GM_setValue
// @icon         https://www.google.com/s2/favicons?domain=atcoder.jp
// @license      CC0 1.0 Universal
// ==/UserScript==

(function() {
    'use strict';

    const VERSION = "0.4";

    const $ = unsafeWindow.$;

    // 設定があればロード
    const config = (() => {
        let data = {};

        {
            const config_json = GM_getValue("config", "{}");
            if (config_json) {
                try {
                    data = JSON.parse(config_json);
                } catch (e) {
                    console.error("AtCoder Template: Invalid JSON", config_json);
                }
            }
        }

        return {
            save() {
                const config_json = JSON.stringify(data);
                GM_setValue("config", config_json);
                console.info("AtCoder Template: saved config");
            },

            delete(key) {
                delete data[key];
                this.save();
            },

            get(key, defaultValue = null) {
                return (key in data) ? data[key] : defaultValue;
            },

            set(key, value) {
                data[key] = value;
                this.save();
            },
        };
    })();

    class View {
        constructor() {
            this.lang = $("#select-lang select[name='data.LanguageId']");

            const btnEditTemplate = $(`<button type="button" class="btn btn-default btn-sm">Edit Templates</button>`);
            const btnLoadTemplate = $(`<button type="button" class="btn btn-danger btn-sm">Load Template</button>`);
            $(".editor-buttons").append(btnEditTemplate, btnLoadTemplate);

            btnEditTemplate.click(() => {
                const langId = this.lang.val();
                const langLabel = this.lang.find(":selected").text();
                this.openTemplateEditor(langId, langLabel);
            });
            btnLoadTemplate.click(() => {
                if (this.getText()) {
                    if (!confirm("テンプレートを読み込むと、現在のソースは上書きされます。読み込みますか?")) return;
                }
                const langId = this.lang.val();
                if (!this.loadTemplate()) {
                    alert(`言語ID ${langId} のテンプレートは登録されていません`);
                }
            });

            this.modal = $(`<section class="modal fade">`).html(`
<div class="modal-dialog">
    <div class="modal-content">
        <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal"><span>&times;</span></button>
            <h4 class="modal-title">テンプレートの編集</h4>
        </div>
        <div class="modal-body">
            <div class="row">
                <p class="col"><span id="atcoder-template-desc"></span>テンプレートを編集します</p>
            </div>
            <div class="row">
                <textarea rows="10" id="atcoder-template-editor" class="col" style="margin: 1%; width: 98%; height: 100%; font-family: monospace"></textarea>
            </div>
            <div class="row"><div class="col">
                <button type="button" id="atcoder-template-save" class="btn btn-primary">保存</button>
                <button type="button" id="atcoder-template-delete" class="btn btn-danger">削除</button>
            </div></div>
        </div>
    </div>
</div>
            `);
            this.modal.appendTo(unsafeWindow.document.body);

            // 言語が変更されるたびに loadTemplate する
            // ただし、提出欄が空でない場合はしない
            this.lang.on("change", _e => {
                if (!this.getText()) this.loadTemplate();
            });
            this.loadTemplate();
        }

        // `languageId` で指定した言語 ID に対するテンプレートを提出欄に貼り付ける
        // 返り値: テンプレートが登録されているかどうか
        loadTemplate(languageId = null) {
            languageId = languageId || this.lang.val();
            const template = config.get(languageId);
            if (template != null) {
                this.setText(template);
                return true;
            } else {
                return false;
            }
        }

        setText(text) {
            const toggler = $('.btn-toggle-editor');
            if (!toggler.hasClass('active')) {
                unsafeWindow.ace.edit('editor').setValue(text);
            } else {
                $('.plain-textarea').val(text);
            }
        }

        getText() {
            const toggler = $('.btn-toggle-editor');
            if (!toggler.hasClass('active')) {
                return unsafeWindow.ace.edit('editor').getValue();
            } else {
                return $('.plain-textarea').val();
            }
        }

        openTemplateEditor(languageId, label = languageId) {
            const editor = this.modal.find("#atcoder-template-editor");
            this.modal.find("#atcoder-template-desc").text(`${label} [${languageId}] の`);
            editor.val(config.get(languageId, ""));
            this.modal.find("#atcoder-template-save")[0].onclick = () => {
                console.log("saved", editor.val());
                config.set(languageId, editor.val());
                config.save();
            };
            this.modal.find("#atcoder-template-delete")[0].onclick = () => {
                config.delete(languageId);
                editor.val("");
            };
            this.modal.modal();
        }
    }

    unsafeWindow.AtCoderTemplate = {
        version: VERSION,
        view: new View(),
    };
})();