| |
| |
|
|
| |
| |
| |
| |
| |
| |
| function execFormat(command, value, keepSelection) { |
| pushUndoState(); |
| document.execCommand(command, false, value !== undefined ? value : null); |
| const editor = getEditorElement(); |
| if (editor) editor.focus(); |
| |
| |
| if (!keepSelection) { |
| const sel = window.getSelection(); |
| if (sel && sel.rangeCount > 0 && !sel.isCollapsed) { |
| sel.collapseToEnd(); |
| } |
| } |
| |
| updateFormatState(); |
| } |
|
|
| |
| function formatBold() { execFormat('bold'); } |
| function formatItalic() { execFormat('italic'); } |
| function formatUnderline() { execFormat('underline'); } |
| function formatStrikethrough() { execFormat('strikethrough'); } |
|
|
| |
| function formatUndo() { editorUndo(); } |
| function formatRedo() { editorRedo(); } |
|
|
| |
| function formatAlignRight() { execFormat('justifyRight'); } |
| function formatAlignCenter() { execFormat('justifyCenter'); } |
| function formatAlignLeft() { execFormat('justifyLeft'); } |
|
|
| |
| function setDirection(dir) { |
| const editor = getEditorElement(); |
| if (!editor) return; |
| const sel = window.getSelection(); |
| if (sel && sel.rangeCount > 0) { |
| const range = sel.getRangeAt(0); |
| let block = range.startContainer; |
| if (block.nodeType === 3) block = block.parentNode; |
| while (block && block !== editor && !['DIV','P','H1','H2','H3','H4','H5','H6','LI','BLOCKQUOTE'].includes(block.tagName)) { |
| block = block.parentNode; |
| } |
| if (block && block !== editor) { |
| block.setAttribute('dir', dir); |
| block.style.direction = dir; |
| block.style.textAlign = dir === 'rtl' ? 'right' : 'left'; |
| } else { |
| editor.setAttribute('dir', dir); |
| editor.style.direction = dir; |
| } |
| } |
| updateFormatState(); |
| } |
|
|
| |
| function insertLink() { |
| const sel = window.getSelection(); |
| if (!sel || !sel.rangeCount) return; |
| const selectedText = sel.toString(); |
| const url = prompt('أدخل الرابط (URL):', 'https://'); |
| if (!url || url === 'https://') return; |
| if (selectedText) { |
| execFormat('createLink', url); |
| } else { |
| const link = document.createElement('a'); |
| link.href = url; |
| link.textContent = url; |
| link.target = '_blank'; |
| const range = sel.getRangeAt(0); |
| range.insertNode(link); |
| range.setStartAfter(link); |
| range.collapse(true); |
| sel.removeAllRanges(); |
| sel.addRange(range); |
| } |
| } |
|
|
| |
| var _currentLineHeight = 1.8; |
| function cycleLineHeight() { |
| const editor = getEditorElement(); |
| if (!editor) return; |
| const heights = [1.5, 2.0, 2.5, 1.8]; |
| const idx = heights.indexOf(_currentLineHeight); |
| _currentLineHeight = heights[(idx + 1) % heights.length]; |
| editor.style.lineHeight = _currentLineHeight; |
| |
| const btn = document.getElementById('fmt-line-height'); |
| if (btn) { |
| btn.setAttribute('data-tooltip', 'ارتفاع السطر: ' + _currentLineHeight); |
| } |
| } |
|
|
|
|
| |
| function formatFont(fontName) { |
| execFormat('fontName', fontName); |
| |
| const label = document.getElementById('fmt-font-label'); |
| if (label) label.textContent = fontName; |
| closeAllFmtDropdowns(); |
| } |
|
|
| |
| function formatFontSize(size) { |
| const sel = window.getSelection(); |
| if (!sel.rangeCount) return; |
|
|
| const range = sel.getRangeAt(0); |
| if (range.collapsed) { |
| |
| |
| const span = document.createElement('span'); |
| span.style.fontSize = size; |
| span.textContent = '\u200B'; |
| range.insertNode(span); |
| |
| const newRange = document.createRange(); |
| newRange.setStartAfter(span); |
| newRange.collapse(true); |
| sel.removeAllRanges(); |
| sel.addRange(newRange); |
| } else { |
| |
| const span = document.createElement('span'); |
| span.style.fontSize = size; |
| try { |
| range.surroundContents(span); |
| } catch (e) { |
| |
| execFormat('fontSize', '4'); |
| const editor = getEditorElement(); |
| if (editor) { |
| editor.querySelectorAll('font[size="4"]').forEach(f => { |
| const s = document.createElement('span'); |
| s.style.fontSize = size; |
| s.innerHTML = f.innerHTML; |
| f.replaceWith(s); |
| }); |
| } |
| } |
| } |
|
|
| |
| const label = document.getElementById('fmt-size-label'); |
| if (label) label.textContent = parseInt(size); |
| |
| |
| document.querySelectorAll('#fmt-size-menu .fmt-dropdown__item').forEach(item => { |
| item.classList.toggle('fmt-dropdown__item--active', item.dataset.size === size); |
| }); |
|
|
| closeAllFmtDropdowns(); |
| const editor = getEditorElement(); |
| if (editor) editor.focus(); |
| updateFormatState(); |
| } |
|
|
| |
| |
| |
| function updateFormatState() { |
| const btnMap = { |
| 'fmt-bold': 'bold', |
| 'fmt-italic': 'italic', |
| 'fmt-underline': 'underline', |
| 'fmt-strikethrough': 'strikeThrough', |
| }; |
|
|
| Object.entries(btnMap).forEach(([id, command]) => { |
| const btn = document.getElementById(id); |
| if (btn) { |
| btn.classList.toggle('fmt-active', document.queryCommandState(command)); |
| } |
| }); |
|
|
| |
| const alignMap = { |
| 'fmt-align-right': 'justifyRight', |
| 'fmt-align-center': 'justifyCenter', |
| 'fmt-align-left': 'justifyLeft', |
| }; |
| Object.entries(alignMap).forEach(([id, command]) => { |
| const btn = document.getElementById(id); |
| if (btn) { |
| btn.classList.toggle('fmt-active', document.queryCommandState(command)); |
| } |
| }); |
|
|
| |
| const listMap = { |
| 'fmt-ul': 'insertUnorderedList', |
| 'fmt-ol': 'insertOrderedList', |
| }; |
| Object.entries(listMap).forEach(([id, command]) => { |
| const btn = document.getElementById(id); |
| if (btn) { |
| btn.classList.toggle('fmt-active', document.queryCommandState(command)); |
| } |
| }); |
| } |
|
|
| |
| |
| |
| function closeAllFmtDropdowns() { |
| document.querySelectorAll('.fmt-dropdown').forEach(d => d.classList.remove('open')); |
| } |
|
|
| |
| |
| |
| function toggleFmtDropdown(wrapperId) { |
| const wrap = document.getElementById(wrapperId); |
| if (!wrap) return; |
| const isOpen = wrap.classList.contains('open'); |
| closeAllFmtDropdowns(); |
| if (!isOpen) wrap.classList.add('open'); |
| } |
|
|
| |
| |
| |
| function initFormatToolbar() { |
| const editor = getEditorElement(); |
| if (!editor) return; |
|
|
| |
| document.addEventListener('selectionchange', () => { |
| if (editor.contains(document.activeElement) || editor === document.activeElement) { |
| updateFormatState(); |
| } |
| }); |
|
|
| |
| const fontTrigger = document.getElementById('fmt-font-trigger'); |
| if (fontTrigger) { |
| fontTrigger.addEventListener('click', (e) => { |
| e.stopPropagation(); |
| toggleFmtDropdown('fmt-font-wrap'); |
| }); |
| } |
|
|
| |
| document.querySelectorAll('#fmt-font-menu .fmt-dropdown__item').forEach(item => { |
| item.addEventListener('click', () => { |
| formatFont(item.dataset.font); |
| }); |
| }); |
|
|
| |
| const sizeTrigger = document.getElementById('fmt-size-trigger'); |
| if (sizeTrigger) { |
| sizeTrigger.addEventListener('click', (e) => { |
| e.stopPropagation(); |
| toggleFmtDropdown('fmt-size-wrap'); |
| }); |
| } |
|
|
| |
| document.querySelectorAll('#fmt-size-menu .fmt-dropdown__item').forEach(item => { |
| item.addEventListener('click', () => { |
| formatFontSize(item.dataset.size); |
| }); |
| }); |
|
|
| |
| document.addEventListener('click', (e) => { |
| if (!e.target.closest('.fmt-dropdown')) { |
| closeAllFmtDropdowns(); |
| } |
| }); |
|
|
| |
| document.addEventListener('keydown', (e) => { |
| if (e.key === 'Escape') closeAllFmtDropdowns(); |
|
|
| |
| if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { |
| const openDropdown = document.querySelector('.fmt-dropdown.open .fmt-dropdown__menu'); |
| if (!openDropdown) return; |
| e.preventDefault(); |
| const items = Array.from(openDropdown.querySelectorAll('.fmt-dropdown__item')); |
| if (!items.length) return; |
| const focused = document.activeElement; |
| const idx = items.indexOf(focused); |
| let next; |
| if (e.key === 'ArrowDown') { |
| next = idx < items.length - 1 ? idx + 1 : 0; |
| } else { |
| next = idx > 0 ? idx - 1 : items.length - 1; |
| } |
| items[next].focus(); |
| } |
| }); |
|
|
| |
| initColorPicker('fmt-textcolor', 'foreColor', 'fmt-textcolor-bar'); |
| initColorPicker('fmt-highlight', 'hiliteColor', 'fmt-highlight-bar'); |
| } |
|
|
| |
| const COLOR_PALETTE = [ |
| '#ECEEF2', '#E88A8A', '#E4B35A', '#6BC98A', '#6BA3E0', '#A594E8', |
| '#F5F5F5', '#FF6B6B', '#FFD93D', '#51CF66', '#339AF0', '#845EF7', |
| '#ADB5BD', '#C92A2A', '#F08C00', '#2B8A3E', '#1864AB', '#5F3DC4', |
| '#495057', '#862E2E', '#B7791F', '#1B5E20', '#0D47A1', '#311B92', |
| '#212529', '#000000', '#5D4037', '#004D40', '#1A237E', '#4A148C', |
| ]; |
|
|
| function initColorPicker(prefix, command, barId) { |
| const trigger = document.getElementById(prefix + '-trigger'); |
| const wrap = document.getElementById(prefix + '-wrap'); |
| const grid = document.getElementById(prefix + '-grid'); |
| if (!trigger || !wrap || !grid) return; |
|
|
| |
| const resetSwatch = document.createElement('button'); |
| resetSwatch.type = 'button'; |
| resetSwatch.className = 'fmt-color-swatch fmt-color-swatch--reset'; |
| resetSwatch.title = '\u0625\u0639\u0627\u062f\u0629 \u0627\u0644\u0627\u0641\u062a\u0631\u0627\u0636\u064a'; |
| resetSwatch.textContent = '\u00d7'; |
| resetSwatch.addEventListener('click', () => { |
| document.execCommand('removeFormat', false, null); |
| const bar = document.getElementById(barId); |
| if (bar) bar.style.background = command === 'foreColor' ? '#ECEEF2' : 'transparent'; |
| closeAllFmtDropdowns(); |
| const editor = getEditorElement(); |
| if (editor) editor.focus(); |
| }); |
| grid.appendChild(resetSwatch); |
|
|
| COLOR_PALETTE.forEach(color => { |
| const swatch = document.createElement('button'); |
| swatch.type = 'button'; |
| swatch.className = 'fmt-color-swatch'; |
| swatch.style.background = color; |
| swatch.title = color; |
| swatch.addEventListener('click', () => { |
| document.execCommand(command, false, color); |
| const bar = document.getElementById(barId); |
| if (bar) bar.style.background = color; |
| closeAllFmtDropdowns(); |
| const editor = getEditorElement(); |
| if (editor) editor.focus(); |
| }); |
| grid.appendChild(swatch); |
| }); |
|
|
| |
| trigger.addEventListener('click', (e) => { |
| e.stopPropagation(); |
| toggleFmtDropdown(prefix + '-wrap'); |
| }); |
| } |
|
|
| |
| function updateEnhancedStats() { |
| const text = getEditorText(); |
| const charCount = text.length; |
| |
| |
| const words = text.trim().split(/\s+/).filter(w => w.length > 0).length; |
| let sentences = 0; |
| if (text.trim().length > 0) { |
| |
| sentences = text.split(/[.!?؟\n]+/).filter(s => s.trim().length > 2).length; |
| if (sentences === 0) sentences = 1; |
| } |
| |
| |
| const readingTimeMinutes = words === 0 ? 0 : Math.max(1, Math.round(words / 180)); |
|
|
| const charEl = document.getElementById('char-count'); |
| const sentEl = document.getElementById('sentence-count'); |
| const readEl = document.getElementById('reading-time'); |
|
|
| if (charEl) charEl.textContent = charCount.toLocaleString('ar-EG'); |
| if (sentEl) sentEl.textContent = sentences.toLocaleString('ar-EG'); |
| if (readEl) readEl.textContent = readingTimeMinutes.toLocaleString('ar-EG'); |
| } |
|
|
| |
| function updateSummaryStats(summaryText) { |
| const originalText = getEditorText(); |
| const summaryWords = summaryText.trim().split(/\s+/).filter(w => w.length > 0).length; |
| const originalWords = originalText.trim().split(/\s+/).filter(w => w.length > 0).length; |
| const compression = originalWords > 0 ? Math.round((1 - summaryWords / originalWords) * 100) : 0; |
|
|
| const statsEl = document.getElementById('summary-stats'); |
| const wordCountEl = document.getElementById('summary-word-count'); |
| const compressionEl = document.getElementById('summary-compression'); |
|
|
| if (statsEl) statsEl.style.display = 'flex'; |
| if (wordCountEl) wordCountEl.textContent = summaryWords; |
| if (compressionEl) compressionEl.textContent = compression + '%'; |
| } |
|
|
| |
| window._summaryMode = 'paragraph'; |
|
|
| function setSummaryMode(mode) { |
| window._summaryMode = mode; |
| document.querySelectorAll('.summary-mode-btn').forEach(btn => { |
| btn.classList.toggle('active', btn.id === 'summary-mode-' + mode); |
| }); |
| } |
|
|
| |
| function renderEmptyState(container, icon, title, desc) { |
| if (!container) return; |
| container.innerHTML = ` |
| <div class="empty-state"> |
| <div class="empty-state__icon">${icon}</div> |
| <div class="empty-state__title">${title}</div> |
| <div class="empty-state__desc">${desc}</div> |
| </div> |
| `; |
| } |
|
|
| |
| function initDocSearch() { |
| const searchInput = document.getElementById('docs-search-input'); |
| if (!searchInput) return; |
|
|
| searchInput.addEventListener('input', () => { |
| const query = searchInput.value.trim().toLowerCase(); |
| const items = document.querySelectorAll('.doc-list-item'); |
| items.forEach(item => { |
| const title = (item.querySelector('.doc-list-item__title')?.textContent || '').toLowerCase(); |
| item.style.display = title.includes(query) || !query ? '' : 'none'; |
| }); |
| }); |
| } |
|
|