Multi Profile AutoFill Script

Multi-profile autofill with draggable panel, collapsible menu, merge import, bulk edit

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

/* jshint esversion: 8 */
// ==UserScript==
// @name         Multi Profile AutoFill Script
// @namespace    http://tampermonkey.net/
// @version      6.1
// @description  Multi-profile autofill with draggable panel, collapsible menu, merge import, bulk edit
// @match        *://*/*
// @grant        GM_getValue
// @grant        GM_setValue
// @license      MIT
// ==/UserScript==

/*
MIT License with No Liability
Copyright (c) 2026 Shariar

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The software is provided "as is", without warranty of any kind, express or
implied, including but not limited to the warranties of merchantability,
fitness for a particular purpose, or non-infringement. In no event shall
the authors or copyright holders be liable for any claim, damages, or other
liability, whether in an action of contract, tort, or otherwise, arising
from, out of, or in connection with the software or the use or other dealings
in the software.
*/

/*
⚠️ DISCLAIMER:
This script is for personal use to automate form-filling.
The author (Shariar) is not responsible for any misuse or damages
resulting from the use of this script.
Do not use for illegal activity.
*/

(async function () {
    'use strict';

    const STORAGE_KEY = "TM_Global_AutoFill_Profiles";
    const defaultProfiles = { "Default": {} };

    // ===== Load profiles globally =====
    async function loadProfiles() {
        const data = await GM_getValue(STORAGE_KEY, "{}");
        return Object.keys(JSON.parse(data)).length ? JSON.parse(data) : defaultProfiles;
    }

    // ===== Save profiles globally =====
    function saveProfiles(data) {
        GM_setValue(STORAGE_KEY, JSON.stringify(data));
    }

    // ===== Initialize profiles inside async function =====
    let profiles = await loadProfiles();
    let currentProfile = Object.keys(profiles)[0];

    // ===== Get Unique Key =====
    function getKey(el) {
        return el.name || el.id || el.placeholder || null;
    }

    // ===== Fill Form =====
    function fillForm() {
        const elements = document.querySelectorAll("input, textarea, select");
        elements.forEach(el => {
            const key = getKey(el);
            if (!key) return;
            const value = profiles[currentProfile][key];
            if (el.type === "checkbox") el.checked = !!value;
            else if (el.type === "radio") { if (value === el.value) el.checked = true; }
            else { if (value !== undefined) el.value = value; }
            el.dispatchEvent(new Event("input", { bubbles: true }));
            el.dispatchEvent(new Event("change", { bubbles: true }));
        });
    }

    // ===== Save From Page =====
    function saveFromPage() {
        const elements = document.querySelectorAll("input, textarea, select");
        elements.forEach(el => {
            const key = getKey(el);
            if (!key) return;
            if (el.type === "checkbox") profiles[currentProfile][key] = el.checked;
            else if (el.type === "radio") { if (el.checked) profiles[currentProfile][key] = el.value; }
            else profiles[currentProfile][key] = el.value;
        });
        saveProfiles(profiles);
        alert(`✅ Data saved to profile: ${currentProfile}`);
    }

    // ===== UI =====
    const panel = document.createElement("div");
    panel.style.position = "fixed";
    panel.style.bottom = "20px";
    panel.style.right = "20px";
    panel.style.background = "#222";
    panel.style.color = "#fff";
    panel.style.padding = "12px";
    panel.style.borderRadius = "8px";
    panel.style.zIndex = "9999";
    panel.style.width = "240px";
    panel.style.fontSize = "13px";

    panel.innerHTML = `
        <b>⚡ AutoFill</b><br><br>
        <select id="profileSelect" style="width:100%; margin-bottom:6px;"></select>
        <button id="fillBtn" style="width:100%; margin-bottom:5px;">Fill</button>
        <button id="saveBtn" style="width:100%; margin-bottom:5px;">Save From Page</button>
        <button id="addBtn" style="width:100%; margin-bottom:5px;">Add Profile</button>
        <button id="editBtn" style="width:100%; margin-bottom:5px;">Edit Profile</button>
        <button id="deleteBtn" style="width:100%; margin-bottom:5px;">Delete</button>
        <button id="logBtn" style="width:100%; margin-bottom:5px;">Log Profiles</button>
        <button id="exportBtn" style="width:100%; margin-bottom:5px;">Export Profiles</button>
        <button id="importBtn" style="width:100%;">Import Profiles</button>
        <input type="file" id="importFile" style="display:none;" accept=".json"/>
    `;

    document.body.appendChild(panel);

    // ===== Draggable panel =====
    let isDragging = false, offsetX = 0, offsetY = 0;
    panel.addEventListener("mousedown", (e) => {
        if (!["BUTTON", "SELECT", "INPUT", "TEXTAREA"].includes(e.target.tagName)) {
            isDragging = true;
            offsetX = e.clientX - panel.offsetLeft;
            offsetY = e.clientY - panel.offsetTop;
        }
    });
    document.addEventListener("mousemove", (e) => {
        if (isDragging) {
            panel.style.left = e.clientX - offsetX + "px";
            panel.style.top = e.clientY - offsetY + "px";
            panel.style.bottom = "auto";
            panel.style.right = "auto";
        }
    });
    document.addEventListener("mouseup", () => { isDragging = false; });

    // ===== Toggle collapsed menu button =====
    const toggleButton = document.createElement("button");
    toggleButton.innerHTML = "⚡ AutoFill";
    toggleButton.style.position = "fixed";
    toggleButton.style.bottom = "20px";
    toggleButton.style.right = "20px";
    toggleButton.style.zIndex = "9999";
    toggleButton.style.padding = "6px 10px";
    toggleButton.style.borderRadius = "6px";
    toggleButton.style.background = "#222";
    toggleButton.style.color = "#fff";
    toggleButton.style.cursor = "pointer";
    toggleButton.style.fontSize = "13px";
    document.body.appendChild(toggleButton);

    panel.style.display = "none"; // hide initially
    toggleButton.onclick = () => {
        panel.style.display = (panel.style.display === "none") ? "block" : "none";
    };
    document.addEventListener("click", (e) => {
        if (!panel.contains(e.target) && e.target !== toggleButton) {
            panel.style.display = "none";
        }
    });

    const profileSelect = document.getElementById("profileSelect");
    function refreshDropdown() {
        profileSelect.innerHTML = "";
        Object.keys(profiles).forEach(name => {
            const opt = document.createElement("option");
            opt.value = name;
            opt.textContent = name;
            profileSelect.appendChild(opt);
        });
        profileSelect.value = currentProfile;
    }
    refreshDropdown();
    profileSelect.onchange = function () { currentProfile = this.value; };

    // ===== Button Handlers =====
    document.getElementById("fillBtn").onclick = fillForm;
    document.getElementById("saveBtn").onclick = saveFromPage;
    document.getElementById("addBtn").onclick = () => {
        const name = prompt("Profile Name:");
        if (!name) return;
        profiles[name] = {};
        saveProfiles(profiles);
        currentProfile = name;
        refreshDropdown();
    };

    // ===== Edit Profile (Bulk Edit + Rename) =====
    document.getElementById("editBtn").onclick = () => {
        if (!currentProfile) return;
        const newName = prompt("Rename profile (leave blank to keep same):", currentProfile);
        if (newName && newName !== currentProfile) {
            profiles[newName] = profiles[currentProfile];
            delete profiles[currentProfile];
            currentProfile = newName;
        }
        const fields = profiles[currentProfile];
        let text = Object.entries(fields).map(([k,v]) => `${k}=${v}`).join("\n");
        const newText = prompt("Edit all fields (one per line: key=value):", text);
        if (newText !== null) {
            const newFields = {};
            newText.split("\n").forEach(line => {
                const [key, ...rest] = line.split("=");
                if (key) newFields[key.trim()] = rest.join("=").trim();
            });
            profiles[currentProfile] = newFields;
        }
        saveProfiles(profiles);
        refreshDropdown();
        alert(`✅ Profile "${currentProfile}" updated!`);
    };

    document.getElementById("deleteBtn").onclick = () => {
        if (Object.keys(profiles).length <= 1) {
            alert("At least one profile required!");
            return;
        }
        if (confirm("Delete this profile?")) {
            delete profiles[currentProfile];
            saveProfiles(profiles);
            currentProfile = Object.keys(profiles)[0];
            refreshDropdown();
        }
    };

    document.getElementById("logBtn").onclick = () => {
        console.log("📋 All Saved Profiles:", profiles);
        alert("✅ Profiles logged to console!");
    };

    // ===== Export & Import (Merge Import) =====
    const importFile = document.getElementById("importFile");
    document.getElementById("exportBtn").onclick = () => {
        const blob = new Blob([JSON.stringify(profiles, null, 2)], { type: "application/json" });
        const link = document.createElement("a");
        link.href = URL.createObjectURL(blob);
        link.download = "autofill_profiles_backup.json";
        link.click();
        alert("✅ Profiles exported!");
    };
    document.getElementById("importBtn").onclick = () => { importFile.click(); };
    importFile.onchange = function () {
        const file = this.files[0];
        if (!file) return;
        const reader = new FileReader();
        reader.onload = function (e) {
            try {
                const importedProfiles = JSON.parse(e.target.result);
                Object.keys(importedProfiles).forEach(name => { profiles[name] = importedProfiles[name]; });
                saveProfiles(profiles);
                currentProfile = Object.keys(profiles)[0];
                refreshDropdown();
                alert("✅ Profiles imported successfully (merged)!");
            } catch (err) {
                alert("❌ Invalid JSON file!");
            }
        };
        reader.readAsText(file);
    };

})();