tagliatelle

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

notes.js (8117B)


      1 // notes.js - Notes editor JavaScript
      2 
      3 const editor = document.getElementById('editor');
      4 const preview = document.getElementById('preview');
      5 const searchInput = document.getElementById('search-input');
      6 
      7 let originalContent = editor.value;
      8 let currentContent = editor.value;
      9 
     10 // Initialize preview with clickable links
     11 document.addEventListener('DOMContentLoaded', () => {
     12     updatePreview(currentContent);
     13 });
     14 
     15 // Auto-update preview on typing (debounced)
     16 let typingTimer;
     17 editor.addEventListener('input', () => {
     18     clearTimeout(typingTimer);
     19     typingTimer = setTimeout(() => {
     20         currentContent = editor.value;
     21         updatePreview(currentContent);
     22     }, 500);
     23 });
     24 
     25 // Search functionality
     26 searchInput.addEventListener('input', () => {
     27     const searchTerm = searchInput.value.toLowerCase();
     28     if (searchTerm === '') {
     29         updatePreview(currentContent);
     30         return;
     31     }
     32 
     33     const lines = currentContent.split('\n');
     34     const filtered = lines.filter(line =>
     35         line.toLowerCase().includes(searchTerm)
     36     );
     37     updatePreviewWithLinks(filtered.join('\n'));
     38 });
     39 
     40 // Convert URLs to clickable links and update preview
     41 function updatePreview(content) {
     42     updatePreviewWithLinks(content);
     43 }
     44 
     45 function updatePreviewWithLinks(content) {
     46     // Clear preview
     47     preview.innerHTML = '';
     48 
     49     if (!content) return;
     50 
     51     const lines = content.split('\n');
     52     const urlRegex = /(https?:\/\/[^\s]+)/g;
     53 
     54     lines.forEach((line, index) => {
     55         const lineDiv = document.createElement('div');
     56         lineDiv.style.marginBottom = '0';
     57 
     58         // Check if line contains URLs
     59         if (urlRegex.test(line)) {
     60             // Reset regex lastIndex
     61             urlRegex.lastIndex = 0;
     62 
     63             let lastIndex = 0;
     64             let match;
     65 
     66             while ((match = urlRegex.exec(line)) !== null) {
     67                 // Add text before URL
     68                 if (match.index > lastIndex) {
     69                     const textNode = document.createTextNode(line.substring(lastIndex, match.index));
     70                     lineDiv.appendChild(textNode);
     71                 }
     72 
     73                 // Add clickable link
     74                 const link = document.createElement('a');
     75                 link.href = match[0];
     76                 link.textContent = match[0];
     77                 link.target = '_blank';
     78                 link.rel = 'noopener noreferrer';
     79                 // link.style.color = '#2563eb';
     80                 link.style.textDecoration = 'underline';
     81                 lineDiv.appendChild(link);
     82 
     83                 lastIndex = match.index + match[0].length;
     84             }
     85 
     86             // Add remaining text after last URL
     87             if (lastIndex < line.length) {
     88                 const textNode = document.createTextNode(line.substring(lastIndex));
     89                 lineDiv.appendChild(textNode);
     90             }
     91         } else {
     92             // No URLs, just add plain text
     93             lineDiv.textContent = line;
     94         }
     95 
     96         preview.appendChild(lineDiv);
     97     });
     98 }
     99 
    100 function updateStats(stats) {
    101     document.getElementById('total-lines').textContent = stats.total_lines || 0;
    102     document.getElementById('categorized-lines').textContent = stats.categorized_lines || 0;
    103     document.getElementById('uncategorized-lines').textContent = stats.uncategorized || 0;
    104     document.getElementById('unique-categories').textContent = stats.unique_categories || 0;
    105 }
    106 
    107 function showMessage(text, type = 'success') {
    108     const msg = document.getElementById('message');
    109     msg.textContent = text;
    110     msg.className = `message ${type} show`;
    111     setTimeout(() => {
    112         msg.classList.remove('show');
    113     }, 3000);
    114 }
    115 
    116 async function saveNotes() {
    117     const content = editor.value;
    118 
    119     try {
    120         const response = await fetch('/notes/save', {
    121             method: 'POST',
    122             headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    123             body: `content=${encodeURIComponent(content)}`
    124         });
    125 
    126         const result = await response.json();
    127 
    128         if (result.success) {
    129             showMessage('Notes saved successfully!', 'success');
    130             originalContent = content;
    131             // Reload to show sorted/deduped version
    132             setTimeout(() => location.reload(), 1000);
    133         } else {
    134             showMessage('Failed to save notes', 'error');
    135         }
    136     } catch (error) {
    137         showMessage('Error: ' + error.message, 'error');
    138     }
    139 }
    140 
    141 async function previewProcessing() {
    142     const content = editor.value;
    143 
    144     try {
    145         const response = await fetch('/notes/preview', {
    146             method: 'POST',
    147             headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    148             body: `content=${encodeURIComponent(content)}`
    149         });
    150 
    151         const result = await response.json();
    152 
    153         if (result.success) {
    154             editor.value = result.content;
    155             currentContent = result.content;
    156             updatePreview(result.content);
    157             updateStats(result.stats);
    158             showMessage(`Processed: ${result.lineCount} lines after sort & dedupe`, 'success');
    159         }
    160     } catch (error) {
    161         showMessage('Error: ' + error.message, 'error');
    162     }
    163 }
    164 
    165 async function applySedRule(ruleIndex) {
    166     const content = editor.value;
    167 
    168     try {
    169         const response = await fetch('/notes/apply-sed', {
    170             method: 'POST',
    171             headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    172             body: `content=${encodeURIComponent(content)}&rule_index=${ruleIndex}`
    173         });
    174 
    175         // Get the response text first to see what we're actually receiving
    176         const responseText = await response.text();
    177         console.log('Raw response:', responseText);
    178         console.log('Response status:', response.status);
    179         console.log('Content-Type:', response.headers.get('content-type'));
    180 
    181         // Try to parse as JSON
    182         let result;
    183         try {
    184             result = JSON.parse(responseText);
    185         } catch (parseError) {
    186             console.error('JSON parse error:', parseError);
    187             console.error('Response was:', responseText.substring(0, 200));
    188             showMessage('Server returned invalid response. Check console for details.', 'error');
    189             return;
    190         }
    191 
    192         if (result.success) {
    193             editor.value = result.content;
    194             currentContent = result.content;
    195             updatePreview(result.content);
    196             updateStats(result.stats);
    197             showMessage('Sed rule applied successfully!', 'success');
    198         } else {
    199             showMessage(result.error || 'Sed rule failed', 'error');
    200         }
    201     } catch (error) {
    202         console.error('Fetch error:', error);
    203         showMessage('Error: ' + error.message, 'error');
    204     }
    205 }
    206 
    207 function filterByCategory() {
    208     const category = document.getElementById('category-filter').value;
    209     if (!category) {
    210         updatePreview(currentContent);
    211         return;
    212     }
    213 
    214     const lines = currentContent.split('\n');
    215 
    216     // Handle uncategorized filter
    217     if (category === '__uncategorized__') {
    218         const filtered = lines.filter(line => {
    219             const trimmed = line.trim();
    220             if (!trimmed) return false;
    221             // Line is uncategorized if it doesn't contain '>' or '>' is not a separator
    222             return !trimmed.includes('>') || trimmed.indexOf('>') === trimmed.length - 1;
    223         });
    224         updatePreviewWithLinks(filtered.join('\n'));
    225         return;
    226     }
    227 
    228     // Handle normal category filter
    229     const filtered = lines.filter(line => {
    230         if (line.includes('>')) {
    231             const cat = line.split('>')[0].trim();
    232             return cat === category;
    233         }
    234         return false;
    235     });
    236 
    237     updatePreviewWithLinks(filtered.join('\n'));
    238 }
    239 
    240 function clearFilters() {
    241     searchInput.value = '';
    242     document.getElementById('category-filter').value = '';
    243     updatePreview(currentContent);
    244 }
    245 
    246 function exportNotes() {
    247     window.location.href = '/notes/export';
    248 }
    249 
    250 // Warn on unsaved changes
    251 window.addEventListener('beforeunload', (e) => {
    252     if (editor.value !== originalContent) {
    253         e.preventDefault();
    254         e.returnValue = '';
    255     }
    256 });