function getTiptap() { return window.Tiptap || {}; } function buildTpToolbar(mini, isOption) { var btns; if (isOption) { btns = '' + '' + ''; } else if (mini) { btns = '' + '' + '' + ''; } else { btns = '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + ''; } return '
' + btns + '
' + ''; } function wireTpToolbar(wrap, editor) { var linkBar = wrap.querySelector('.tp-link-bar'); var linkInput = wrap.querySelector('.tp-link-input'); wrap.querySelector('.tp-toolbar').addEventListener('mousedown', function(e) { var btn = e.target.closest('.tp-btn[data-cmd]'); if (!btn) return; e.preventDefault(); var cmd = btn.dataset.cmd; switch (cmd) { case 'bold': editor.chain().focus().toggleBold().run(); break; case 'italic': editor.chain().focus().toggleItalic().run(); break; case 'underline': editor.chain().focus().toggleUnderline().run(); break; case 'strike': editor.chain().focus().toggleStrike().run(); break; case 'h2': editor.chain().focus().toggleHeading({ level: 2 }).run(); break; case 'h3': editor.chain().focus().toggleHeading({ level: 3 }).run(); break; case 'bulletList': editor.chain().focus().toggleBulletList().run(); break; case 'orderedList': editor.chain().focus().toggleOrderedList().run(); break; case 'blockquote': editor.chain().focus().toggleBlockquote().run(); break; case 'codeBlock': editor.chain().focus().toggleCodeBlock().run(); break; case 'clear': editor.chain().focus().unsetAllMarks().clearNodes().run(); break; case 'link': if (linkBar.style.display === 'none') { linkInput.value = editor.getAttributes('link').href || ''; linkBar.style.display = 'flex'; setTimeout(function() { linkInput.focus(); }, 0); } else { linkBar.style.display = 'none'; } break; } updateTpState(wrap, editor); }); wrap.querySelector('.tp-link-apply').addEventListener('mousedown', function(e) { e.preventDefault(); var url = linkInput.value.trim(); if (url) editor.chain().focus().setLink({ href: url }).run(); linkBar.style.display = 'none'; }); wrap.querySelector('.tp-link-remove').addEventListener('mousedown', function(e) { e.preventDefault(); editor.chain().focus().unsetLink().run(); linkBar.style.display = 'none'; }); wrap.querySelector('.tp-link-cancel').addEventListener('mousedown', function(e) { e.preventDefault(); linkBar.style.display = 'none'; }); linkInput.addEventListener('keydown', function(e) { if (e.key === 'Enter') { e.preventDefault(); wrap.querySelector('.tp-link-apply').dispatchEvent(new MouseEvent('mousedown')); } if (e.key === 'Escape') linkBar.style.display = 'none'; }); } function updateTpState(wrap, editor) { wrap.querySelectorAll('.tp-btn[data-cmd]').forEach(function(btn) { var a = false; switch (btn.dataset.cmd) { case 'bold': a = editor.isActive('bold'); break; case 'italic': a = editor.isActive('italic'); break; case 'underline': a = editor.isActive('underline'); break; case 'strike': a = editor.isActive('strike'); break; case 'h2': a = editor.isActive('heading', { level: 2 }); break; case 'h3': a = editor.isActive('heading', { level: 3 }); break; case 'bulletList': a = editor.isActive('bulletList'); break; case 'orderedList': a = editor.isActive('orderedList'); break; case 'blockquote': a = editor.isActive('blockquote'); break; case 'codeBlock': a = editor.isActive('codeBlock'); break; case 'link': a = editor.isActive('link'); break; } btn.classList.toggle('active', a); }); } export function makeTpEditor(wrap, existingHtml, mini, isOption) { var T = getTiptap(); wrap.innerHTML = buildTpToolbar(mini, isOption) + '
'; var ed = new T.Editor({ element: wrap.querySelector('.tp-content'), extensions: [ T.StarterKit, T.Link.configure({ openOnClick: false, autolink: true }), T.Underline ], content: existingHtml || '', onUpdate: function() { updateTpState(wrap, ed); }, onSelectionUpdate: function() { updateTpState(wrap, ed); } }); wireTpToolbar(wrap, ed); return ed; } export function getTpHTML(editor) { if (!editor) return ''; var html = editor.getHTML(); return (html === '

' || html === '') ? '' : html; } export function destroyTpEditor(editor) { if (editor && typeof editor.destroy === 'function') editor.destroy(); } export function destroyOptionEditor(row) { if (!row || !row._optEditor) return; destroyTpEditor(row._optEditor); row._optEditor = null; } export function destroyQuestionBlockEditors(block) { if (!block) return; destroyTpEditor(block._questionEditor); block._questionEditor = null; block.querySelectorAll('.lh-option-row').forEach(destroyOptionEditor); } export function clearQuestionBlocks(container) { if (!container) return; container.querySelectorAll('.lh-question-block').forEach(destroyQuestionBlockEditors); container.innerHTML = ''; }