Files
CodePress/public/assets/js/editor-toolbar.js
Edwin Noorlander dc0d370e65 Media browser: recursive scan entire content/ tree, /-media/ URL prefix, editor change detection fix
- handleMediaList() now scans content/ recursively for all media files
- URLs use /-media/ prefix mapping directly to content/ (no special cases)
- index.php: added /-media/ route, kept /-assets/ for backward compat
- editor-toolbar.js: fixed editor.on('change') placement (was inside switchMode)
- content-edit.php and content-new.php: back-btn unsaved-changes detection
- Removed unused __editorCleanup global
2026-06-24 17:00:59 +02:00

234 lines
8.4 KiB
JavaScript

(function () {
'use strict';
var textarea = document.getElementById('editor-textarea');
if (!textarea || typeof CodeMirror === 'undefined') return;
var ext = textarea.dataset.ext || 'md';
var modeMap = { md: 'markdown', html: 'htmlmixed', php: 'php' };
var editor = CodeMirror.fromTextArea(textarea, {
mode: modeMap[ext] || 'markdown',
lineNumbers: true,
lineWrapping: true,
matchBrackets: true,
autoCloseBrackets: true,
styleActiveLine: true,
indentUnit: 4,
tabSize: 4,
indentWithTabs: true,
viewportMargin: Infinity,
extraKeys: {
'Ctrl-S': function () { document.getElementById('editor-form').submit(); },
'Cmd-S': function () { document.getElementById('editor-form').submit(); }
}
});
var commands = {
md: [
{ cmd: 'bold', icon: 'bi-type-bold', title: 'Vet' },
{ cmd: 'italic', icon: 'bi-type-italic', title: 'Cursief' },
{ cmd: 'heading', icon: 'bi-type-h2', title: 'Kop' },
{ cmd: 'link', icon: 'bi-link-45deg', title: 'Link' },
null,
{ cmd: 'ulist', icon: 'bi-list-ul', title: 'Ongenummerde lijst' },
{ cmd: 'olist', icon: 'bi-list-ol', title: 'Genummerde lijst' },
{ cmd: 'code', icon: 'bi-code-slash', title: 'Code' },
{ cmd: 'quote', icon: 'bi-chat-quote', title: 'Citaat' },
null,
{ cmd: 'hr', icon: 'bi-hr', title: 'Horizontale lijn' },
null,
{ cmd: 'media', icon: 'bi-images', title: 'Media invoegen' }
],
html: [
{ cmd: 'strong', icon: 'bi-type-bold', title: '<strong>' },
{ cmd: 'em', icon: 'bi-type-italic', title: '<em>' },
null,
{ cmd: 'link', icon: 'bi-link-45deg', title: 'Link' },
{ cmd: 'image', icon: 'bi-image', title: 'Afbeelding' },
null,
{ cmd: 'comment', icon: 'bi-chat-square-dots', title: 'Commentaar' },
null,
{ cmd: 'media', icon: 'bi-images', title: 'Media invoegen' }
],
php: [
{ cmd: 'comment_php', icon: 'bi-slash-circle', title: '// Commentaar' },
{ cmd: 'docblock', icon: 'bi-blockquote-left', title: '/** DocBlock */' },
null,
{ cmd: 'media', icon: 'bi-images', title: 'Media invoegen' }
]
};
function wrap(editor, before, after) {
var sel = editor.getSelection();
editor.replaceSelection(before + sel + after);
if (!sel) {
var cur = editor.getCursor();
editor.setCursor({ line: cur.line, ch: cur.ch - after.length });
}
editor.focus();
}
function insert(editor, text, cursorOffset) {
editor.replaceSelection(text);
if (cursorOffset !== undefined) {
var cur = editor.getCursor();
editor.setCursor({ line: cur.line, ch: cur.ch - text.length + cursorOffset });
}
editor.focus();
}
function prependLine(editor, prefix) {
var cur = editor.getCursor();
editor.replaceRange(prefix, { line: cur.line, ch: 0 }, { line: cur.line, ch: 0 });
editor.setCursor({ line: cur.line, ch: prefix.length });
editor.focus();
}
function execute(cmd) {
var sel = editor.getSelection();
switch (cmd) {
case 'bold':
sel ? wrap(editor, '**', '**') : insert(editor, '****', 2);
break;
case 'italic':
sel ? wrap(editor, '*', '*') : insert(editor, '**', 1);
break;
case 'heading':
prependLine(editor, '## ');
break;
case 'link':
sel ? wrap(editor, '[', '](url)') : insert(editor, '[linktekst](url)', 1);
break;
case 'ulist':
prependLine(editor, '- ');
break;
case 'olist':
prependLine(editor, '1. ');
break;
case 'code':
sel ? wrap(editor, '`', '`') : insert(editor, '``', 1);
break;
case 'quote':
prependLine(editor, '> ');
break;
case 'hr':
insert(editor, '\n---\n', 0);
break;
case 'strong':
sel ? wrap(editor, '<strong>', '</strong>') : insert(editor, '<strong></strong>', 8);
break;
case 'em':
sel ? wrap(editor, '<em>', '</em>') : insert(editor, '<em></em>', 4);
break;
case 'image':
insert(editor, '<img src="" alt="">', 10);
break;
case 'comment':
sel ? wrap(editor, '<!-- ', ' -->') : insert(editor, '<!-- -->', 4);
break;
case 'comment_php':
if (sel) {
sel.indexOf('\n') !== -1
? insert(editor, '/* ' + sel + ' */', 0)
: insert(editor, '// ' + sel, 0);
} else {
prependLine(editor, '// ');
}
break;
case 'docblock':
insert(editor, '/**\n * \n */', 7);
break;
case 'media':
var modal = new bootstrap.Modal(document.getElementById('mediaModal'));
if (modal) modal.show();
break;
}
}
function buildToolbar(ext) {
var toolbar = document.getElementById('editor-toolbar');
if (!toolbar) return;
toolbar.innerHTML = '';
var cmds = commands[ext] || [];
cmds.forEach(function (item) {
if (item === null) {
var sep = document.createElement('div');
sep.className = 'vr';
toolbar.appendChild(sep);
return;
}
var btn = document.createElement('button');
btn.type = 'button';
btn.className = 'btn btn-sm btn-outline-secondary';
btn.title = item.title;
btn.innerHTML = '<i class="bi ' + item.icon + '"></i>';
btn.addEventListener('click', function () { execute(item.cmd); });
toolbar.appendChild(btn);
});
}
var templates = {
md: '# Nieuwe Pagina\n\nSchrijf hier je inhoud...\n',
html: '<h1>Nieuwe Pagina</h1>\n<p>Dit is een HTML content pagina.</p>\n',
php: '---\ntitle: Nieuwe Pagina\n---\n\n<?php\n$pageTitle = "Nieuwe Pagina";\n?>\n\n<h1><?= $pageTitle ?></h1>\n<p>PHP content - alles wat je hier echo&#39;t wordt weergegeven.</p>\n\n<?php\n$items = [\'Item 1\', \'Item 2\', \'Item 3\'];\n?>\n<ul>\n<?php foreach ($items as $item): ?>\n <li><?= $item ?></li>\n<?php endforeach; ?>\n</ul>\n'
};
var defaultContents = {};
function getTemplate(ext) {
return templates[ext] || '';
}
function setDefaultContent(ext) {
var tpl = getTemplate(ext);
editor.setValue(tpl);
editor.setCursor({ line: 0, ch: 0 });
editor.focus();
defaultContents[ext] = tpl;
}
function switchMode(ext, forceContent) {
var mode = modeMap[ext] || 'markdown';
editor.setOption('mode', mode);
textarea.dataset.ext = ext;
if (forceContent) {
setDefaultContent(ext);
} else {
var cur = editor.getValue().trim();
var expected = defaultContents[ext];
if (cur === '' || (expected && cur === expected.trim())) {
setDefaultContent(ext);
}
}
}
editor.on('change', function () {
if (window.__onContentChange) window.__onContentChange();
});
buildToolbar(ext);
var form = document.getElementById('editor-form');
var isNewPage = form && form.hasAttribute('data-new-page');
if (isNewPage && editor.getValue().trim() === '') {
setDefaultContent(ext);
}
var typeSelect = document.querySelector('[data-editor-mode]');
if (typeSelect && isNewPage) {
typeSelect.addEventListener('change', function () {
switchMode(this.value, true);
});
}
document.getElementById('editor-form').addEventListener('submit', function () {
editor.save();
});
})();