AtCoder Template

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

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==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(),
    };
})();