2025-04-12 01:20:53 +02:00

308 lines
11 KiB
JavaScript

$(document).ready(function() {
// init privacy policy modal
$('.footer p:nth-child(2)').html('All pastes are publicly accessible via the generated URL. <a href="#" id="privacy-link">View the Privacy Policy</a> 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 = $('<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() {
$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());
});
});