Reverse Alias Arrow Handler (for Steam)

Adds a button to automatically move alias arrow to start of Steam name using a formatting error.

// ==UserScript==
// @name         Reverse Alias Arrow Handler (for Steam)
// @namespace    steam.raah
// @version      1337
// @description  Adds a button to automatically move alias arrow to start of Steam name using a formatting error.
// @match        https://steamcommunity.com/id/*/edit/info
// @grant        GM_xmlhttpRequest
// @connect      pastebin.com
// ==/UserScript==

(function () {
    'use strict';

    const PASTEBIN_RAW_URL = 'https://pastebin.com/raw/gBrYZNcJ';
    const BUTTON_ID = 'alias-arrow-button';
    const FALLBACK_SEQUENCE = '\u{E01D3}\u2067\u2067 '; // Fallback: 󠁳⁧⁧ + space

    function fetchSequence(callback) {
        console.log('Fetching sequence from Pastebin...');
        GM_xmlhttpRequest({
            method: 'GET',
            url: PASTEBIN_RAW_URL,
            onload: function (response) {
                if (response.status === 200) {
                    let sequence = response.responseText.trim();
                    console.log('Raw Pastebin response:', sequence, 'Length:', sequence.length, 'Codes:', Array.from(sequence).map(c => c.charCodeAt(0).toString(16).padStart(4, '0')));
                    // Sanitize: Ensure sequence is valid (4 chars: tag, RLO, RLO, space)
                    if (sequence && sequence.length === 4) {
                        console.log('Valid sequence fetched:', sequence);
                        callback(sequence);
                    } else {
                        console.error('Invalid sequence from Pastebin, length:', sequence.length);
                        callback(FALLBACK_SEQUENCE);
                    }
                } else {
                    console.error('Pastebin fetch failed, status:', response.status);
                    callback(FALLBACK_SEQUENCE);
                }
            },
            onerror: function () {
                console.error('Network error fetching Pastebin sequence');
                callback(FALLBACK_SEQUENCE);
            }
        });
    }

    function removeSteamErrorMessages() {
        const errorElems = document.querySelectorAll('.form_error, .profile_error_msg, .DialogError, .error');
        errorElems.forEach(elem => {
            if (!elem.textContent.includes('You are changing username too fast!')) {
                elem.remove();
            }
        });
    }

    function clickSaveButton() {
        const saveButton = document.querySelector('.DialogButton._DialogLayout.Primary.Focusable');
        if (saveButton) {
            console.log('Clicking save button');
            saveButton.click();
        } else {
            console.warn('Save button not found');
        }
    }

    function setInputValue(input, value) {
        console.log('Setting input value to:', value);
        const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
        nativeSetter.call(input, value);
        input.dispatchEvent(new Event('input', { bubbles: true }));
        input.dispatchEvent(new Event('change', { bubbles: true }));
    }

    function insertStyledToggleButton(sequence) {
        const nameInput = document.querySelector('input[name="personaName"]');
        if (!nameInput) {
            console.warn('Name input field not found');
            return;
        }

        const prefix = sequence; // Use sequence as-is (󠁳⁧⁧ + space)
        console.log('Using prefix:', prefix, 'Length:', prefix.length);
        if (document.getElementById(BUTTON_ID)) {
            console.log('Button already exists, skipping creation');
            return;
        }

        const wrapper = document.createElement('div');
        wrapper.style.display = 'flex';
        wrapper.style.justifyContent = 'flex-end';
        wrapper.style.marginTop = '4px';

        const btn = document.createElement('button');
        btn.id = BUTTON_ID;
        btn.innerHTML = '↺';
        btn.title = 'Toggle Alias Arrow Position';
        btn.type = 'button';
        btn.style.marginLeft = 'auto';
        btn.style.cursor = 'pointer';
        btn.style.transition = 'background-color 0.3s';

        // Prevent Enter key from triggering button
        nameInput.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') {
                console.log('Preventing Enter key trigger');
                e.preventDefault();
            }
        });

        // Detect if alias arrow sequence is already active
        function isSequenceActive(value) {
            const active = value.startsWith(prefix);
            console.log('Checking if sequence active:', value, 'Result:', active);
            return active;
        }

        // Color indicator
        function updateButtonVisual(active) {
            btn.innerHTML = active ? '↻' : '↺';
            btn.style.backgroundColor = active ? '#5cb85c' : '';
            btn.style.color = active ? 'white' : '';
            console.log('Button visual updated, active:', active);
        }

        // Initial visual state
        let currentName = nameInput.value || '';
        console.log('Initial name:', currentName);
        updateButtonVisual(isSequenceActive(currentName));

        btn.onclick = function () {
            let current = nameInput.value || '';
            console.log('Current name on click:', current);
            let cleanName = current.replace(new RegExp('^' + prefix.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'), ''), '').replace(/\?\?/g, '');
            console.log('Cleaned name:', cleanName);

            const shouldAdd = !isSequenceActive(current);
            console.log('Should add sequence:', shouldAdd);

            if (shouldAdd) {
                // Step 1: Set name with Pastebin sequence
                const tempName = prefix + cleanName;
                console.log('Step 1: Setting name with sequence:', tempName);
                setInputValue(nameInput, tempName);
                removeSteamErrorMessages();
                clickSaveButton();

                // Step 2: After delay, remove ?? and preserve sequence
                setTimeout(() => {
                    let currentAfterSave = nameInput.value || '';
                    console.log('Step 2: Current name after save:', currentAfterSave);
                    let finalName = currentAfterSave.replace(/\?\?/g, '');
                    console.log('Step 2: Final name (?? removed):', finalName);
                    setInputValue(nameInput, finalName);
                    removeSteamErrorMessages();
                    clickSaveButton();
                }, 2000);
            } else {
                // Remove sequence to revert to normal
                console.log('Removing sequence, setting name:', cleanName);
                setInputValue(nameInput, cleanName);
                removeSteamErrorMessages();
                clickSaveButton();
            }

            // Update visuals
            updateButtonVisual(shouldAdd);
        };

        wrapper.appendChild(btn);
        nameInput.parentElement.appendChild(wrapper);
        console.log('Toggle button inserted');
    }

    window.addEventListener('load', () => {
        console.log('Page loaded, initializing script');
        setTimeout(() => {
            fetchSequence((seq) => {
                console.log('Sequence received, codes:', Array.from(seq).map(c => c.charCodeAt(0).toString(16).padStart(4, '0')));
                insertStyledToggleButton(seq);
                removeSteamErrorMessages();
            });
        }, 1000);
    });
})();