Import multiple decks to Duelingbook

Script that loops the process of importing cards in order to allow multiple imports at once

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Import multiple decks to Duelingbook
// @namespace    http://tampermonkey.net/
// @version      1.9
// @description  Script that loops the process of importing cards in order to allow multiple imports at once
// @author       Andrino Cauduro - https://github.com/AndrinoC
// @match        https://www.duelingbook.com/*
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // Define the inputOK function
    function inputOK() {
        hideDisplayBoxes();
        input_callback && input_callback();
    }

    // Create control panel
    const controlPanel = document.createElement('div');
    controlPanel.style.position = 'fixed';
    controlPanel.style.top = '10px';
    controlPanel.style.right = '10px';
    controlPanel.style.zIndex = '10000';
    controlPanel.style.backgroundColor = 'white';
    controlPanel.style.border = '1px solid black';
    controlPanel.style.padding = '15px';
    controlPanel.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.5)';
    controlPanel.style.width = '150px';
    controlPanel.style.textAlign = 'center';
    controlPanel.style.transition = 'width 0.3s'; 
    document.body.appendChild(controlPanel);

    // Create close button
    const closeButton = document.createElement('button');
    closeButton.textContent = '×';
    closeButton.style.position = 'absolute';
    closeButton.style.top = '5px';
    closeButton.style.left = '-5px';
    closeButton.style.fontSize = '14px';
    closeButton.style.border = 'none';
    closeButton.style.backgroundColor = 'transparent';
    closeButton.style.cursor = 'pointer';
    closeButton.style.zIndex = '10001'; 
    controlPanel.appendChild(closeButton);

    // Create File Input button (hidden)
    const fileInputButton = document.createElement('input');
    fileInputButton.type = 'file';
    fileInputButton.style.fontSize = '20px';
    fileInputButton.style.width = '100%';
    fileInputButton.style.display = 'none';
    fileInputButton.multiple = true;
    fileInputButton.accept = '.ydk';
    fileInputButton.id = 'file-input';
    controlPanel.appendChild(fileInputButton);

    // Create Label to act as button
    const fileInputLabel = document.createElement('label');
    fileInputLabel.textContent = 'Choose .ydk files';
    fileInputLabel.htmlFor = 'file-input';
    fileInputLabel.style.fontSize = '20px';
    fileInputLabel.style.width = '100%';
    fileInputLabel.style.display = 'inline-block';
    fileInputLabel.style.backgroundColor = '#007bff';
    fileInputLabel.style.cursor = 'pointer';
    controlPanel.appendChild(fileInputLabel);

    // Create Status label
    const statusLabel = document.createElement('div');
    statusLabel.style.position = 'relative';
    statusLabel.textContent = 'Status: Waiting for file';
    statusLabel.style.fontSize = '10px';
    statusLabel.style.marginTop = '10px';
    controlPanel.appendChild(statusLabel);

    // Function to transform YDK content to JSON
    function ydkToJson(ydkContent) {
        const main = [];
        const extra = [];
        const side = [];
        let section = 'main';

        ydkContent.split('\n').forEach(line => {
            line = line.trim();
            if (line === '#main') section = 'main';
            else if (line === '#extra') section = 'extra';
            else if (line === '!side') section = 'side';
            else if (line && !line.startsWith('#')) {
                const cardId = parseInt(line, 10);
                if (!isNaN(cardId)) {
                    if (section === 'main') main.push(cardId);
                    else if (section === 'extra') extra.push(cardId);
                    else if (section === 'side') side.push(cardId);
                }
            }
        });

        return { main, extra, side };
    }

    // Function to import YDK content
    function importYDK(ydkContent, deckName) {
        const { main, extra, side } = ydkToJson(ydkContent);

        window.importYDK = function(content) {
            const cardIds = { main: [], extra: [], side: [] };
            const sections = { '#main': 'main', '#extra': 'extra', '!side': 'side' };
            let section = 'main';

            Object.entries(sections).forEach(([marker, type]) => {
                const start = content.indexOf(marker);
                const end = content.indexOf(Object.keys(sections).find(k => k !== marker), start + marker.length) || content.length;
                const sectionContent = content.slice(start + marker.length, end).trim();
                sectionContent.split('\n').forEach(line => {
                    const cardId = parseInt(line.trim(), 10);
                    if (!isNaN(cardId)) cardIds[type].push(cardId);
                });
            });

            ['main', 'extra', 'side'].forEach(type => {
                cardIds[type] = cardIds[type].map(cardId => {
                    const card = getCardByPasscode(cardId);
                    return card ? card.id : null;
                }).filter(id => id !== null);
            });

            importedCards = cardIds;
            importedDeckName = deckName;
            importDeckE();
            inputOK(); 
        };

        window.importYDK(ydkContent);

        // Reset the file input button and update status
        fileInputButton.value = '';
        statusLabel.textContent = 'Status: Ready for new file';
    }

    // Function to process files (single .ydk or array of promises for .ydk files)
    function processFiles(files) {
        if (!Array.isArray(files)) files = [files];
        let fileIndex = 0;
        const delay = 500; // Delay before processing the next file

        function importNextFile() {
            if (fileIndex < files.length) {
                files[fileIndex].then(({ content, name }) => {
                    importYDK(content, name);
                    fileIndex++;
                    setTimeout(importNextFile, delay); 
                });
            } else {
                statusLabel.textContent = 'Status: All files processed';
            }
        }

        importNextFile();
    }

    // Function to extract deck name from file name
    function getDeckName(fileName) {
        return fileName.replace('.ydk', '');
    }

    // Function to read file content and name
    function readFile(file) {
        return new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.onload = (e) => resolve({ content: e.target.result, name: getDeckName(file.name) });
            reader.onerror = reject;
            reader.readAsText(file);
        });
    }

    // Event listener for file input
    fileInputButton.addEventListener('change', (event) => {
        const files = Array.from(event.target.files);
        if (files.length > 0) {
            statusLabel.textContent = 'Status: Processing files...';
            const ydkFiles = files.filter(file => file.name.endsWith('.ydk'));

            if (ydkFiles.length > 0) {
                processFiles(ydkFiles.map(readFile));
            } else {
                statusLabel.textContent = 'Status: No .ydk files found';
            }
        }
    });

    // Minimize/Maximize functionality
    let isMinimized = false;

    closeButton.addEventListener('click', () => {
        if (isMinimized) {
            controlPanel.style.width = '150px';
            statusLabel.style.display = 'block';
            fileInputLabel.style.display = 'inline-block';
            fileInputButton.style.display = 'none'; // Ensure file input button stays hidden
        } else {
            controlPanel.style.width = '30px';
            statusLabel.style.display = 'none';
            fileInputLabel.style.display = 'none';
            fileInputButton.style.display = 'none';
        }
        isMinimized = !isMinimized;
    });

})();