|
|
@@ -0,0 +1,226 @@
|
|
|
+/**
|
|
|
+ * Simple WYSIWYG Editor
|
|
|
+ * Lightweight rich text editor for publication content
|
|
|
+ */
|
|
|
+
|
|
|
+class WYSIWYGEditor {
|
|
|
+ constructor(textareaId, options = {}) {
|
|
|
+ this.textarea = document.getElementById(textareaId);
|
|
|
+ this.options = {
|
|
|
+ toolbar: ['bold', 'italic', 'underline', '|', 'link', 'image', '|', 'ul', 'ol', '|', 'h1', 'h2', 'h3'],
|
|
|
+ ...options
|
|
|
+ };
|
|
|
+
|
|
|
+ this.createEditor();
|
|
|
+ this.bindEvents();
|
|
|
+ }
|
|
|
+
|
|
|
+ createEditor() {
|
|
|
+ // Create editor container
|
|
|
+ this.container = document.createElement('div');
|
|
|
+ this.container.className = 'wysiwyg-container';
|
|
|
+
|
|
|
+ // Create toolbar
|
|
|
+ this.toolbar = document.createElement('div');
|
|
|
+ this.toolbar.className = 'wysiwyg-toolbar';
|
|
|
+ this.createToolbarButtons();
|
|
|
+
|
|
|
+ // Create content area
|
|
|
+ this.content = document.createElement('div');
|
|
|
+ this.content.className = 'wysiwyg-content';
|
|
|
+ this.content.contentEditable = true;
|
|
|
+
|
|
|
+ // Create character count
|
|
|
+ this.charCount = document.createElement('div');
|
|
|
+ this.charCount.className = 'wysiwyg-char-count';
|
|
|
+
|
|
|
+ // Assemble editor
|
|
|
+ this.container.appendChild(this.toolbar);
|
|
|
+ this.container.appendChild(this.content);
|
|
|
+ this.container.appendChild(this.charCount);
|
|
|
+
|
|
|
+ // Replace textarea
|
|
|
+ this.textarea.parentNode.insertBefore(this.container, this.textarea);
|
|
|
+ this.textarea.style.display = 'none';
|
|
|
+
|
|
|
+ // Initialize content
|
|
|
+ this.content.innerHTML = this.textarea.value;
|
|
|
+ this.updateCharCount();
|
|
|
+ }
|
|
|
+
|
|
|
+ createToolbarButtons() {
|
|
|
+ this.options.toolbar.forEach(item => {
|
|
|
+ if (item === '|') {
|
|
|
+ const separator = document.createElement('div');
|
|
|
+ separator.className = 'wysiwyg-separator';
|
|
|
+ this.toolbar.appendChild(separator);
|
|
|
+ } else {
|
|
|
+ const button = document.createElement('button');
|
|
|
+ button.className = 'wysiwyg-btn';
|
|
|
+ button.type = 'button';
|
|
|
+ button.innerHTML = this.getButtonLabel(item);
|
|
|
+ button.dataset.command = item;
|
|
|
+
|
|
|
+ button.addEventListener('click', () => this.execCommand(item));
|
|
|
+ this.toolbar.appendChild(button);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ getButtonLabel(command) {
|
|
|
+ const labels = {
|
|
|
+ 'bold': 'B',
|
|
|
+ 'italic': 'I',
|
|
|
+ 'underline': 'U',
|
|
|
+ 'link': '🔗',
|
|
|
+ 'image': '🖼️',
|
|
|
+ 'ul': '• List',
|
|
|
+ 'ol': '1. List',
|
|
|
+ 'h1': 'H1',
|
|
|
+ 'h2': 'H2',
|
|
|
+ 'h3': 'H3'
|
|
|
+ };
|
|
|
+ return labels[command] || command;
|
|
|
+ }
|
|
|
+
|
|
|
+ execCommand(command) {
|
|
|
+ switch (command) {
|
|
|
+ case 'bold':
|
|
|
+ document.execCommand('bold', false, null);
|
|
|
+ break;
|
|
|
+ case 'italic':
|
|
|
+ document.execCommand('italic', false, null);
|
|
|
+ break;
|
|
|
+ case 'underline':
|
|
|
+ document.execCommand('underline', false, null);
|
|
|
+ break;
|
|
|
+ case 'link':
|
|
|
+ this.insertLink();
|
|
|
+ break;
|
|
|
+ case 'image':
|
|
|
+ this.insertImage();
|
|
|
+ break;
|
|
|
+ case 'ul':
|
|
|
+ document.execCommand('insertUnorderedList', false, null);
|
|
|
+ break;
|
|
|
+ case 'ol':
|
|
|
+ document.execCommand('insertOrderedList', false, null);
|
|
|
+ break;
|
|
|
+ case 'h1':
|
|
|
+ this.formatHeading('h1');
|
|
|
+ break;
|
|
|
+ case 'h2':
|
|
|
+ this.formatHeading('h2');
|
|
|
+ break;
|
|
|
+ case 'h3':
|
|
|
+ this.formatHeading('h3');
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.content.focus();
|
|
|
+ }
|
|
|
+
|
|
|
+ insertLink() {
|
|
|
+ const selection = window.getSelection();
|
|
|
+ const url = prompt('Enter URL:');
|
|
|
+ if (url) {
|
|
|
+ document.execCommand('createLink', false, url);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ insertImage() {
|
|
|
+ const url = prompt('Enter image URL:');
|
|
|
+ if (url) {
|
|
|
+ document.execCommand('insertImage', false, url);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ formatHeading(tag) {
|
|
|
+ const selection = window.getSelection();
|
|
|
+ const range = selection.getRangeAt(0);
|
|
|
+ const heading = document.createElement(tag);
|
|
|
+ heading.textContent = range.toString();
|
|
|
+ range.deleteContents();
|
|
|
+ range.insertNode(heading);
|
|
|
+ }
|
|
|
+
|
|
|
+ bindEvents() {
|
|
|
+ // Update textarea when content changes
|
|
|
+ this.content.addEventListener('input', () => {
|
|
|
+ this.textarea.value = this.content.innerHTML;
|
|
|
+ this.updateCharCount();
|
|
|
+ });
|
|
|
+
|
|
|
+ // Update content when textarea changes (for form submission)
|
|
|
+ this.textarea.addEventListener('input', () => {
|
|
|
+ this.content.innerHTML = this.textarea.value;
|
|
|
+ this.updateCharCount();
|
|
|
+ });
|
|
|
+
|
|
|
+ // Handle paste events
|
|
|
+ this.content.addEventListener('paste', (e) => {
|
|
|
+ e.preventDefault();
|
|
|
+ const text = e.clipboardData.getData('text/html') || e.clipboardData.getData('text/plain');
|
|
|
+ document.execCommand('insertHTML', false, text);
|
|
|
+ });
|
|
|
+
|
|
|
+ // Keyboard shortcuts
|
|
|
+ this.content.addEventListener('keydown', (e) => {
|
|
|
+ if (e.ctrlKey || e.metaKey) {
|
|
|
+ switch (e.key) {
|
|
|
+ case 'b':
|
|
|
+ e.preventDefault();
|
|
|
+ this.execCommand('bold');
|
|
|
+ break;
|
|
|
+ case 'i':
|
|
|
+ e.preventDefault();
|
|
|
+ this.execCommand('italic');
|
|
|
+ break;
|
|
|
+ case 'u':
|
|
|
+ e.preventDefault();
|
|
|
+ this.execCommand('underline');
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ updateCharCount() {
|
|
|
+ const text = this.content.innerText || this.content.textContent || '';
|
|
|
+ const count = text.length;
|
|
|
+ this.charCount.textContent = `Characters: ${count}`;
|
|
|
+ }
|
|
|
+
|
|
|
+ getContent() {
|
|
|
+ return this.content.innerHTML;
|
|
|
+ }
|
|
|
+
|
|
|
+ setContent(html) {
|
|
|
+ this.content.innerHTML = html;
|
|
|
+ this.textarea.value = html;
|
|
|
+ this.updateCharCount();
|
|
|
+ }
|
|
|
+
|
|
|
+ destroy() {
|
|
|
+ this.textarea.style.display = 'block';
|
|
|
+ this.textarea.value = this.content.innerHTML;
|
|
|
+ this.container.remove();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Initialize editor when DOM is ready
|
|
|
+document.addEventListener('DOMContentLoaded', () => {
|
|
|
+ const editor = new WYSIWYGEditor('content');
|
|
|
+
|
|
|
+ // Make editor globally accessible
|
|
|
+ window.wysiwygEditor = editor;
|
|
|
+
|
|
|
+ // Handle form submission
|
|
|
+ const form = document.querySelector('.publication-form');
|
|
|
+ if (form) {
|
|
|
+ form.addEventListener('submit', () => {
|
|
|
+ // Ensure textarea has latest content
|
|
|
+ document.getElementById('content').value = editor.getContent();
|
|
|
+ });
|
|
|
+ }
|
|
|
+});
|