Alook Userscript Installer Helper (Auto Install)

[Auto-Install Version] Helps Alook browser install scripts from GreasyFork, OpenUserJS, and any raw .user.js links. Features auto-trigger, emoji support, and run-at detection.

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         Alook Userscript Installer Helper (Auto Install)
// @namespace    http://tampermonkey.net/
// @version      0.2
// @license      GPL3
// @description  [Auto-Install Version] Helps Alook browser install scripts from GreasyFork, OpenUserJS, and any raw .user.js links. Features auto-trigger, emoji support, and run-at detection.
// @author       King1x32
// @match        *://*/*.user.js
// @icon         https://www.google.com/s2/favicons?domain=alookweb.com
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    // Check if running on a compatible browser (Alook/Via)
    let isVia = window.via && typeof window.via.addon === "function";
    let Base64 = {encode: s=>btoa(unescape(encodeURIComponent(s))), decode: s=>decodeURIComponent(escape(window.atob(s)))};
    let zhBase64 = { // This encoding part is kept for compatibility with Alook's addon format
        encode: function (input) {
            let _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
            let output = "";
            let chr1, chr2, chr3, enc1, enc2, enc3, enc4;
            let i = 0;
            input = this.utf8_encode(input);
            while (i < input.length) {
                chr1 = input.charCodeAt(i++);
                chr2 = input.charCodeAt(i++);
                chr3 = input.charCodeAt(i++);
                enc1 = chr1 >> 2;
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                enc4 = chr3 & 63;
                if (isNaN(chr2)) { enc3 = enc4 = 64;
                } else if (isNaN(chr3)) { enc4 = 64; }
                output = output + _keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4);
            }
            return output;
        },
        utf8_encode: function(_string) {
            return _string.replace(/([\u2E80-\u2EFF\u2F00-\u2FDF\u3000-\u303F\u31C0-\u31EF\u3200-\u32FF\u3300-\u33FF\u3400-\u4DBF\u4DC0-\u4DFF\u4E00-\u9FBF\uF900-\uFAFF\uFE30-\uFE4F\uFF00-\uFFEF])/g, str => {
                return '\\u' + str.charCodeAt(0).toString(16)
            })
        }
    };

    function tryGetExec(result, idx, fallback) { if (!result || result.length < idx + 1) return fallback; else return result[idx]; }

    async function addonApp(scriptUrl) {
        try {
            let scriptContent = await (await (fetch(scriptUrl))).text();
            
            // IMPROVEMENT: Use more robust regex to capture full names/authors with spaces and special characters.
            let version = tryGetExec(/\/\/\s*@version\s*([^\s\n]*)/.exec(scriptContent), 1, "1.0");
            let author = tryGetExec(/\/\/\s*@author\s*([^\n]*)/.exec(scriptContent), 1, "unknown");
            let name = tryGetExec(/\/\/\s*@name\s*([^\n]*)/.exec(scriptContent), 1, "unknown script");

            let matchRules = scriptContent.match(/\/\/\s*@(match|include)\s*([^\s\n]*)/g);
            let match = ".*"; // Default if not found
            if (matchRules) {
                match = matchRules
                    .map(s => tryGetExec(/\/\/\s*@(match|include)\s*([^\s]*)/.exec(s), 2, ""))
                    .filter(s => s)
                    .map(s => s.replace(/\*/g, ".*")) // Use compatible replace method
                    .join("@@");
            }
            
            // IMPROVEMENT: Automatically detect the run-at time.
            let runAtValue = 2; // Default to 2 (DOMContentLoaded)
            const runAtMatch = /\/\/\s*@run-at\s*([^\s\n]*)/.exec(scriptContent);
            if (runAtMatch && runAtMatch[1].trim() === 'document-start') {
                runAtValue = 1; // Set to 1 for earliest execution
            }

            let codeStr = Base64.encode(`(function (){${scriptContent}})();`);
            var appJson = {
                author: author.trim(),
                id: "1", // Alook seems to ignore this, so "1" is fine.
                runat : runAtValue,
                version : version.trim(),
                name: name.trim(),
                url: match,
                code: codeStr,
            };
            var appStr = JSON.stringify(appJson);
            var addon_result = zhBase64.encode(appStr);

            window.via.addon(addon_result);
        } catch (error) {
            alert("An error occurred while preparing the script: " + error);
            console.error(error);
        }
    }

    function FindAndCreateInstallBtn(selectStr) {
        let installBtn = document.querySelector(selectStr);
        if (!installBtn) return false;

        var button = document.createElement("a");
        button.append(isVia ? "Install for Alook" : "Browser not supported");
        button.className = installBtn.className;
        button.style.color = "black";
        button.style.background = "lightgreen";
        button.style.marginLeft = "10px";
        button.href = "javascript:void(0);"; // Prevent navigation

        // IMPROVEMENT: No more confirmation prompt. Install directly.
        button.onclick = () => {
            if (isVia) {
                addonApp(installBtn.href);
            }
        };
        installBtn.parentNode.insertBefore(button, installBtn.nextSibling);
        return true;
    };

    // --- MAIN CONTROLLER ---
    if (!isVia) return; // Exit if not on a compatible browser

    // If it's a raw .user.js page, trigger installation automatically on load.
    if (location.href.endsWith('.user.js')) {
        // Wait for the body to be sure the page content (the script text) is loaded.
        if (document.readyState === 'complete' || document.readyState === 'interactive') {
            addonApp(location.href);
        } else {
            window.addEventListener('DOMContentLoaded', () => addonApp(location.href));
        }
    } else {
        // If it's a script repository site, find the existing install button and add our own.
        let targets = [
            "a.install-link", // Greasy Fork
            "a.btn.btn-info[type='text/javascript']", // OpenUserJS
        ];
        for (let i = 0; i < targets.length; ++i) {
            if (FindAndCreateInstallBtn(targets[i])) break;
        }
    }

})();