17lands Draft Log Comments

Tool for making comments to a 17lands draft log.

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

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

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         17lands Draft Log Comments
// @namespace    http://tampermonkey.net/
// @version      2025-12-02
// @description  Tool for making comments to a 17lands draft log.
// @author       [email protected] (@iluvatar777)
// @match        https://www.17lands.com/draft/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=17lands.com
// @grant        GM_setClipboard
// @license      MIT
// ==/UserScript==

function init() {
    const userid = localStorage.getItem(getStorageKey('userid', true));

    // add html to dom
    const el=document.getElementById('app')?.children[1]?.children[0];
    const mainDiv = document.createElement('div');
    mainDiv.id = 'comment-main';
    mainDiv.innerHTML = `
     <div id='comment-text-area' style='position: absolute; bottom: 5px; left: 5px; z-index: 100; width: calc(100% - 23px); padding: 10px; margin-left: 5px; background: rgba(0, 0, 0, .75); border: 1px solid #CCCCCC; border-radius:5px;'>
           <div id='comment-controls' style='margin: 3px 0 7px 0; min-width: 600px; background: rgba(0, 0, 0, .5)'>
               <span>Add comments to draft log</span>
               <button id='comment-button-export' title='export comments of current user'>export</button>
               <button id='comment-button-import' title='import other user comments from clipboard'>import</button>
               <span id='comment-id-span' style='display: inline-block;' title='changing will clear existing comments'>
                   user id: <span id='comment-user-id' style='color:#CCCCFF; text-decoration: underline; cursor: pointer'>${userid}</span>
                   <input id='comment-user-id-input' style='display:none'></input>
               </span>
               <button id='comment-button-clear' style='margin-left: 20px;'>clear comment</button>
           </div>
           <div id='comment-other-comments' style='width: auto; margin-right: 20px'></div>
           <textarea id='comment-textarea' style='width: stretch; margin-right: 20px'></textarea>
       </div>
    `;
    el.appendChild(mainDiv);

    if (userid === null) {
        toggleLogin();
    }

    //add listenrs
    window.navigation.addEventListener('navigate', handleNav);
    window.addEventListener("beforeunload", function(e){
        localStorage.removeItem(getStorageKey('pick'))
    });
    window.addEventListener('load', () => {
        // initial load of comments
        const intv = setInterval(() => {
            const url = window.location.href.split('/');
            if (url.length === 7) {
                handleNav({'destination':{'url': url.join('/')}});
                clearInterval(intv);
            }
        }, 50);
    });

    const clearButton = document.getElementById('comment-button-clear')
    clearButton.addEventListener("click", clearCurrentComment, false);
    const exportButton = document.getElementById('comment-button-export')
    exportButton.addEventListener("click", exportComments, false);
    const importButton = document.getElementById('comment-button-import')
    importButton.addEventListener("click", importCommentsFromClipboard, false);
    const userIdDisplay = document.getElementById('comment-user-id')
    userIdDisplay.addEventListener("click", toggleLogin, false);
    const userIdInput = document.getElementById('comment-user-id-input')
    userIdInput.addEventListener("blur", userIdUpdate, false);
    const testArea = document.getElementById('comment-textarea')
    testArea.addEventListener("focus", highlightLogin, false);
}

function handleNav(event) {
    const url = event.destination.url.split('/');
    if (url.length !== 7) {
        //pick not in URL, ignore
        return;
    }
    const userid = localStorage.getItem(getStorageKey('userid', true));
    const oldPick = localStorage.getItem(getStorageKey('pick'));
    const newPick = 'p' + url[5] + 'p' + url[6];
    const draftid = url[4];
    localStorage.setItem(getStorageKey('pick'), newPick);

    if ((oldPick === newPick)) return;

    const allComments = loadCommentsFromStorage(draftid) || {};
    const newPickComments = allComments[newPick] || {};
    const oldPickText = document.getElementById('comment-textarea').value;
    document.getElementById('comment-textarea').value = JSON.stringify((newPickComments[userid]) || '').slice(1, -1).replaceAll('\\\\','\\').replaceAll('\\n','\n');

    displayOtherComments(newPick);
    if (oldPick) {
        storeText(draftid, oldPick, userid, oldPickText);
    }
}

function displayOtherComments(pick = '') {
    const url = window.location.href.split('/');
    pick = pick || 'p' + url[5] + 'p' + url[6];
    const draftid = url[4];
    const allComments = loadCommentsFromStorage(draftid) || {};
    const pickComments = allComments[pick];
    const userid = localStorage.getItem(getStorageKey('userid', true));
    const colors = ['CCFFCC', 'CCCCFF', 'FFCCCC', 'FFFFCC', 'FFCCFF', 'CCFFFF'];
    let c = 0;

    const otherCommentDiv = document.getElementById('comment-other-comments');
    otherCommentDiv.innerHTML = '';
    if (pickComments) {
        for (const key of Object.keys(pickComments)) {
            if (key === userid) continue;
            otherCommentDiv.innerHTML = otherCommentDiv.innerHTML +
                `<div style='color:#${colors[c % colors.length]}'>
                    <div style='display: inline-block; vertical-align: top; margin-right: 5px;'>${key}:</div>
                    <div style='display: inline-block;'>${pickComments[key].replace('\n','<br/>')}</div>
                </div>`;
            c = c + 1;
        }
    }
}

function clearCurrentComment() {
    const url = window.location.href.split('/');
    const userid = localStorage.getItem(getStorageKey('userid', true));
    const pick = 'p' + url[5] + 'p' + url[6];
    const draftid = url[4];
    storeText(draftid, pick, userid, '');
    document.getElementById('comment-textarea').value = '';
}

function storeText(draftid, pick, userid, text) {
    const allComments = loadCommentsFromStorage(draftid) || {};
    const oldPickComments = allComments[pick] || {};
    if ((text === '') && (allComments[pick] !== undefined)) {
        delete allComments[pick][userid];
    } else {
        oldPickComments[userid] = text;
        allComments[pick] = oldPickComments;
    }
    setCommentsFromStorage(draftid, allComments);
}

function loadCommentsFromStorage(draftid) {
    return JSON.parse(localStorage.getItem(getStorageKey('comments')));
}

function setCommentsFromStorage(draftid, comments) {
    return localStorage.setItem(getStorageKey('comments'), JSON.stringify(comments));
}

function getCurrentPick(padded = false) {
    const url = window.location.href.split('/');
    if (url.length !== 7) { //pick not in URL, ignore
        return;
    }
    return 'p' + url[5] + 'p' + url[6] + (padded && url[6] < 10 ? ' ' : '');
}

function getStorageKey(item = '', global = false) {
    const url = window.location.href.split('/');
    if ((!global) && (url.length !== 7)) { //pick not in URL, ignore for draft specific
        return;
    }
    return '17lcomments' + (global ? '' : '|' + url[4]) + (item !== '' ? '|' + item : '');
}

function exportComments() {
    const userid = localStorage.getItem(getStorageKey('userid', true));
    const url = window.location.href.split('/');
    const draftid = url[4];
    const pick = 'p' + url[5] + 'p' + url[6];
    const pickText = document.getElementById('comment-textarea').value;

    storeText(draftid, pick, userid, pickText);

    const allComments = loadCommentsFromStorage(draftid);
    const allUsers = [userid];
    const out = [];
    out.push(userid + '|' + url.slice(0,-2).join('/') + ' ')
    for (const pack of [1,2,3]) {
        out.push('');
        for (const n of [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]) {
            const pick = 'p' + pack + 'p' + n;
            if (allComments[pick] && allComments[pick][userid]) {
                out.push(pick + ': ' + allComments[pick][userid]);
            }
        }
    }
    copyToClipboard(out.join('\n'));
}

function copyToClipboard(text) {
    if (navigator.clipboard && navigator.clipboard.writeText) {
        navigator.clipboard.writeText(text)
        .then(() => console.log('Comments copied to clipboard!'))
        .catch(err => console.error('Failed to copy text: ', err));
     } else {
         console.log('clipboard not supported')
     }
}

function importCommentsFromClipboard() {
    navigator.clipboard.readText()
    .then(text => {
        const headerRegex = /(\S+)\|https\:\/\/www\.17lands\.com\/draft\/([a-f0-9]+)/;
        const commentRegex = /(p\dp\d{1,2}) ?\: (.+)/;

        const lines = text.split('\n');
        let userid = '';
        let draft = '';
        let pick = '';
        let comment = '';
        for (const line of lines) {
            const headerMatch = line.match(headerRegex);
            if (headerMatch !== null) {
                userid = headerMatch[1];
                draft = headerMatch[2];
                continue;
            }
            const commentMatch = line.match(commentRegex);
            if ((commentMatch !== null) && (userid !== '')) {
                pick = commentMatch[1];
                comment = commentMatch[2];
                storeText(draft, pick, userid, comment);
            }
            if ((commentMatch === null) && (headerMatch === null) && (line.trim() !== '')) {
                console.log(line)
                comment = comment + '\n' + line.trim();
                storeText(draft, pick, userid, comment);
            }
        }
    })
    .catch(err => {
        console.error('Failed to read clipboard contents: ', err);
    });
}

function toggleLogin(forceDisplayMode = false) {
    const nameDisplay = document.getElementById('comment-user-id');
    const nameInput = document.getElementById('comment-user-id-input');
    const idSpan = document.getElementById('comment-id-span');
    const textArea = document.getElementById('comment-textarea');
    const userid = localStorage.getItem(getStorageKey('userid', true));

    const inputMode = nameDisplay.style && (nameDisplay.style.display === 'none');
    if (inputMode || (forceDisplayMode === true)) {
        nameDisplay.innerHTML = userid;
        nameDisplay.style.display = null;
        nameInput.style = 'display: none';
        idSpan.style.border = '';
        idSpan.style.margin = '';
        textArea.disabled = false;
    } else {
        nameDisplay.style.display = 'none';
        nameInput.style = null;
        nameInput.value = userid;
    }
}

function userIdUpdate() {
    const userId = document.getElementById('comment-user-id-input').value;
    if (userId !== '' ) {
        localStorage.setItem(getStorageKey('userid', true), userId);
        toggleLogin(true);
    }
}

function highlightLogin(){
    const userInput = document.getElementById('comment-user-id-input').value;
    if (userInput !== '') {
        userIdUpdate();
    }
    const userid = localStorage.getItem(getStorageKey('userid', true));
    const idSpan = document.getElementById('comment-id-span');
    const textArea = document.getElementById('comment-textarea');
    if (userid === null) {
        idSpan.style.border = '3px solid yellow';
        idSpan.style.margin = '-3px';
        textArea.disabled = true;
    } else {
        idSpan.style.border = '';
        idSpan.style.margin = '';
        textArea.disabled = false;
    }
}

(function() {
    'use strict';
    init();
})();