document.addEventListener('DOMContentLoaded', () => { const editor = ace.edit("editor"); editor.session.setMode("ace/mode/lua"); loadSettings(); const obfuscateBtn = document.getElementById('obfuscate-btn'); const saveBtn = document.getElementById('save-btn'); const loadBtn = document.getElementById('load-btn'); const clearOutputBtn = document.getElementById('clear-output-btn'); const runBtn = document.getElementById('run-btn'); const runOutput = document.getElementById('run-output'); const obfuscatedOutput = document.getElementById('obfuscated-output'); const loader = document.getElementById('loader'); const versionSpan = document.getElementById('luau-version'); const examplesMenu = document.getElementById('examples-menu'); const saveStatus = document.getElementById('save-status'); const LS_CODE_KEY = 'luau-saved-code'; const LS_SETTINGS_KEY = 'luau-editor-settings'; const editorThemes = [ 'ambiance', 'chaos', 'chrome', 'clouds', 'cobalt', 'crimson_editor', 'dawn', 'dracula', 'dreamweaver', 'eclipse', 'github', 'gob', 'gruvbox', 'idle_fingers', 'iplastic', 'katzenmilch', 'kr_theme', 'kuroir', 'merbivore', 'merbivore_soft', 'mono_industrial', 'monokai', 'nord_dark', 'pastel_on_dark', 'solarized_dark', 'solarized_light', 'sqlserver', 'terminal', 'textmate', 'tomorrow', 'tomorrow_night', 'tomorrow_night_blue', 'tomorrow_night_bright', 'tomorrow_night_eighties', 'twilight', 'vibrant_ink', 'xcode' ]; let editorSettings = {}; // --- Editor and Snippet Setup --- const examples = [ { title: 'Hello World', code: 'print("Hello, world!")' }, { title: 'Variables & Types', code: '-- Luau has types that you can optionally add!\n\n' + 'local message: string = "Hello, Luau!"\n' + 'local version: number = 1.0\n' + 'local isAwesome: boolean = true\n\n' + 'print(message, type(message))\n' + 'print("Version:", version, type(version))\n' + 'print("Is it awesome?", isAwesome, type(isAwesome))' }, { title: 'Loops', code: 'print("Counting from 1 to 5:")\n' + 'for i = 1, 5 do\n' + ' print("Number:", i)\n' + 'end\n\n' + 'local t = {"apple", "banana", "cherry"}\n' + 'print("\nIterating through a table:")\n' + 'for _, fruit in ipairs(t) do\n' + ' print("- " .. fruit)\n' + 'end' }, { title: 'Functions', code: 'function greet(name: string): string\n' + ' return "Hello, " .. name .. "!"\n' + 'end\n\n' + 'local message = greet("User")\n' + 'print(message)\n\n' + '-- Function with a type alias\n' + 'type Player = { name: string, score: number }\n\n' + 'local function displayPlayer(p: Player)\n' + ' print("Player: " .. p.name .. ", Score: " .. p.score)\n' + 'end\n\n' + 'local player1: Player = { name = "Alice", score = 100 }\n' + 'displayPlayer(player1)' } ]; examples.forEach((example, index) => { const li = document.createElement('li'); const a = document.createElement('a'); a.classList.add('dropdown-item'); a.href = '#'; a.textContent = example.title; a.dataset.index = index; li.appendChild(a); examplesMenu.appendChild(li); }); examplesMenu.addEventListener('click', (e) => { if (e.target.tagName === 'A') { e.preventDefault(); const index = e.target.dataset.index; if (index) { editor.setValue(examples[index].code, 1); // 1 moves cursor to the end } } }); // --- Settings Modal Logic --- const themeSelect = document.getElementById('theme-select'); const fontSizeInput = document.getElementById('font-size-input'); const softWrapCheck = document.getElementById('soft-wrap-check'); function applySettings() { editor.setTheme("ace/theme/" + editorSettings.theme); editor.setFontSize(editorSettings.fontSize); editor.session.setUseWrapMode(editorSettings.softWrap); // Update UI controls to reflect current settings themeSelect.value = editorSettings.theme; fontSizeInput.value = editorSettings.fontSize; softWrapCheck.checked = editorSettings.softWrap; } function saveSettings() { try { localStorage.setItem(LS_SETTINGS_KEY, JSON.stringify(editorSettings)); } catch (e) { console.error("Failed to save settings:", e); } } function loadSettings() { const defaults = { theme: 'tomorrow_night', fontSize: 14, softWrap: false }; try { const saved = localStorage.getItem(LS_SETTINGS_KEY); if (saved) { editorSettings = Object.assign({}, defaults, JSON.parse(saved)); } else { editorSettings = defaults; } } catch (e) { console.error("Failed to load settings:", e); editorSettings = defaults; } applySettings(); } // Populate theme dropdown editorThemes.forEach(theme => { const option = document.createElement('option'); option.value = theme; option.textContent = theme.split('_').map(w => w[0].toUpperCase() + w.substr(1)).join(' '); themeSelect.appendChild(option); }); // Settings event listeners themeSelect.addEventListener('change', () => { editorSettings.theme = themeSelect.value; applySettings(); saveSettings(); }); fontSizeInput.addEventListener('input', () => { editorSettings.fontSize = parseInt(fontSizeInput.value, 10); applySettings(); saveSettings(); }); softWrapCheck.addEventListener('change', () => { editorSettings.softWrap = softWrapCheck.checked; applySettings(); saveSettings(); }); // --- Local Storage and UI Logic --- function showSaveStatus(message, duration = 2000) { saveStatus.textContent = message; setTimeout(() => { saveStatus.textContent = ''; }, duration); } saveBtn.addEventListener('click', () => { try { localStorage.setItem(LS_CODE_KEY, editor.getValue()); showSaveStatus('Code saved!'); } catch (e) { console.error("Failed to save to localStorage:", e); showSaveStatus('Error saving!'); } }); loadBtn.addEventListener('click', () => { const savedCode = localStorage.getItem(LS_CODE_KEY); if (savedCode) { editor.setValue(savedCode, 1); showSaveStatus('Code restored!'); } else { showSaveStatus('No code saved yet.'); } }); clearOutputBtn.addEventListener('click', () => { obfuscatedOutput.textContent = ''; }); // --- Load initial code --- const savedCode = localStorage.getItem(LS_CODE_KEY); if (savedCode) { editor.setValue(savedCode, 1); } else { // Load default example if nothing is saved editor.setValue(examples[0].code, 1); } versionSpan.textContent = `Luau Obfuscator`; obfuscateBtn.addEventListener('click', async () => { const code = editor.getValue(); obfuscatedOutput.textContent = ''; loader.style.display = 'block'; obfuscateBtn.disabled = true; // Give the UI a moment to update await new Promise(resolve => setTimeout(resolve, 50)); try { const formData = new FormData(); formData.append('code', code); const response = await fetch('obfuscator.php', { method: 'POST', body: formData }); if (response.ok) { const result = await response.text(); obfuscatedOutput.textContent = result; } else { obfuscatedOutput.textContent = `Error: ${response.statusText}`; } } catch (err) { obfuscatedOutput.textContent = `Error: ${err.message}`; console.error(err); } finally { loader.style.display = 'none'; obfuscateBtn.disabled = false; } }); // --- Split View --- let split; function setupSplit() { if (window.innerWidth < 992) { if (split) { split.destroy(); split = null; } } else { if (!split) { split = Split(['#split-0', '#split-1'], { sizes: [50, 50], minSize: 200, gutterSize: 8, onDrag: function() { editor.resize(); } }); } } } window.addEventListener('resize', setupSplit); setupSplit(); // Initial call runBtn.addEventListener('click', async () => { const code = editor.getValue(); runOutput.textContent = ''; loader.style.display = 'block'; runBtn.disabled = true; // Give the UI a moment to update await new Promise(resolve => setTimeout(resolve, 50)); try { const formData = new FormData(); formData.append('code', code); const response = await fetch('run.php', { method: 'POST', body: formData }); if (response.ok) { const result = await response.text(); runOutput.textContent = result; } else { runOutput.textContent = `Error: ${response.statusText}`; } } catch (err) { runOutput.textContent = `Error: ${err.message}`; console.error(err); } finally { loader.style.display = 'none'; runBtn.disabled = false; } }); });