(function () { function initializeCatmenuProsemirror() { if (window.__catmenuProsemirrorInitialized) return; if (!window.Prosemirror || !window.Prosemirror.classes) return; window.__catmenuProsemirrorInitialized = true; const {classes: {MenuItem, AbstractMenuItemDispatcher}} = window.Prosemirror; const i18n = (window.LANG && LANG.plugins && LANG.plugins.catmenu) ? LANG.plugins.catmenu : {}; function hiddenMenuItem() { return new MenuItem({ label: '', render: () => { const el = document.createElement('span'); el.style.display = 'none'; return el; }, command: () => false }); } function t(key, fallback) { return i18n[key] || fallback; } function shouldShowInEditorMenu() { const raw = window.JSINFO && JSINFO.plugins && JSINFO.plugins.catmenu ? JSINFO.plugins.catmenu.show_in_editor_menu : true; if (typeof raw === 'boolean') return raw; const normalized = String(raw).trim().toLowerCase(); return !(normalized === '0' || normalized === 'false' || normalized === 'off' || normalized === 'no'); } window.Prosemirror.pluginSchemas.push((nodes, marks) => { nodes = nodes.addToEnd('catmenu', { group: 'protected_block', inline: false, selectable: true, draggable: true, defining: true, isolating: true, code: true, attrs: { syntax: {default: '{{catmenu>.}}'} }, toDOM: (node) => ['pre', {class: 'dwplugin', 'data-pluginname': 'catmenu'}, node.attrs.syntax], parseDOM: [{ tag: 'pre.dwplugin[data-pluginname="catmenu"]', getAttrs: (dom) => ({syntax: (dom.textContent || '{{catmenu>.}}').trim()}) }] }); return {nodes, marks}; }); function parseCatmenuSyntax(syntax) { const m = (syntax || '').match(/^\{\{catmenu>(.*?)\}\}$/i); if (!m) return null; return {namespace: (m[1] || '.').trim() || '.'}; } function buildCatmenuSyntax(values) { return '{{catmenu>' + ((values && values.namespace) ? values.namespace : '.') + '}}'; } function formatCatmenuLabel(values) { return 'CatMenu: ' + (values.namespace || '.'); } function getFolderIconUrl() { const svg = ""; return 'data:image/svg+xml;utf8,' + encodeURIComponent(svg); } function getFolderMenuIcon() { const ns = 'http://www.w3.org/2000/svg'; const svg = document.createElementNS(ns, 'svg'); svg.setAttribute('viewBox', '0 0 24 24'); const path1 = document.createElementNS(ns, 'path'); path1.setAttribute('d', 'M10 4l2 2h8a2 2 0 0 1 2 2v2H2V6a2 2 0 0 1 2-2h6z'); path1.setAttribute('fill', 'currentColor'); svg.appendChild(path1); const path2 = document.createElementNS(ns, 'path'); path2.setAttribute('d', 'M2 10h20v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-8z'); path2.setAttribute('fill', 'currentColor'); svg.appendChild(path2); return svg; } function isLegacyCatmenuPluginNode(node) { return !!( node && node.type && (node.type.name === 'dwplugin_inline' || node.type.name === 'dwplugin_block') && node.attrs && node.attrs['data-pluginname'] === 'catmenu' ); } function isCatmenuNode(node) { return !!(node && node.type && node.type.name === 'catmenu') || isLegacyCatmenuPluginNode(node); } function syntaxFromNode(node) { if (!node) return '{{catmenu>.}}'; if (node.type && node.type.name === 'catmenu') { return String((node.attrs && node.attrs.syntax) || '{{catmenu>.}}'); } return String(node.textContent || '{{catmenu>.}}'); } function createCatmenuNode(schema, syntax) { const normalized = String(syntax || '{{catmenu>.}}').trim() || '{{catmenu>.}}'; if (schema.nodes.catmenu) { return schema.nodes.catmenu.createChecked({syntax: normalized}); } const fallback = schema.nodes.dwplugin_block; if (!fallback) return null; return fallback.createChecked( {class: 'dwplugin', 'data-pluginname': 'catmenu'}, schema.text(normalized) ); } function findCatmenuAtSelection(state) { const {selection} = state; if (isCatmenuNode(selection.node)) { return {node: selection.node, pos: selection.from}; } const $from = selection.$from; if ($from.depth > 0 && isCatmenuNode($from.parent)) { return {node: $from.parent, pos: $from.before($from.depth)}; } if (isCatmenuNode($from.nodeBefore)) { return {node: $from.nodeBefore, pos: $from.pos - $from.nodeBefore.nodeSize}; } if (isCatmenuNode($from.nodeAfter)) { return {node: $from.nodeAfter, pos: $from.pos}; } for (let depth = $from.depth; depth > 0; depth -= 1) { const ancestor = $from.node(depth); if (isCatmenuNode(ancestor)) { return {node: ancestor, pos: $from.before(depth)}; } } return null; } function insertParagraphAfterSelectedCatmenu(view) { if (!view || !view.state) return false; const selected = findCatmenuAtSelection(view.state); if (!selected) return false; const {schema} = view.state; const paragraph = schema.nodes.paragraph && schema.nodes.paragraph.createAndFill(); if (!paragraph) return false; const insertPos = selected.pos + selected.node.nodeSize; let tr = view.state.tr.insert(insertPos, paragraph).scrollIntoView(); view.dispatch(tr); try { const SelectionClass = view.state.selection.constructor; const $target = view.state.doc.resolve(insertPos + 1); const selection = SelectionClass.near($target, 1); view.dispatch(view.state.tr.setSelection(selection).scrollIntoView()); } catch (e) { // Keep default selection on fallback. } view.focus(); return true; } function insertCatmenuBlock(view, pluginNode) { const state = view.state; const {$from} = state.selection; const index = $from.index(); if ($from.parent.canReplaceWith(index, index, pluginNode.type)) { view.dispatch(state.tr.replaceSelectionWith(pluginNode)); return true; } for (let depth = $from.depth; depth > 0; depth -= 1) { const insertPos = $from.after(depth); try { view.dispatch(state.tr.insert(insertPos, pluginNode)); return true; } catch (e) { // try a higher ancestor } } return false; } function showCatmenuDialog(initialValues, onSubmit) { const values = {namespace: '.', ...initialValues}; const $dialog = jQuery('
'); $dialog.append(''); const $namespace = jQuery('').val(values.namespace); $dialog.append($namespace); $dialog.append('