$(document).ready(function() { // init privacy policy modal $('.footer p:nth-child(2)').html('All pastes are publicly accessible via the generated URL. View the Privacy Policy for more information.'); $('#privacy-link').click(function(e) { e.preventDefault(); var privacyModal = new bootstrap.Modal(document.getElementById('privacyPolicyModal')); privacyModal.show(); }); // DOM Elements const $editor = $('#editor'); const $viewer = $('#viewer'); const $lineNumbers = $('#line-numbers'); const $themeToggle = $('#theme-toggle'); const $sunIcon = $themeToggle.find('.sun-icon'); const $moonIcon = $themeToggle.find('.moon-icon'); const $saveBtn = $('#save-btn'); const $copyBtn = $('#copy-btn'); const $newBtn = $('#new-btn'); const $notification = $('#notification'); const $languageSelector = $('#language-selector'); const $editorContainer = $('.editor-container'); // Format JSON button const $formatJsonBtn = $('', { class: 'control-btn', id: 'format-json-btn', html: ` Format JSON `, css: { display: $languageSelector.val() === 'json' ? 'inline-flex' : 'none' } }); $('.controls').prepend($formatJsonBtn); // Initial theme setup function setInitialTheme() { const savedTheme = localStorage.getItem('theme'); if (savedTheme === 'dark' || (!savedTheme && window.matchMedia('(prefers-color-scheme: dark)').matches)) { $('html').attr('data-theme', 'dark'); $sunIcon.hide(); $moonIcon.show(); } else { $('html').attr('data-theme', 'light'); $sunIcon.show(); $moonIcon.hide(); } } // Line numbers function updateLineNumbers() { const lineCount = $editor.val().split('\n').length; $lineNumbers.empty(); for (let i = 1; i <= lineCount; i++) { $('').text(i).appendTo($lineNumbers); } } // Auto-detect JSON content function detectContentType(content) { try { JSON.parse(content); return 'json'; } catch (e) { return $languageSelector.val() || 'javascript'; } } // Show notification function showNotification(message, type = 'success') { $notification.text(message).addClass('show'); if (type === 'error') { $notification.css({ 'background-color': 'var(--error-bg, #fa5252)', 'color': 'var(--error-text, #fff5f5)' }); } else { $notification.css({ 'background-color': 'var(--notification-bg)', 'color': 'var(--notification-text)' }); } setTimeout(() => { $notification.removeClass('show'); }, 3000); } // Load paste on page load async function loadPasteFromUrl() { $viewer.text("Loading..."); const urlParams = new URLSearchParams(window.location.search); const pasteId = urlParams.get('id'); if (pasteId) { try { const response = await fetch(`/api/pastes/${pasteId}`); if (response.ok) { $('pre').css('width', '100%'); // Set pre width const content = await response.text(); $editor.val(content); updateLineNumbers(); // Auto-detect JSON content if (content.trim().startsWith('{') || content.trim().startsWith('[')) { try { JSON.parse(content); $languageSelector.val('json'); } catch (e) { // Not valid JSON, check URL hash or use default const hash = window.location.hash.substring(1); if (hash && $(`option[value="${hash}"]`).length) { $languageSelector.val(hash); } } } else { // Check for language in URL hash const hash = window.location.hash.substring(1); if (hash && $(`option[value="${hash}"]`).length) { $languageSelector.val(hash); } } // Update viewer $viewer.text(content); $viewer.attr('class', `language-${$languageSelector.val()}`); hljs.highlightElement($viewer[0]); // Switch to viewer mode $editorContainer.addClass('viewer-active'); } else { showNotification('Paste not found or has expired.', 'error'); } } catch (error) { showNotification('Failed to load paste.', 'error'); console.error('Load error:', error); } } } // Initialize setInitialTheme(); updateLineNumbers(); loadPasteFromUrl(); hljs.highlightAll(); // Theme toggle $themeToggle.on('click', () => { if ($('html').attr('data-theme') === 'dark') { $('html').attr('data-theme', 'light'); localStorage.setItem('theme', 'light'); $sunIcon.show(); $moonIcon.hide(); } else { $('html').attr('data-theme', 'dark'); localStorage.setItem('theme', 'dark'); $sunIcon.hide(); $moonIcon.show(); } }); // Handle editor input $editor.on('input', () => { updateLineNumbers(); // Update viewer content with syntax highlighting $viewer.text($editor.val()); // Try to auto-detect JSON if content changes significantly if ($editor.val().trim().startsWith('{') || $editor.val().trim().startsWith('[')) { const detectedLanguage = detectContentType($editor.val()); if (detectedLanguage === 'json') { $languageSelector.val('json'); $formatJsonBtn.show(); } } $viewer.attr('class', `language-${$languageSelector.val()}`); hljs.highlightElement($viewer[0]); }); // Handle tab key in editor $editor.on('keydown', (e) => { if (e.key === 'Tab') { e.preventDefault(); const start = e.target.selectionStart; const end = e.target.selectionEnd; // Insert tab at cursor position const value = $editor.val(); $editor.val(value.substring(0, start) + ' ' + value.substring(end)); // Move cursor after the inserted tab e.target.selectionStart = e.target.selectionEnd = start + 4; // Update line numbers updateLineNumbers(); } }); // Save paste $saveBtn.on('click', async () => { if (!$editor.val().trim()) { showNotification('Cannot save empty paste!', 'error'); return; } try { const response = await fetch('/api/pastes', { method: 'POST', headers: { 'Content-Type': 'text/plain' }, body: $editor.val() }); const data = await response.json(); if (response.ok) { // Add language as a hash parameter with query param format const url = `${window.location.origin}/?id=${data.id}#${$languageSelector.val()}`; navigator.clipboard.writeText(url); showNotification('Paste saved! URL copied to clipboard.', 'success'); // Update URL without reloading the page window.history.pushState({}, '', `/?id=${data.id}#${$languageSelector.val()}`); // Switch to viewer mode $('pre').css('width', '100%'); // Set pre width $editorContainer.addClass('viewer-active'); } else { showNotification(`Error: ${data.error}`, 'error'); } } catch (error) { showNotification('Failed to save paste. Please try again.', 'error'); console.error('Save error:', error); } }); // Copy to clipboard $copyBtn.on('click', () => { if ($editorContainer.hasClass('viewer-active')) { navigator.clipboard.writeText($viewer.text()); } else { navigator.clipboard.writeText($editor.val()); } showNotification('Content copied to clipboard!', 'success'); }); // Create new paste $newBtn.on('click', () => { $editor.val(''); updateLineNumbers(); window.history.pushState({}, '', '/'); // Reset URL to homepage $editorContainer.removeClass('viewer-active'); $languageSelector.val('javascript'); $formatJsonBtn.hide(); // Hide JSON button when switching to new paste with JavaScript }); // Language change $languageSelector.on('change', () => { // Toggle Format JSON button visibility based on language selection $formatJsonBtn.toggle($languageSelector.val() === 'json'); if ($editorContainer.hasClass('viewer-active')) { $viewer.attr('class', `language-${$languageSelector.val()}`); hljs.highlightElement($viewer[0]); // Update URL hash const urlParams = new URLSearchParams(window.location.search); const pasteId = urlParams.get('id'); if (pasteId) { window.history.replaceState({}, '', `/?id=${pasteId}#${$languageSelector.val()}`); } } }); // Format JSON handler $formatJsonBtn.on('click', () => { try { const parsed = JSON.parse($editor.val()); const formatted = JSON.stringify(parsed, null, 2); $editor.val(formatted); $viewer.text(formatted); hljs.highlightElement($viewer[0]); updateLineNumbers(); showNotification('JSON formatted successfully!', 'success'); } catch (e) { showNotification('Invalid JSON: ' + e.message, 'error'); } }); // Sync scroll between line numbers and editor $editor.on('scroll', () => { $lineNumbers.scrollTop($editor.scrollTop()); }); $viewer.on('scroll', () => { $lineNumbers.scrollTop($viewer.scrollTop()); }); });