Furaffinity-Message-Box

Library to hold MessageBox functions for Furaffinity

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.greasyfork.org/scripts/528997/1760939/Furaffinity-Message-Box.js

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        Furaffinity-Message-Box
// @namespace   Violentmonkey Scripts
// @require     https://greasyfork.org/scripts/525666-furaffinity-prototype-extensions/code/525666-furaffinity-prototype-extensions.js
// @grant       GM_info
// @version     1.0.3
// @author      Midori Dragon
// @description Library to hold MessageBox functions for Furaffinity
// @icon        https://www.furaffinity.net/themes/beta/img/banners/fa_logo.png
// @license     MIT
// @homepageURL https://greasyfork.org/scripts/528997-furaffinity-message-box
// @supportURL  https://greasyfork.org/scripts/528997-furaffinity-message-box/feedback
// ==/UserScript==
// jshint esversion: 11
(function () {
    'use strict';

    /**
     * Specifies the buttons that are displayed on a message box.
     * Similar to System.Windows.Forms.MessageBoxButtons in C#.
     */
    var MessageBoxButtons;
    (function (MessageBoxButtons) {
        /**
         * The message box contains an OK button.
         */
        MessageBoxButtons[MessageBoxButtons["OK"] = 0] = "OK";
        /**
         * The message box contains OK and Cancel buttons.
         */
        MessageBoxButtons[MessageBoxButtons["OKCancel"] = 1] = "OKCancel";
        /**
         * The message box contains Abort, Retry, and Ignore buttons.
         */
        MessageBoxButtons[MessageBoxButtons["AbortRetryIgnore"] = 2] = "AbortRetryIgnore";
        /**
         * The message box contains Yes, No, and Cancel buttons.
         */
        MessageBoxButtons[MessageBoxButtons["YesNoCancel"] = 3] = "YesNoCancel";
        /**
         * The message box contains Yes and No buttons.
         */
        MessageBoxButtons[MessageBoxButtons["YesNo"] = 4] = "YesNo";
        /**
         * The message box contains Retry and Cancel buttons.
         */
        MessageBoxButtons[MessageBoxButtons["RetryCancel"] = 5] = "RetryCancel";
    })(MessageBoxButtons || (MessageBoxButtons = {}));

    /**
     * Specifies the icon that is displayed on a message box.
     * Similar to System.Windows.Forms.MessageBoxIcon in C#.
     */
    var MessageBoxIcon;
    (function (MessageBoxIcon) {
        /**
         * No icon is displayed.
         */
        MessageBoxIcon[MessageBoxIcon["None"] = 0] = "None";
        /**
         * An error icon is displayed on the message box.
         */
        MessageBoxIcon[MessageBoxIcon["Error"] = 16] = "Error";
        /**
         * A warning icon is displayed on the message box.
         */
        MessageBoxIcon[MessageBoxIcon["Warning"] = 48] = "Warning";
        /**
         * An information icon is displayed on the message box.
         */
        MessageBoxIcon[MessageBoxIcon["Information"] = 64] = "Information";
        /**
         * A question mark icon is displayed on the message box.
         */
        MessageBoxIcon[MessageBoxIcon["Question"] = 32] = "Question";
    })(MessageBoxIcon || (MessageBoxIcon = {}));

    /**
     * Specifies identifiers to indicate the return value of a dialog box.
     * Similar to System.Windows.Forms.DialogResult in C#.
     */
    var DialogResult;
    (function (DialogResult) {
        /**
         * Nothing is returned from the dialog box. This means that the modal dialog continues running.
         */
        DialogResult[DialogResult["None"] = 0] = "None";
        /**
         * The dialog box return value is OK (usually sent from a button labeled OK).
         */
        DialogResult[DialogResult["OK"] = 1] = "OK";
        /**
         * The dialog box return value is Cancel (usually sent from a button labeled Cancel).
         */
        DialogResult[DialogResult["Cancel"] = 2] = "Cancel";
        /**
         * The dialog box return value is Abort (usually sent from a button labeled Abort).
         */
        DialogResult[DialogResult["Abort"] = 3] = "Abort";
        /**
         * The dialog box return value is Retry (usually sent from a button labeled Retry).
         */
        DialogResult[DialogResult["Retry"] = 4] = "Retry";
        /**
         * The dialog box return value is Ignore (usually sent from a button labeled Ignore).
         */
        DialogResult[DialogResult["Ignore"] = 5] = "Ignore";
        /**
         * The dialog box return value is Yes (usually sent from a button labeled Yes).
         */
        DialogResult[DialogResult["Yes"] = 6] = "Yes";
        /**
         * The dialog box return value is No (usually sent from a button labeled No).
         */
        DialogResult[DialogResult["No"] = 7] = "No";
    })(DialogResult || (DialogResult = {}));

    /**
     * Provides SVG icons for the MessageBox component.
     */
    class MessageBoxIcons {
        /**
         * Gets the SVG icon based on the specified MessageBoxIcon.
         * @param icon The MessageBoxIcon to get the SVG for.
         * @returns The SVG string for the specified icon.
         */
        static getIconSvg(icon) {
            switch (icon) {
                case MessageBoxIcon.Error:
                    return '<svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 -960 960 960" width="32px" fill="#ff0000"><path d="M480-280q17 0 28.5-11.5T520-320q0-17-11.5-28.5T480-360q-17 0-28.5 11.5T440-320q0 17 11.5 28.5T480-280Zm-40-160h80v-240h-80v240Zm40 360q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>';
                case MessageBoxIcon.Warning:
                    return '<svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 -960 960 960" width="32px" fill="#ffcc4d"><path d="m40-120 440-760 440 760H40Zm138-80h604L480-720 178-200Zm302-40q17 0 28.5-11.5T520-280q0-17-11.5-28.5T480-320q-17 0-28.5 11.5T440-280q0 17 11.5 28.5T480-240Zm-40-120h80v-200h-80v200Zm40-100Z"/></svg>';
                case MessageBoxIcon.Information:
                    return '<svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 -960 960 960" width="32px" fill="#2196f3"><path d="M440-280h80v-240h-80v240Zm40-320q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm0 520q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>';
                case MessageBoxIcon.Question:
                    return '<svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 -960 960 960" width="32px" fill="#2196f3"><path d="M478-240q21 0 35.5-14.5T528-290q0-21-14.5-35.5T478-340q-21 0-35.5 14.5T428-290q0 21 14.5 35.5T478-240Zm-36-154h74q0-33 7.5-52t42.5-52q26-26 41-49.5t15-56.5q0-56-41-86t-97-30q-57 0-92.5 30T342-618l66 26q5-18 22.5-39t53.5-21q32 0 48 17.5t16 38.5q0 20-12 37.5T506-526q-44 39-54 59t-10 73Zm38 314q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>';
                case MessageBoxIcon.None:
                default:
                    return '';
            }
        }
    }

    /**
     * Specifies the theme that is used for the message box.
     */
    var MessageBoxThemes;
    (function (MessageBoxThemes) {
        /**
         * Dark theme with white text and dark gray buttons.
         */
        MessageBoxThemes["Dark"] = "dark";
        /**
         * Aurora theme with white text and medium gray buttons.
         */
        MessageBoxThemes["Aurora"] = "aurora";
        /**
         * Retro theme with white text and medium blue-gray buttons.
         */
        MessageBoxThemes["Retro"] = "retro";
        /**
         * Slate theme with white text and light gray buttons.
         */
        MessageBoxThemes["Slate"] = "slate";
        /**
         * Light theme with black text and light gray buttons.
         */
        MessageBoxThemes["Light"] = "light";
    })(MessageBoxThemes || (MessageBoxThemes = {}));

    function styleInject(css, ref) {
      if ( ref === void 0 ) ref = {};
      var insertAt = ref.insertAt;

      if (typeof document === 'undefined') { return; }

      var head = document.head || document.getElementsByTagName('head')[0];
      var style = document.createElement('style');
      style.type = 'text/css';

      if (insertAt === 'top') {
        if (head.firstChild) {
          head.insertBefore(style, head.firstChild);
        } else {
          head.appendChild(style);
        }
      } else {
        head.appendChild(style);
      }

      if (style.styleSheet) {
        style.styleSheet.cssText = css;
      } else {
        style.appendChild(document.createTextNode(css));
      }
    }

    var css_248z = "/* Base styles */\n.message-box-overlay {\n    position: fixed;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n    background-color: rgba(0, 0, 0, 0.5);\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    z-index: 9999;\n}\n\n/* Dark theme (default) */\n.message-box-container {\n    border: 1px solid #444;\n    border-radius: 5px;\n    padding: 20px;\n    max-width: 500px;\n    width: 100%;\n    font-family: Arial, sans-serif;\n    transition: background-color 0.3s, color 0.3s, border-color 0.3s, box-shadow 0.3s;\n}\n\n.message-box-header {\n    display: flex;\n    align-items: center;\n    margin-bottom: 15px;\n}\n\n.message-box-icon-container {\n    margin-right: 15px;\n    width: 32px;\n    height: 32px;\n    flex-shrink: 0;\n}\n\n.message-box-title {\n    font-size: 18px;\n    font-weight: bold;\n    margin: 0;\n    transition: color 0.3s;\n}\n\n.message-box-content {\n    margin-bottom: 20px;\n    line-height: 1.5;\n    transition: color 0.3s;\n}\n\n.message-box-button-container {\n    display: flex;\n    justify-content: flex-end;\n    gap: 10px;\n}\n\n.message-box-button {\n    padding: 8px 16px;\n    border: none;\n    border-radius: 4px;\n    cursor: pointer;\n    font-size: 14px;\n    font-weight: bold;\n    background-color: #f1efeb;\n    transition: background-color 0.2s, color 0.2s, border-color 0.2s;\n}\n\n.message-box-button:hover {\n    background-color: #e0ded8;\n}\n\n/* Theme: Dark */\nbody[class*=\"theme-dark\"] .message-box-container {\n    background-color: #353b45;\n    border-color: #444;\n    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4);\n}\n\nbody[class*=\"theme-dark\"] .message-box-button {\n    background-color: #434b5b;\n}\n\nbody[class*=\"theme-dark\"] .message-box-button:hover {\n    background-color: #576175;\n}\n\n/* Theme: Aurora */\nbody[class*=\"theme-aurora\"] .message-box-container {\n    background-color: #262931;\n    border-color: #444;\n    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4);\n}\n\nbody[class*=\"theme-aurora\"] .message-box-button {\n    background-color: #65707c;\n}\n\nbody[class*=\"theme-aurora\"] .message-box-button:hover {\n    background-color: #8692a0;\n}\n\n/* Theme: Retro */\nbody[class*=\"theme-retro\"] .message-box-container {\n    background-color: #2e3b41;\n    border-color: #444;\n    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4);\n}\n\nbody[class*=\"theme-retro\"] .message-box-button {\n    background-color: #4c585e;\n}\n\nbody[class*=\"theme-retro\"] .message-box-button:hover {\n    background-color: #7b909a;\n}\n\n/* Theme: Slate */\nbody[class*=\"theme-slate\"] .message-box-container {\n    background-color: #202225;\n    border-color: #444;\n    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4);\n}\n\nbody[class*=\"theme-slate\"] .message-box-button {\n    background-color: #8c8c8c;\n}\n\nbody[class*=\"theme-slate\"] .message-box-button:hover {\n    background-color: #b3b1b1;\n}\n\n/* Theme: Light - already defined in base styles */\nbody[class*=\"theme-light\"] .message-box-container {\n    background-color: #f7f7f7;\n    border-color: #ccc;\n    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);\n}\n\nbody[class*=\"theme-light\"] .message-box-button {\n    background-color: #f1efeb;\n}\n\nbody[class*=\"theme-light\"] .message-box-button:hover {\n    background-color: #f1ede7;\n}\n";
    styleInject(css_248z);

    class string {
        static isNullOrWhitespace(str) {
            return str == null || str.trim() === '';
        }
        static isNullOrEmpty(str) {
            return str == null || str === '';
        }
    }

    class MessageBox {
        static overlay = null;
        static container = null;
        static result = DialogResult.None;
        static resolvePromise = null;
        static currentTheme = MessageBoxThemes.Light;
        /**
         * Gets the current theme.
         * @returns The current theme.
         */
        static getTheme() {
            return this.currentTheme;
        }
        /**
         * Shows a message box with the specified text, caption, buttons, and icon.
         * @param text The text to display in the message box.
         * @param caption The text to display in the title bar of the message box.
         * @param buttons One of the MessageBoxButtons values that specifies which buttons to display in the message box.
         * @param icon One of the MessageBoxIcon values that specifies which icon to display in the message box.
         * @returns A DialogResult value that indicates which button was clicked.
         */
        static async show(text, caption = '', buttons = MessageBoxButtons.OK, icon = MessageBoxIcon.None) {
            // Create a promise that will be resolved when a button is clicked
            return new Promise((resolve) => {
                this.resolvePromise = resolve;
                this.createMessageBox(text, caption, buttons, icon);
            });
        }
        /**
         * Creates the message box elements and adds them to the DOM.
         */
        static createMessageBox(text, caption, buttons, icon) {
            // Create overlay
            this.overlay = document.createElement('div');
            this.overlay.className = 'message-box-overlay';
            // Create container
            this.container = document.createElement('div');
            this.container.className = 'message-box-container';
            // Create header (icon + title)
            const header = document.createElement('div');
            header.className = 'message-box-header';
            // Add icon if specified
            if (icon !== MessageBoxIcon.None) {
                const iconContainer = document.createElement('div');
                iconContainer.className = 'message-box-icon-container';
                iconContainer.innerHTML = MessageBoxIcons.getIconSvg(icon);
                header.appendChild(iconContainer);
            }
            // Add title if specified
            if (!string.isNullOrWhitespace(caption)) {
                const title = document.createElement('h3');
                title.className = 'message-box-title';
                title.textContent = caption;
                header.appendChild(title);
            }
            // Add header to container if it has children
            if (header.children.length !== 0) {
                this.container.appendChild(header);
            }
            // Add content
            const content = document.createElement('div');
            content.className = 'message-box-content';
            content.textContent = text;
            this.container.appendChild(content);
            // Add buttons
            const buttonContainer = document.createElement('div');
            buttonContainer.className = 'message-box-button-container';
            // Add appropriate buttons based on the MessageBoxButtons enum
            this.addButtons(buttonContainer, buttons);
            this.container.appendChild(buttonContainer);
            this.overlay.appendChild(this.container);
            // Add to DOM
            document.body.appendChild(this.overlay);
        }
        /**
         * Adds the appropriate buttons to the message box based on the MessageBoxButtons enum.
         */
        static addButtons(buttonContainer, buttons) {
            switch (buttons) {
                case MessageBoxButtons.OK:
                    this.createButton(buttonContainer, 'OK', DialogResult.OK);
                    break;
                case MessageBoxButtons.OKCancel:
                    this.createButton(buttonContainer, 'OK', DialogResult.OK);
                    this.createButton(buttonContainer, 'Cancel', DialogResult.Cancel);
                    break;
                case MessageBoxButtons.AbortRetryIgnore:
                    this.createButton(buttonContainer, 'Abort', DialogResult.Abort);
                    this.createButton(buttonContainer, 'Retry', DialogResult.Retry);
                    this.createButton(buttonContainer, 'Ignore', DialogResult.Ignore);
                    break;
                case MessageBoxButtons.YesNoCancel:
                    this.createButton(buttonContainer, 'Yes', DialogResult.Yes);
                    this.createButton(buttonContainer, 'No', DialogResult.No);
                    this.createButton(buttonContainer, 'Cancel', DialogResult.Cancel);
                    break;
                case MessageBoxButtons.YesNo:
                    this.createButton(buttonContainer, 'Yes', DialogResult.Yes);
                    this.createButton(buttonContainer, 'No', DialogResult.No);
                    break;
                case MessageBoxButtons.RetryCancel:
                    this.createButton(buttonContainer, 'Retry', DialogResult.Retry);
                    this.createButton(buttonContainer, 'Cancel', DialogResult.Cancel);
                    break;
            }
        }
        /**
         * Creates a button element with the specified text and result.
         */
        static createButton(container, text, result) {
            const button = document.createElement('button');
            button.className = 'message-box-button';
            button.textContent = text;
            // Add click handler
            button.addEventListener('click', () => {
                this.close(result);
            });
            container.appendChild(button);
        }
        /**
         * Closes the message box and resolves the promise with the specified result.
         */
        static close(result) {
            this.result = result;
            // Remove from DOM
            if (this.overlay != null) {
                document.body.removeChild(this.overlay);
                this.overlay = null;
                this.container = null;
            }
            // Resolve the promise
            if (this.resolvePromise != null) {
                this.resolvePromise(result);
                this.resolvePromise = null;
            }
        }
    }

    Object.defineProperties(window, {
        FAMessageBox: { get: () => MessageBox },
        FAMessageBoxButtons: { get: () => MessageBoxButtons },
        FAMessageBoxIcon: { get: () => MessageBoxIcon },
        FADialogResult: { get: () => DialogResult },
    });
    let themeClassName = 'dark';
    const themeStylesheets = document.head.querySelectorAll('link[rel="stylesheet"][href]') ?? [];
    for (const themeStylesheet of Array.from(themeStylesheets)) {
        const themePath = themeStylesheet.getAttribute('href')?.toLowerCase() ?? '';
        if (themePath.includes('dark')) {
            themeClassName = 'dark';
        }
        else if (themePath.includes('aurora')) {
            themeClassName = 'aurora';
        }
        else if (themePath.includes('retro')) {
            themeClassName = 'retro';
        }
        else if (themePath.includes('slate')) {
            themeClassName = 'slate';
        }
        else if (themePath.includes('light')) {
            themeClassName = 'light';
        }
    }
    document.body.classList.add(`theme-${themeClassName}`);

})();