AtCoder Template

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

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

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