292 lines
10 KiB
JavaScript
292 lines
10 KiB
JavaScript
$(document).ready(function() {
|
|
// 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 = $('<button>', {
|
|
class: 'control-btn',
|
|
id: 'format-json-btn',
|
|
html: `
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 3H3C1.89 3 1 3.89 1 5V19C1 20.11 1.89 21 3 21H21C22.11 21 23 20.11 23 19V5C23 3.89 22.11 3 21 3Z"></path><path d="M7 8L12 13L17 8"></path></svg>
|
|
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++) {
|
|
$('<div>').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() {
|
|
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) {
|
|
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
|
|
$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());
|
|
});
|
|
}); |