/* ============================================
   RSL Text Editor Component
   Version: 1.0.0
   Description: Accessible, lightweight WYSIWYG text editor

   Features:
   - Full keyboard navigation and screen reader support
   - Data-attribute driven configuration
   - Clean HTML output for CMS integration
   - Smart paste (cleans Word/Google Docs formatting)
   - Keyboard shortcuts with visual overlay
   - Character/word count with limits
   - Source view mode
   - Dark mode support
   - JSON Layout API V2 compatible
   ============================================ */

(function() {
    'use strict';

    // Initialize RSL namespace
    window.RSL = window.RSL || {};

    // Track instances
    var instances = new Map();

    /**
     * Text Editor Constructor
     * @param {HTMLElement} element - Container element
     * @param {Object} options - Configuration options
     */
    function TextEditor(element, options) {
        this.element = element;
        this.config = this._parseConfig(options);
        this.dom = {};
        this.state = {
            sourceView: false,
            wordCount: 0,
            charCount: 0,
            selection: null,
            focusMode: false
        };

        this._buildDOM();
        this._bindEvents();
        this._updateCounts();

        // Set default paragraph separator to <p> instead of <div>
        try {
            document.execCommand('defaultParagraphSeparator', false, 'p');
        } catch (e) {
            // Some browsers don't support this command
        }

        // Dispatch ready event
        this._dispatchEvent('rsl-editor-ready', { editor: this });
    }

    /**
     * Default configuration
     */
    TextEditor.DEFAULTS = {
        placeholder: 'Start typing...',
        toolbar: 'full',  // full, basic, minimal, custom
        buttons: null,    // Custom buttons array
        minHeight: '150px',
        maxHeight: null,
        output: 'html',   // html, text
        showWordCount: true,
        showCharCount: true,
        maxChars: null,
        readOnly: false,
        autoSave: false,
        pasteAsText: false,
        focusMode: false,
        spellcheck: true
    };

    /**
     * Toolbar button definitions
     */
    TextEditor.BUTTONS = {
        bold: {
            icon: '<svg viewBox="0 0 24 24"><path d="M15.6 10.79c.97-.67 1.65-1.77 1.65-2.79 0-2.26-1.75-4-4-4H7v14h7.04c2.09 0 3.71-1.7 3.71-3.79 0-1.52-.86-2.82-2.15-3.42zM10 6.5h3c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5h-3v-3zm3.5 9H10v-3h3.5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5z"/></svg>',
            command: 'bold',
            tooltip: 'Bold (Ctrl+B)',
            shortcut: 'ctrl+b',
            group: 'format-basic',
            toggle: true
        },
        italic: {
            icon: '<svg viewBox="0 0 24 24"><path d="M10 4v3h2.21l-3.42 8H6v3h8v-3h-2.21l3.42-8H18V4z"/></svg>',
            command: 'italic',
            tooltip: 'Italic (Ctrl+I)',
            shortcut: 'ctrl+i',
            group: 'format-basic',
            toggle: true
        },
        underline: {
            icon: '<svg viewBox="0 0 24 24"><path d="M12 17c3.31 0 6-2.69 6-6V3h-2.5v8c0 1.93-1.57 3.5-3.5 3.5S8.5 12.93 8.5 11V3H6v8c0 3.31 2.69 6 6 6zm-7 2v2h14v-2H5z"/></svg>',
            command: 'underline',
            tooltip: 'Underline (Ctrl+U)',
            shortcut: 'ctrl+u',
            group: 'format-basic',
            toggle: true
        },
        strikethrough: {
            icon: '<svg viewBox="0 0 24 24"><path d="M10 19h4v-3h-4v3zM5 4v3h5v3h4V7h5V4H5zM3 14h18v-2H3v2z"/></svg>',
            command: 'strikeThrough',
            tooltip: 'Strikethrough',
            group: 'format-basic',
            toggle: true
        },
        alignLeft: {
            icon: '<svg viewBox="0 0 24 24"><path d="M15 15H3v2h12v-2zm0-8H3v2h12V7zM3 13h18v-2H3v2zm0 8h18v-2H3v2zM3 3v2h18V3H3z"/></svg>',
            command: 'justifyLeft',
            tooltip: 'Align Left',
            group: 'format-align'
        },
        alignCenter: {
            icon: '<svg viewBox="0 0 24 24"><path d="M7 15v2h10v-2H7zm-4 6h18v-2H3v2zm0-8h18v-2H3v2zm4-6v2h10V7H7zM3 3v2h18V3H3z"/></svg>',
            command: 'justifyCenter',
            tooltip: 'Align Center',
            group: 'format-align'
        },
        alignRight: {
            icon: '<svg viewBox="0 0 24 24"><path d="M3 21h18v-2H3v2zm6-4h12v-2H9v2zm-6-4h18v-2H3v2zm6-4h12V7H9v2zM3 3v2h18V3H3z"/></svg>',
            command: 'justifyRight',
            tooltip: 'Align Right',
            group: 'format-align'
        },
        alignJustify: {
            icon: '<svg viewBox="0 0 24 24"><path d="M3 21h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18V7H3v2zm0-6v2h18V3H3z"/></svg>',
            command: 'justifyFull',
            tooltip: 'Justify',
            group: 'format-align'
        },
        bulletList: {
            icon: '<svg viewBox="0 0 24 24"><path d="M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z"/></svg>',
            command: 'insertUnorderedList',
            tooltip: 'Bullet List',
            group: 'format-list',
            toggle: true
        },
        numberedList: {
            icon: '<svg viewBox="0 0 24 24"><path d="M2 17h2v.5H3v1h1v.5H2v1h3v-4H2v1zm1-9h1V4H2v1h1v3zm-1 3h1.8L2 13.1v.9h3v-1H3.2L5 10.9V10H2v1zm5-6v2h14V5H7zm0 14h14v-2H7v2zm0-6h14v-2H7v2z"/></svg>',
            command: 'insertOrderedList',
            tooltip: 'Numbered List',
            group: 'format-list',
            toggle: true
        },
        blockquote: {
            icon: '<svg viewBox="0 0 24 24"><path d="M6 17h3l2-4V7H5v6h3zm8 0h3l2-4V7h-6v6h3z"/></svg>',
            command: 'formatBlock',
            value: 'blockquote',
            tooltip: 'Blockquote',
            group: 'format-list'
        },
        link: {
            icon: '<svg viewBox="0 0 24 24"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/></svg>',
            action: 'link',
            tooltip: 'Insert Link (Ctrl+K)',
            shortcut: 'ctrl+k',
            group: 'insert-link'
        },
        image: {
            icon: '<svg viewBox="0 0 24 24"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></svg>',
            action: 'image',
            tooltip: 'Insert Image',
            group: 'insert-image'
        },
        undo: {
            icon: '<svg viewBox="0 0 24 24"><path d="M12.5 8c-2.65 0-5.05.99-6.9 2.6L2 7v9h9l-3.62-3.62c1.39-1.16 3.16-1.88 5.12-1.88 3.54 0 6.55 2.31 7.6 5.5l2.37-.78C21.08 11.03 17.15 8 12.5 8z"/></svg>',
            command: 'undo',
            tooltip: 'Undo (Ctrl+Z)',
            shortcut: 'ctrl+z',
            group: 'actions-history'
        },
        redo: {
            icon: '<svg viewBox="0 0 24 24"><path d="M18.4 10.6C16.55 8.99 14.15 8 11.5 8c-4.65 0-8.58 3.03-9.96 7.22L3.9 16c1.05-3.19 4.05-5.5 7.6-5.5 1.95 0 3.73.72 5.12 1.88L13 16h9V7l-3.6 3.6z"/></svg>',
            command: 'redo',
            tooltip: 'Redo (Ctrl+Y)',
            shortcut: 'ctrl+y',
            group: 'actions-history'
        },
        source: {
            icon: '<svg viewBox="0 0 24 24"><path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/></svg>',
            action: 'source',
            tooltip: 'View Source',
            group: 'actions-source',
            toggle: true
        },
        removeFormat: {
            icon: '<svg viewBox="0 0 24 24"><path d="M15 16h4v2h-4zm0-8h7v2h-7zm0 4h6v2h-6zM3 18c0 1.1.9 2 2 2h6c1.1 0 2-.9 2-2V8H3v10zM14 5h-3l-1-1H6L5 5H2v2h12z"/></svg>',
            command: 'removeFormat',
            tooltip: 'Clear Formatting',
            group: 'format-basic'
        },
        horizontalRule: {
            icon: '<svg viewBox="0 0 24 24"><path d="M4 11h16v2H4z"/></svg>',
            command: 'insertHorizontalRule',
            tooltip: 'Horizontal Line',
            group: 'insert-misc'
        }
    };

    /**
     * Heading options
     */
    TextEditor.HEADINGS = [
        { value: 'p', label: 'Paragraph' },
        { value: 'h1', label: 'Heading 1' },
        { value: 'h2', label: 'Heading 2' },
        { value: 'h3', label: 'Heading 3' },
        { value: 'h4', label: 'Heading 4' }
    ];

    /**
     * Toolbar presets
     */
    TextEditor.PRESETS = {
        full: ['bold', 'italic', 'underline', 'strikethrough', '|', 'headings', '|', 'alignLeft', 'alignCenter', 'alignRight', '|', 'bulletList', 'numberedList', 'blockquote', '|', 'link', 'image', '|', 'undo', 'redo', 'source'],
        basic: ['bold', 'italic', 'underline', '|', 'bulletList', 'numberedList', '|', 'link', '|', 'undo', 'redo'],
        minimal: ['bold', 'italic', '|', 'link']
    };

    /**
     * Parse configuration from data attributes
     * Supports both short form (data-toolbar) and long form (data-editor-toolbar)
     */
    TextEditor.prototype._parseConfig = function(options) {
        var config = Object.assign({}, TextEditor.DEFAULTS, options || {});
        var el = this.element;

        // Helper to check both attribute forms
        function getAttr(name) {
            return el.getAttribute('data-' + name) || el.getAttribute('data-editor-' + name);
        }
        function hasAttr(name) {
            return el.hasAttribute('data-' + name) || el.hasAttribute('data-editor-' + name);
        }

        if (hasAttr('placeholder')) {
            config.placeholder = getAttr('placeholder');
        }
        if (hasAttr('toolbar')) {
            config.toolbar = getAttr('toolbar');
        }
        if (hasAttr('toolbar-buttons')) {
            var buttons = getAttr('toolbar-buttons');
            config.buttons = buttons.split(',').map(function(b) { return b.trim(); });
            config.toolbar = 'custom';
        }
        if (hasAttr('min-height')) {
            config.minHeight = getAttr('min-height');
        }
        if (hasAttr('max-height')) {
            config.maxHeight = getAttr('max-height');
        }
        if (hasAttr('output')) {
            config.output = getAttr('output');
        }
        if (hasAttr('show-count')) {
            var showCount = getAttr('show-count') !== 'false';
            config.showWordCount = showCount;
            config.showCharCount = showCount;
        }
        if (hasAttr('show-word-count')) {
            config.showWordCount = getAttr('show-word-count') === 'true';
        }
        if (hasAttr('show-char-count')) {
            config.showCharCount = getAttr('show-char-count') === 'true';
        }
        if (hasAttr('char-limit')) {
            config.maxChars = parseInt(getAttr('char-limit'), 10);
        }
        if (hasAttr('word-limit')) {
            config.maxWords = parseInt(getAttr('word-limit'), 10);
        }
        if (hasAttr('max-chars')) {
            config.maxChars = parseInt(getAttr('max-chars'), 10);
        }
        if (hasAttr('readonly')) {
            config.readOnly = getAttr('readonly') === 'true';
        }
        if (hasAttr('read-only')) {
            config.readOnly = getAttr('read-only') === 'true';
        }
        if (hasAttr('paste-as-text')) {
            config.pasteAsText = getAttr('paste-as-text') === 'true';
        }
        if (hasAttr('focus-mode')) {
            config.focusMode = getAttr('focus-mode') === 'true';
        }
        if (hasAttr('spellcheck')) {
            config.spellcheck = getAttr('spellcheck') !== 'false';
        }
        if (hasAttr('autofocus')) {
            config.autofocus = getAttr('autofocus') === 'true';
        }

        return config;
    };

    /**
     * Build DOM structure
     */
    TextEditor.prototype._buildDOM = function() {
        var self = this;

        // Capture initial content BEFORE clearing
        this._initialContent = this.element.innerHTML.trim();
        this.element.innerHTML = '';

        this.element.classList.add('rsl-text-editor');
        this.element.setAttribute('data-editor-toolbar', this.config.toolbar);

        if (this.config.readOnly) {
            this.element.setAttribute('data-editor-read-only', 'true');
        }

        // Set max height CSS variable
        if (this.config.maxHeight) {
            this.element.style.setProperty('--editor-max-height', this.config.maxHeight);
        }

        // Create live region for announcements
        this.dom.liveRegion = document.createElement('div');
        this.dom.liveRegion.className = 'rsl-text-editor-live-region';
        this.dom.liveRegion.setAttribute('role', 'status');
        this.dom.liveRegion.setAttribute('aria-live', 'polite');
        this.dom.liveRegion.setAttribute('aria-atomic', 'true');
        this.element.appendChild(this.dom.liveRegion);

        // Build toolbar FIRST
        if (!this.config.readOnly) {
            this._buildToolbar();
        }

        // Build content area (after toolbar)
        this._buildContentArea();

        // Build source textarea
        this._buildSourceArea();

        // Build status bar
        if (!this.config.readOnly && (this.config.showWordCount || this.config.showCharCount)) {
            this._buildStatusBar();
        }

        // Build dialogs
        this._buildLinkDialog();
        this._buildImageDialog();
        this._buildShortcutsOverlay();
    };

    /**
     * Build toolbar
     */
    TextEditor.prototype._buildToolbar = function() {
        var self = this;

        this.dom.toolbar = document.createElement('div');
        this.dom.toolbar.className = 'rsl-text-editor-toolbar';
        this.dom.toolbar.setAttribute('role', 'toolbar');
        this.dom.toolbar.setAttribute('aria-label', 'Text formatting toolbar');
        this.dom.toolbar.setAttribute('contenteditable', 'false');  // Prevent typing in toolbar

        // Get buttons for this preset
        var buttons = this._getToolbarButtons();
        var currentGroup = null;
        var groupEl = null;

        buttons.forEach(function(btnName) {
            if (btnName === '|') {
                // Separator
                var sep = document.createElement('div');
                sep.className = 'rsl-text-editor-separator';
                sep.setAttribute('role', 'separator');
                self.dom.toolbar.appendChild(sep);
                currentGroup = null;
                return;
            }

            if (btnName === 'headings') {
                // Heading dropdown
                self._buildHeadingDropdown();
                currentGroup = null;
                return;
            }

            var btnDef = TextEditor.BUTTONS[btnName];
            if (!btnDef) return;

            // Create group if needed
            if (btnDef.group !== currentGroup) {
                groupEl = document.createElement('div');
                groupEl.className = 'rsl-text-editor-group';
                groupEl.setAttribute('role', 'group');
                groupEl.setAttribute('data-group', btnDef.group);
                groupEl.setAttribute('aria-label', self._getGroupLabel(btnDef.group));
                self.dom.toolbar.appendChild(groupEl);
                currentGroup = btnDef.group;
            }

            // Create button
            var btn = self._createToolbarButton(btnName, btnDef);
            groupEl.appendChild(btn);
        });

        this.element.appendChild(this.dom.toolbar);
    };

    /**
     * Get toolbar buttons based on preset or custom config
     */
    TextEditor.prototype._getToolbarButtons = function() {
        if (this.config.toolbar === 'custom' && this.config.buttons) {
            return this.config.buttons;
        }
        return TextEditor.PRESETS[this.config.toolbar] || TextEditor.PRESETS.full;
    };

    /**
     * Get group label for accessibility
     */
    TextEditor.prototype._getGroupLabel = function(group) {
        var labels = {
            'format-basic': 'Text formatting',
            'format-headings': 'Headings',
            'format-align': 'Text alignment',
            'format-list': 'Lists and quotes',
            'insert-link': 'Insert link',
            'insert-image': 'Insert image',
            'insert-misc': 'Insert elements',
            'actions-history': 'Undo and redo',
            'actions-source': 'View source'
        };
        return labels[group] || 'Formatting';
    };

    /**
     * Create a toolbar button
     */
    TextEditor.prototype._createToolbarButton = function(name, def) {
        var self = this;
        var btn = document.createElement('button');
        btn.type = 'button';
        btn.className = 'rsl-text-editor-btn';
        btn.innerHTML = def.icon;
        btn.setAttribute('data-command', name);
        btn.setAttribute('data-tooltip', def.tooltip);
        btn.setAttribute('aria-label', def.tooltip);

        if (def.toggle) {
            btn.setAttribute('aria-pressed', 'false');
        }

        btn.addEventListener('click', function(e) {
            e.preventDefault();
            self._handleButtonClick(name, def);
        });

        return btn;
    };

    /**
     * Build heading dropdown
     */
    TextEditor.prototype._buildHeadingDropdown = function() {
        var self = this;

        var container = document.createElement('div');
        container.className = 'rsl-text-editor-dropdown rsl-text-editor-group';
        container.setAttribute('data-group', 'format-headings');

        // Dropdown button
        var btn = document.createElement('button');
        btn.type = 'button';
        btn.className = 'rsl-text-editor-btn rsl-text-editor-dropdown-btn';
        btn.innerHTML = '<span>Paragraph</span><svg class="dropdown-arrow" viewBox="0 0 24 24"><path d="M7 10l5 5 5-5z"/></svg>';
        btn.setAttribute('aria-haspopup', 'listbox');
        btn.setAttribute('aria-expanded', 'false');
        btn.setAttribute('aria-label', 'Format block');
        btn.setAttribute('data-tooltip', 'Format block');

        // Dropdown menu
        var menu = document.createElement('ul');
        menu.className = 'rsl-text-editor-dropdown-menu';
        menu.setAttribute('role', 'listbox');
        menu.setAttribute('aria-label', 'Block format options');

        TextEditor.HEADINGS.forEach(function(heading, index) {
            var item = document.createElement('li');
            item.setAttribute('role', 'option');

            var itemBtn = document.createElement('button');
            itemBtn.type = 'button';
            itemBtn.className = 'rsl-text-editor-dropdown-item';
            itemBtn.setAttribute('data-value', heading.value);
            itemBtn.textContent = heading.label;
            itemBtn.setAttribute('tabindex', index === 0 ? '0' : '-1');

            itemBtn.addEventListener('click', function(e) {
                e.preventDefault();
                self._setHeading(heading.value);
                btn.querySelector('span').textContent = heading.label;
                self._closeDropdown(menu, btn);
            });

            item.appendChild(itemBtn);
            menu.appendChild(item);
        });

        // Toggle dropdown
        btn.addEventListener('click', function(e) {
            e.preventDefault();
            var isOpen = menu.classList.contains('open');
            if (isOpen) {
                self._closeDropdown(menu, btn);
            } else {
                self._openDropdown(menu, btn);
            }
        });

        // Keyboard navigation
        btn.addEventListener('keydown', function(e) {
            if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {
                e.preventDefault();
                self._openDropdown(menu, btn);
                menu.querySelector('button').focus();
            }
        });

        menu.addEventListener('keydown', function(e) {
            var items = menu.querySelectorAll('button');
            var currentIndex = Array.prototype.indexOf.call(items, document.activeElement);

            if (e.key === 'ArrowDown') {
                e.preventDefault();
                var next = (currentIndex + 1) % items.length;
                items[next].focus();
            } else if (e.key === 'ArrowUp') {
                e.preventDefault();
                var prev = (currentIndex - 1 + items.length) % items.length;
                items[prev].focus();
            } else if (e.key === 'Escape') {
                e.preventDefault();
                self._closeDropdown(menu, btn);
                btn.focus();
            }
        });

        container.appendChild(btn);
        container.appendChild(menu);
        this.dom.toolbar.appendChild(container);
        this.dom.headingDropdown = { button: btn, menu: menu };

        // Close on outside click
        document.addEventListener('click', function(e) {
            if (!container.contains(e.target) && menu.classList.contains('open')) {
                self._closeDropdown(menu, btn);
            }
        });
    };

    /**
     * Open dropdown
     */
    TextEditor.prototype._openDropdown = function(menu, btn) {
        menu.classList.add('open');
        btn.setAttribute('aria-expanded', 'true');
    };

    /**
     * Close dropdown
     */
    TextEditor.prototype._closeDropdown = function(menu, btn) {
        menu.classList.remove('open');
        btn.setAttribute('aria-expanded', 'false');
    };

    /**
     * Build content area
     */
    TextEditor.prototype._buildContentArea = function() {
        this.dom.content = document.createElement('div');
        this.dom.content.className = 'rsl-text-editor-content';
        this.dom.content.setAttribute('contenteditable', this.config.readOnly ? 'false' : 'true');
        this.dom.content.setAttribute('role', 'textbox');
        this.dom.content.setAttribute('aria-multiline', 'true');
        this.dom.content.setAttribute('aria-label', this.config.placeholder);
        this.dom.content.setAttribute('data-placeholder', this.config.placeholder);
        this.dom.content.setAttribute('spellcheck', String(this.config.spellcheck));
        this.dom.content.style.minHeight = this.config.minHeight;

        // Set initial content if provided
        if (this._initialContent) {
            this.dom.content.innerHTML = this._initialContent;
        }

        this.element.appendChild(this.dom.content);
    };

    /**
     * Build source textarea
     */
    TextEditor.prototype._buildSourceArea = function() {
        this.dom.source = document.createElement('textarea');
        this.dom.source.className = 'rsl-text-editor-source';
        this.dom.source.setAttribute('aria-label', 'HTML source code');
        this.dom.source.setAttribute('spellcheck', 'false');
        this.element.appendChild(this.dom.source);
    };

    /**
     * Build status bar
     */
    TextEditor.prototype._buildStatusBar = function() {
        var self = this;

        this.dom.status = document.createElement('div');
        this.dom.status.className = 'rsl-text-editor-status';

        var left = document.createElement('div');
        left.className = 'rsl-text-editor-status-left';

        if (this.config.showWordCount) {
            var wordCount = document.createElement('div');
            wordCount.className = 'rsl-text-editor-count';
            wordCount.setAttribute('data-count-type', 'words');

            var wordHtml = '<span class="rsl-text-editor-count-label">Words:</span><span class="rsl-text-editor-count-value" data-count="words">0</span>';
            if (this.config.maxWords) {
                wordHtml += '<span class="rsl-text-editor-count-label">/ ' + this.config.maxWords + '</span>';
            }
            wordCount.innerHTML = wordHtml;
            left.appendChild(wordCount);

            // Add progress bar if max words set
            if (this.config.maxWords) {
                var wordProgress = document.createElement('div');
                wordProgress.className = 'rsl-text-editor-progress';
                wordProgress.innerHTML = '<div class="rsl-text-editor-progress-bar" data-progress="words" style="width: 0%"></div>';
                left.appendChild(wordProgress);
                this.dom.wordProgressBar = wordProgress.querySelector('.rsl-text-editor-progress-bar');
            }
        }

        if (this.config.showCharCount) {
            var charCount = document.createElement('div');
            charCount.className = 'rsl-text-editor-count';
            charCount.setAttribute('data-count-type', 'chars');

            var charHtml = '<span class="rsl-text-editor-count-label">Characters:</span><span class="rsl-text-editor-count-value" data-count="chars">0</span>';
            if (this.config.maxChars) {
                charHtml += '<span class="rsl-text-editor-count-label">/ ' + this.config.maxChars + '</span>';
            }
            charCount.innerHTML = charHtml;
            left.appendChild(charCount);

            // Add progress bar if max chars set
            if (this.config.maxChars) {
                var progress = document.createElement('div');
                progress.className = 'rsl-text-editor-progress';
                progress.innerHTML = '<div class="rsl-text-editor-progress-bar" data-progress="chars" style="width: 0%"></div>';
                left.appendChild(progress);
                this.dom.charProgressBar = progress.querySelector('.rsl-text-editor-progress-bar');
            }
        }

        var right = document.createElement('div');
        right.className = 'rsl-text-editor-status-right';

        // Source view indicator
        var sourceIndicator = document.createElement('div');
        sourceIndicator.className = 'rsl-text-editor-source-indicator hidden';
        sourceIndicator.innerHTML = '<svg viewBox="0 0 24 24" width="12" height="12"><path fill="currentColor" d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/></svg> Source';
        right.appendChild(sourceIndicator);
        this.dom.sourceIndicator = sourceIndicator;

        this.dom.status.appendChild(left);
        this.dom.status.appendChild(right);
        this.element.appendChild(this.dom.status);

        this.dom.wordCountEl = this.dom.status.querySelector('[data-count="words"]');
        this.dom.charCountEl = this.dom.status.querySelector('[data-count="chars"]');
        this.dom.wordCountContainer = this.dom.status.querySelector('[data-count-type="words"]');
        this.dom.charCountContainer = this.dom.status.querySelector('[data-count-type="chars"]');
    };

    /**
     * Build link dialog
     */
    TextEditor.prototype._buildLinkDialog = function() {
        var self = this;

        this.dom.linkDialog = document.createElement('div');
        this.dom.linkDialog.className = 'rsl-text-editor-dialog-overlay';
        this.dom.linkDialog.setAttribute('role', 'dialog');
        this.dom.linkDialog.setAttribute('aria-labelledby', 'rsl-editor-link-title');
        this.dom.linkDialog.setAttribute('aria-modal', 'true');

        this.dom.linkDialog.innerHTML = [
            '<div class="rsl-text-editor-dialog">',
            '  <div class="rsl-text-editor-dialog-header">',
            '    <h3 class="rsl-text-editor-dialog-title" id="rsl-editor-link-title">Insert Link</h3>',
            '    <button type="button" class="rsl-text-editor-dialog-close" aria-label="Close dialog">',
            '      <svg viewBox="0 0 24 24" width="20" height="20"><path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>',
            '    </button>',
            '  </div>',
            '  <div class="rsl-text-editor-dialog-body">',
            '    <div class="rsl-text-editor-dialog-field">',
            '      <label class="rsl-text-editor-dialog-label" for="rsl-editor-link-url">URL</label>',
            '      <input type="url" class="rsl-text-editor-dialog-input" id="rsl-editor-link-url" placeholder="https://example.com">',
            '    </div>',
            '    <div class="rsl-text-editor-dialog-field">',
            '      <label class="rsl-text-editor-dialog-label" for="rsl-editor-link-text">Link Text (optional)</label>',
            '      <input type="text" class="rsl-text-editor-dialog-input" id="rsl-editor-link-text" placeholder="Display text">',
            '    </div>',
            '  </div>',
            '  <div class="rsl-text-editor-dialog-actions">',
            '    <button type="button" class="rsl-text-editor-dialog-btn rsl-text-editor-dialog-btn-cancel">Cancel</button>',
            '    <button type="button" class="rsl-text-editor-dialog-btn rsl-text-editor-dialog-btn-primary">Insert Link</button>',
            '  </div>',
            '</div>'
        ].join('\n');

        document.body.appendChild(this.dom.linkDialog);

        // Event handlers
        var closeBtn = this.dom.linkDialog.querySelector('.rsl-text-editor-dialog-close');
        var cancelBtn = this.dom.linkDialog.querySelector('.rsl-text-editor-dialog-btn-cancel');
        var insertBtn = this.dom.linkDialog.querySelector('.rsl-text-editor-dialog-btn-primary');
        var urlInput = this.dom.linkDialog.querySelector('#rsl-editor-link-url');
        var textInput = this.dom.linkDialog.querySelector('#rsl-editor-link-text');

        closeBtn.addEventListener('click', function() { self._closeLinkDialog(); });
        cancelBtn.addEventListener('click', function() { self._closeLinkDialog(); });
        insertBtn.addEventListener('click', function() { self._insertLink(); });

        // Close on overlay click
        this.dom.linkDialog.addEventListener('click', function(e) {
            if (e.target === self.dom.linkDialog) {
                self._closeLinkDialog();
            }
        });

        // Close on Escape
        this.dom.linkDialog.addEventListener('keydown', function(e) {
            if (e.key === 'Escape') {
                self._closeLinkDialog();
            }
        });

        // Submit on Enter in URL field
        urlInput.addEventListener('keydown', function(e) {
            if (e.key === 'Enter') {
                e.preventDefault();
                self._insertLink();
            }
        });
    };

    /**
     * Build image dialog
     */
    TextEditor.prototype._buildImageDialog = function() {
        var self = this;

        this.dom.imageDialog = document.createElement('div');
        this.dom.imageDialog.className = 'rsl-text-editor-dialog-overlay';
        this.dom.imageDialog.setAttribute('role', 'dialog');
        this.dom.imageDialog.setAttribute('aria-labelledby', 'rsl-editor-image-title');
        this.dom.imageDialog.setAttribute('aria-modal', 'true');

        this.dom.imageDialog.innerHTML = [
            '<div class="rsl-text-editor-dialog">',
            '  <div class="rsl-text-editor-dialog-header">',
            '    <h3 class="rsl-text-editor-dialog-title" id="rsl-editor-image-title">Insert Image</h3>',
            '    <button type="button" class="rsl-text-editor-dialog-close" aria-label="Close dialog">',
            '      <svg viewBox="0 0 24 24" width="20" height="20"><path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>',
            '    </button>',
            '  </div>',
            '  <div class="rsl-text-editor-dialog-body">',
            '    <div class="rsl-text-editor-dialog-field">',
            '      <label class="rsl-text-editor-dialog-label" for="rsl-editor-image-url">Image URL</label>',
            '      <input type="url" class="rsl-text-editor-dialog-input" id="rsl-editor-image-url" placeholder="https://example.com/image.jpg">',
            '    </div>',
            '    <div class="rsl-text-editor-dialog-field">',
            '      <label class="rsl-text-editor-dialog-label" for="rsl-editor-image-alt">Alt Text (required for accessibility)</label>',
            '      <input type="text" class="rsl-text-editor-dialog-input" id="rsl-editor-image-alt" placeholder="Describe the image">',
            '    </div>',
            '  </div>',
            '  <div class="rsl-text-editor-dialog-actions">',
            '    <button type="button" class="rsl-text-editor-dialog-btn rsl-text-editor-dialog-btn-cancel">Cancel</button>',
            '    <button type="button" class="rsl-text-editor-dialog-btn rsl-text-editor-dialog-btn-primary">Insert Image</button>',
            '  </div>',
            '</div>'
        ].join('\n');

        document.body.appendChild(this.dom.imageDialog);

        // Event handlers
        var closeBtn = this.dom.imageDialog.querySelector('.rsl-text-editor-dialog-close');
        var cancelBtn = this.dom.imageDialog.querySelector('.rsl-text-editor-dialog-btn-cancel');
        var insertBtn = this.dom.imageDialog.querySelector('.rsl-text-editor-dialog-btn-primary');

        closeBtn.addEventListener('click', function() { self._closeImageDialog(); });
        cancelBtn.addEventListener('click', function() { self._closeImageDialog(); });
        insertBtn.addEventListener('click', function() { self._insertImage(); });

        // Close on overlay click
        this.dom.imageDialog.addEventListener('click', function(e) {
            if (e.target === self.dom.imageDialog) {
                self._closeImageDialog();
            }
        });

        // Close on Escape
        this.dom.imageDialog.addEventListener('keydown', function(e) {
            if (e.key === 'Escape') {
                self._closeImageDialog();
            }
        });
    };

    /**
     * Build keyboard shortcuts overlay
     */
    TextEditor.prototype._buildShortcutsOverlay = function() {
        var self = this;

        this.dom.shortcutsOverlay = document.createElement('div');
        this.dom.shortcutsOverlay.className = 'rsl-text-editor-shortcuts-overlay';
        this.dom.shortcutsOverlay.setAttribute('role', 'dialog');
        this.dom.shortcutsOverlay.setAttribute('aria-labelledby', 'rsl-editor-shortcuts-title');
        this.dom.shortcutsOverlay.setAttribute('aria-modal', 'true');

        var isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
        var ctrlKey = isMac ? '⌘' : 'Ctrl';

        this.dom.shortcutsOverlay.innerHTML = [
            '<div class="rsl-text-editor-shortcuts">',
            '  <h3 class="rsl-text-editor-shortcuts-title">',
            '    <svg viewBox="0 0 24 24" width="24" height="24"><path fill="currentColor" d="M20 5H4c-1.1 0-1.99.9-1.99 2L2 17c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-9 3h2v2h-2V8zm0 3h2v2h-2v-2zM8 8h2v2H8V8zm0 3h2v2H8v-2zm-1 2H5v-2h2v2zm0-3H5V8h2v2zm9 7H8v-2h8v2zm0-4h-2v-2h2v2zm0-3h-2V8h2v2zm3 3h-2v-2h2v2zm0-3h-2V8h2v2z"/></svg>',
            '    Keyboard Shortcuts',
            '  </h3>',
            '  <div class="rsl-text-editor-shortcuts-grid">',
            '    <div class="rsl-text-editor-shortcut">',
            '      <span class="rsl-text-editor-shortcut-label">Bold</span>',
            '      <span class="rsl-text-editor-shortcut-keys"><span class="rsl-text-editor-key">' + ctrlKey + '</span><span class="rsl-text-editor-key">B</span></span>',
            '    </div>',
            '    <div class="rsl-text-editor-shortcut">',
            '      <span class="rsl-text-editor-shortcut-label">Italic</span>',
            '      <span class="rsl-text-editor-shortcut-keys"><span class="rsl-text-editor-key">' + ctrlKey + '</span><span class="rsl-text-editor-key">I</span></span>',
            '    </div>',
            '    <div class="rsl-text-editor-shortcut">',
            '      <span class="rsl-text-editor-shortcut-label">Underline</span>',
            '      <span class="rsl-text-editor-shortcut-keys"><span class="rsl-text-editor-key">' + ctrlKey + '</span><span class="rsl-text-editor-key">U</span></span>',
            '    </div>',
            '    <div class="rsl-text-editor-shortcut">',
            '      <span class="rsl-text-editor-shortcut-label">Insert Link</span>',
            '      <span class="rsl-text-editor-shortcut-keys"><span class="rsl-text-editor-key">' + ctrlKey + '</span><span class="rsl-text-editor-key">K</span></span>',
            '    </div>',
            '    <div class="rsl-text-editor-shortcut">',
            '      <span class="rsl-text-editor-shortcut-label">Undo</span>',
            '      <span class="rsl-text-editor-shortcut-keys"><span class="rsl-text-editor-key">' + ctrlKey + '</span><span class="rsl-text-editor-key">Z</span></span>',
            '    </div>',
            '    <div class="rsl-text-editor-shortcut">',
            '      <span class="rsl-text-editor-shortcut-label">Redo</span>',
            '      <span class="rsl-text-editor-shortcut-keys"><span class="rsl-text-editor-key">' + ctrlKey + '</span><span class="rsl-text-editor-key">Y</span></span>',
            '    </div>',
            '  </div>',
            '  <p class="rsl-text-editor-shortcuts-hint">Press <span class="rsl-text-editor-key">Esc</span> or click outside to close</p>',
            '</div>'
        ].join('\n');

        document.body.appendChild(this.dom.shortcutsOverlay);

        // Close on click outside or Escape
        this.dom.shortcutsOverlay.addEventListener('click', function(e) {
            if (e.target === self.dom.shortcutsOverlay) {
                self._closeShortcuts();
            }
        });

        this.dom.shortcutsOverlay.addEventListener('keydown', function(e) {
            if (e.key === 'Escape') {
                self._closeShortcuts();
            }
        });
    };

    /**
     * Bind event listeners
     */
    TextEditor.prototype._bindEvents = function() {
        var self = this;

        // Content events
        this.dom.content.addEventListener('input', function() {
            self._onInput();
        });

        this.dom.content.addEventListener('focus', function() {
            self._onFocus();
        });

        this.dom.content.addEventListener('blur', function() {
            self._onBlur();
        });

        this.dom.content.addEventListener('paste', function(e) {
            self._onPaste(e);
        });

        this.dom.content.addEventListener('keydown', function(e) {
            self._onKeydown(e);
        });

        // Selection change for updating button states
        document.addEventListener('selectionchange', function() {
            if (document.activeElement === self.dom.content) {
                self._updateButtonStates();
            }
        });

        // Source textarea sync
        this.dom.source.addEventListener('input', function() {
            self._onSourceInput();
        });
    };

    /**
     * Handle content input
     */
    TextEditor.prototype._onInput = function() {
        this._updateCounts();
        this._dispatchEvent('rsl-editor-change', {
            html: this.getValue(),
            text: this.getText(),
            wordCount: this.state.wordCount,
            charCount: this.state.charCount
        });
    };

    /**
     * Handle focus
     */
    TextEditor.prototype._onFocus = function() {
        if (this.config.focusMode) {
            this.element.classList.add('focus-mode');
        }
        this._dispatchEvent('rsl-editor-focus');
    };

    /**
     * Handle blur
     */
    TextEditor.prototype._onBlur = function() {
        if (this.config.focusMode) {
            this.element.classList.remove('focus-mode');
        }
        this._dispatchEvent('rsl-editor-blur');
    };

    /**
     * Handle paste
     */
    TextEditor.prototype._onPaste = function(e) {
        if (this.config.pasteAsText) {
            e.preventDefault();
            var text = (e.clipboardData || window.clipboardData).getData('text/plain');
            document.execCommand('insertText', false, text);
        } else {
            // Clean paste - remove unwanted formatting
            var self = this;
            setTimeout(function() {
                self._cleanPaste();
            }, 0);
        }
    };

    /**
     * Clean pasted content
     */
    TextEditor.prototype._cleanPaste = function() {
        var content = this.dom.content.innerHTML;

        // Remove Word/Google Docs specific tags and attributes
        content = content.replace(/<o:p[^>]*>[\s\S]*?<\/o:p>/gi, '');
        content = content.replace(/<w:[^>]*>[\s\S]*?<\/w:[^>]*>/gi, '');
        content = content.replace(/class="[^"]*Mso[^"]*"/gi, '');
        content = content.replace(/style="[^"]*mso-[^"]*"/gi, '');
        content = content.replace(/<\/?span[^>]*>/gi, '');
        content = content.replace(/<\/?font[^>]*>/gi, '');
        content = content.replace(/<!--[\s\S]*?-->/g, '');

        // Clean up empty paragraphs
        content = content.replace(/<p>\s*<\/p>/gi, '');
        content = content.replace(/<p><br\s*\/?><\/p>/gi, '');

        // Remove style attributes from all elements
        content = content.replace(/\s*style="[^"]*"/gi, '');

        this.dom.content.innerHTML = content;
    };

    /**
     * Handle keydown
     */
    TextEditor.prototype._onKeydown = function(e) {
        var isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
        var ctrlKey = isMac ? e.metaKey : e.ctrlKey;

        // Keyboard shortcuts
        if (ctrlKey) {
            switch (e.key.toLowerCase()) {
                case 'b':
                    e.preventDefault();
                    this.execCommand('bold');
                    break;
                case 'i':
                    e.preventDefault();
                    this.execCommand('italic');
                    break;
                case 'u':
                    e.preventDefault();
                    this.execCommand('underline');
                    break;
                case 'k':
                    e.preventDefault();
                    this._openLinkDialog();
                    break;
            }
        }

        // Show shortcuts on ?
        if (e.key === '?' && !ctrlKey && !e.shiftKey) {
            // Only show if not typing in an input-like context
            var sel = window.getSelection();
            if (sel.isCollapsed && this.dom.content.textContent.length === 0) {
                e.preventDefault();
                this._openShortcuts();
            }
        }
    };

    /**
     * Handle source input
     */
    TextEditor.prototype._onSourceInput = function() {
        // Will sync when switching back to WYSIWYG
    };

    /**
     * Handle button click
     */
    TextEditor.prototype._handleButtonClick = function(name, def) {
        if (def.action) {
            // Custom action
            switch (def.action) {
                case 'link':
                    this._openLinkDialog();
                    break;
                case 'image':
                    this._openImageDialog();
                    break;
                case 'source':
                    this.toggleSource();
                    break;
            }
        } else if (def.command) {
            // execCommand
            if (def.value) {
                this.execCommand(def.command, def.value);
            } else {
                this.execCommand(def.command);
            }
        }

        // Update button states
        this._updateButtonStates();

        // Announce action
        this._announce(def.tooltip.split('(')[0].trim() + ' applied');
    };

    /**
     * Execute a command
     */
    TextEditor.prototype.execCommand = function(command, value) {
        this.dom.content.focus();

        if (command === 'formatBlock' && value) {
            document.execCommand('formatBlock', false, '<' + value + '>');
        } else {
            document.execCommand(command, false, value || null);
        }

        this._updateButtonStates();
        this._dispatchEvent('rsl-editor-command', { command: command, value: value });
    };

    /**
     * Set heading
     */
    TextEditor.prototype._setHeading = function(tag) {
        this.dom.content.focus();
        document.execCommand('formatBlock', false, '<' + tag + '>');
        this._announce('Format changed to ' + (tag === 'p' ? 'paragraph' : tag.toUpperCase()));
    };

    /**
     * Update button states (active/inactive)
     */
    TextEditor.prototype._updateButtonStates = function() {
        var self = this;
        var buttons = this.dom.toolbar ? this.dom.toolbar.querySelectorAll('.rsl-text-editor-btn[data-command]') : [];

        buttons.forEach(function(btn) {
            var command = btn.getAttribute('data-command');
            var def = TextEditor.BUTTONS[command];

            if (def && def.toggle) {
                var isActive = false;
                try {
                    isActive = document.queryCommandState(def.command);
                } catch (e) {
                    // Some commands don't support queryCommandState
                }
                btn.setAttribute('aria-pressed', String(isActive));
                btn.classList.toggle('active', isActive);
            }
        });

        // Update heading dropdown
        if (this.dom.headingDropdown) {
            var block = document.queryCommandValue('formatBlock');
            var label = 'Paragraph';
            TextEditor.HEADINGS.forEach(function(h) {
                if (block.toLowerCase() === h.value) {
                    label = h.label;
                }
            });
            this.dom.headingDropdown.button.querySelector('span').textContent = label;
        }
    };

    /**
     * Update word/character counts
     */
    TextEditor.prototype._updateCounts = function() {
        var text = this.dom.content.textContent || '';
        var words = text.trim().split(/\s+/).filter(function(w) { return w.length > 0; });

        this.state.wordCount = words.length;
        this.state.charCount = text.length;

        if (this.dom.wordCountEl) {
            this.dom.wordCountEl.textContent = this.state.wordCount;
        }

        if (this.dom.charCountEl) {
            this.dom.charCountEl.textContent = this.state.charCount;
        }

        // Update word limit warning
        if (this.config.maxWords && this.dom.wordCountContainer) {
            var wordPercent = (this.state.wordCount / this.config.maxWords) * 100;
            this.dom.wordCountContainer.classList.remove('warning', 'danger');

            if (wordPercent >= 100) {
                this.dom.wordCountContainer.classList.add('danger');
            } else if (wordPercent >= 80) {
                this.dom.wordCountContainer.classList.add('warning');
            }

            if (this.dom.wordProgressBar) {
                this.dom.wordProgressBar.style.width = Math.min(wordPercent, 100) + '%';
                this.dom.wordProgressBar.classList.remove('warning', 'danger');
                if (wordPercent >= 100) {
                    this.dom.wordProgressBar.classList.add('danger');
                } else if (wordPercent >= 80) {
                    this.dom.wordProgressBar.classList.add('warning');
                }
            }
        }

        // Update character limit warning
        if (this.config.maxChars && this.dom.charCountContainer) {
            var charPercent = (this.state.charCount / this.config.maxChars) * 100;
            this.dom.charCountContainer.classList.remove('warning', 'danger');

            if (charPercent >= 100) {
                this.dom.charCountContainer.classList.add('danger');
            } else if (charPercent >= 80) {
                this.dom.charCountContainer.classList.add('warning');
            }

            if (this.dom.charProgressBar) {
                this.dom.charProgressBar.style.width = Math.min(charPercent, 100) + '%';
                this.dom.charProgressBar.classList.remove('warning', 'danger');
                if (charPercent >= 100) {
                    this.dom.charProgressBar.classList.add('danger');
                } else if (charPercent >= 80) {
                    this.dom.charProgressBar.classList.add('warning');
                }
            }
        }
    };

    /**
     * Open link dialog
     */
    TextEditor.prototype._openLinkDialog = function() {
        // Save selection
        this.state.selection = this._saveSelection();

        // Get selected text
        var selectedText = window.getSelection().toString();

        // Clear and populate fields
        var urlInput = this.dom.linkDialog.querySelector('#rsl-editor-link-url');
        var textInput = this.dom.linkDialog.querySelector('#rsl-editor-link-text');
        urlInput.value = '';
        textInput.value = selectedText;

        this.dom.linkDialog.classList.add('open');
        urlInput.focus();

        // Trap focus
        this._trapFocus(this.dom.linkDialog);
    };

    /**
     * Close link dialog
     */
    TextEditor.prototype._closeLinkDialog = function() {
        this.dom.linkDialog.classList.remove('open');
        this.dom.content.focus();

        if (this.state.selection) {
            this._restoreSelection(this.state.selection);
        }
    };

    /**
     * Insert link
     */
    TextEditor.prototype._insertLink = function() {
        var url = this.dom.linkDialog.querySelector('#rsl-editor-link-url').value.trim();
        var text = this.dom.linkDialog.querySelector('#rsl-editor-link-text').value.trim();

        if (!url) {
            this._announce('Please enter a URL');
            return;
        }

        // Add protocol if missing
        if (!/^https?:\/\//i.test(url)) {
            url = 'https://' + url;
        }

        this._closeLinkDialog();

        // Restore selection
        if (this.state.selection) {
            this._restoreSelection(this.state.selection);
        }

        if (text) {
            // Insert link with custom text
            var link = '<a href="' + this._escapeHtml(url) + '">' + this._escapeHtml(text) + '</a>';
            document.execCommand('insertHTML', false, link);
        } else {
            // Wrap selection in link
            document.execCommand('createLink', false, url);
        }

        this._announce('Link inserted');
        this._onInput();
    };

    /**
     * Open image dialog
     */
    TextEditor.prototype._openImageDialog = function() {
        this.state.selection = this._saveSelection();

        var urlInput = this.dom.imageDialog.querySelector('#rsl-editor-image-url');
        var altInput = this.dom.imageDialog.querySelector('#rsl-editor-image-alt');
        urlInput.value = '';
        altInput.value = '';

        this.dom.imageDialog.classList.add('open');
        urlInput.focus();

        this._trapFocus(this.dom.imageDialog);
    };

    /**
     * Close image dialog
     */
    TextEditor.prototype._closeImageDialog = function() {
        this.dom.imageDialog.classList.remove('open');
        this.dom.content.focus();

        if (this.state.selection) {
            this._restoreSelection(this.state.selection);
        }
    };

    /**
     * Insert image
     */
    TextEditor.prototype._insertImage = function() {
        var url = this.dom.imageDialog.querySelector('#rsl-editor-image-url').value.trim();
        var alt = this.dom.imageDialog.querySelector('#rsl-editor-image-alt').value.trim();

        if (!url) {
            this._announce('Please enter an image URL');
            return;
        }

        if (!alt) {
            this._announce('Please enter alt text for accessibility');
            return;
        }

        this._closeImageDialog();

        if (this.state.selection) {
            this._restoreSelection(this.state.selection);
        }

        var img = '<img src="' + this._escapeHtml(url) + '" alt="' + this._escapeHtml(alt) + '">';
        document.execCommand('insertHTML', false, img);

        this._announce('Image inserted');
        this._onInput();
    };

    /**
     * Toggle source view
     */
    TextEditor.prototype.toggleSource = function() {
        this.state.sourceView = !this.state.sourceView;

        if (this.state.sourceView) {
            // Switch to source
            this.dom.source.value = this._formatHTML(this.dom.content.innerHTML);
            this.element.setAttribute('data-source-view', 'true');
            this.dom.source.focus();
            if (this.dom.sourceIndicator) {
                this.dom.sourceIndicator.classList.remove('hidden');
            }
        } else {
            // Switch back to WYSIWYG
            this.dom.content.innerHTML = this.dom.source.value;
            this.element.removeAttribute('data-source-view');
            this.dom.content.focus();
            if (this.dom.sourceIndicator) {
                this.dom.sourceIndicator.classList.add('hidden');
            }
        }

        // Update source button state
        var sourceBtn = this.dom.toolbar ? this.dom.toolbar.querySelector('[data-command="source"]') : null;
        if (sourceBtn) {
            sourceBtn.setAttribute('aria-pressed', String(this.state.sourceView));
            sourceBtn.classList.toggle('active', this.state.sourceView);
        }

        this._announce(this.state.sourceView ? 'Source view enabled' : 'Visual editor enabled');
    };

    /**
     * Format HTML for source view
     */
    TextEditor.prototype._formatHTML = function(html) {
        // Simple formatting - add newlines after block elements
        return html
            .replace(/></g, '>\n<')
            .replace(/\n\n+/g, '\n')
            .trim();
    };

    /**
     * Open shortcuts overlay
     */
    TextEditor.prototype._openShortcuts = function() {
        this.dom.shortcutsOverlay.classList.add('open');
        this.dom.shortcutsOverlay.focus();
    };

    /**
     * Close shortcuts overlay
     */
    TextEditor.prototype._closeShortcuts = function() {
        this.dom.shortcutsOverlay.classList.remove('open');
        this.dom.content.focus();
    };

    /**
     * Save selection
     */
    TextEditor.prototype._saveSelection = function() {
        var sel = window.getSelection();
        if (sel.rangeCount > 0) {
            return sel.getRangeAt(0).cloneRange();
        }
        return null;
    };

    /**
     * Restore selection
     */
    TextEditor.prototype._restoreSelection = function(range) {
        if (range) {
            var sel = window.getSelection();
            sel.removeAllRanges();
            sel.addRange(range);
        }
    };

    /**
     * Trap focus within an element
     */
    TextEditor.prototype._trapFocus = function(container) {
        var focusable = container.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
        var first = focusable[0];
        var last = focusable[focusable.length - 1];

        container.addEventListener('keydown', function handler(e) {
            if (e.key === 'Tab') {
                if (e.shiftKey) {
                    if (document.activeElement === first) {
                        e.preventDefault();
                        last.focus();
                    }
                } else {
                    if (document.activeElement === last) {
                        e.preventDefault();
                        first.focus();
                    }
                }
            }
        });
    };

    /**
     * Announce message to screen readers
     */
    TextEditor.prototype._announce = function(message) {
        this.dom.liveRegion.textContent = message;
        setTimeout(function() {
            this.dom.liveRegion.textContent = '';
        }.bind(this), 1000);
    };

    /**
     * Escape HTML
     */
    TextEditor.prototype._escapeHtml = function(str) {
        var div = document.createElement('div');
        div.textContent = str;
        return div.innerHTML;
    };

    /**
     * Dispatch custom event
     */
    TextEditor.prototype._dispatchEvent = function(name, detail) {
        var event = new CustomEvent(name, {
            bubbles: true,
            detail: detail || {}
        });
        this.element.dispatchEvent(event);
    };

    // ========================================================================
    // Public API Methods
    // ========================================================================

    /**
     * Get HTML content (normalized)
     */
    TextEditor.prototype.getValue = function() {
        if (this.state.sourceView) {
            return this.dom.source.value;
        }
        return this._normalizeHTML(this.dom.content.innerHTML);
    };

    /**
     * Normalize HTML output - wrap loose text in <p> tags, convert <div> to <p>
     */
    TextEditor.prototype._normalizeHTML = function(html) {
        if (!html || !html.trim()) return '';

        // Create a temporary container to work with the HTML
        var temp = document.createElement('div');
        temp.innerHTML = html;

        // Helper to check if element is empty (only contains <br> or whitespace)
        function isEmpty(el) {
            var content = el.innerHTML.trim();
            return !content || content === '<br>' || content === '<br/>';
        }

        // Helper to check if element is a blank line (intentional empty paragraph)
        function isBlankLine(el) {
            var content = el.innerHTML.trim();
            return content === '<br>' || content === '<br/>';
        }

        // Process child nodes
        var fragment = document.createDocumentFragment();
        var currentP = null;

        Array.prototype.slice.call(temp.childNodes).forEach(function(node) {
            if (node.nodeType === Node.TEXT_NODE) {
                // Text node - wrap in <p> if it has content
                var text = node.textContent;
                if (text.trim() || text.includes('\u00A0')) { // \u00A0 is &nbsp;
                    if (!currentP) {
                        currentP = document.createElement('p');
                        fragment.appendChild(currentP);
                    }
                    currentP.appendChild(node.cloneNode());
                }
            } else if (node.nodeType === Node.ELEMENT_NODE) {
                var tagName = node.tagName.toLowerCase();

                // Convert <div> to <p> (unless it contains block elements)
                if (tagName === 'div' && !node.querySelector('div, p, h1, h2, h3, h4, h5, h6, ul, ol, blockquote, table')) {
                    // Skip empty divs that just contain <br>
                    if (isEmpty(node)) {
                        return;
                    }
                    var p = document.createElement('p');
                    p.innerHTML = node.innerHTML;
                    // Copy any inline styles
                    if (node.getAttribute('style')) {
                        p.setAttribute('style', node.getAttribute('style'));
                    }
                    fragment.appendChild(p);
                    currentP = null;
                } else if (tagName === 'p') {
                    // Convert <p><br></p> to clean empty <p></p> for blank lines
                    if (isBlankLine(node)) {
                        var emptyP = document.createElement('p');
                        fragment.appendChild(emptyP);
                    } else if (!isEmpty(node)) {
                        fragment.appendChild(node.cloneNode(true));
                    }
                    currentP = null;
                } else if (['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'blockquote', 'table', 'pre', 'hr'].indexOf(tagName) !== -1) {
                    // Block elements - add directly
                    fragment.appendChild(node.cloneNode(true));
                    currentP = null;
                } else if (tagName === 'br') {
                    // <br> - if we have a current paragraph, close it
                    if (currentP) {
                        currentP = null;
                    }
                    // Don't add standalone <br> tags - they'll be paragraph breaks
                } else {
                    // Inline elements (span, strong, em, etc.) - wrap in <p>
                    if (!currentP) {
                        currentP = document.createElement('p');
                        fragment.appendChild(currentP);
                    }
                    currentP.appendChild(node.cloneNode(true));
                }
            }
        });

        // Build result
        var result = document.createElement('div');
        result.appendChild(fragment);
        return result.innerHTML;
    };

    /**
     * Set HTML content
     */
    TextEditor.prototype.setValue = function(html) {
        this.dom.content.innerHTML = html || '';
        if (this.state.sourceView) {
            this.dom.source.value = this._formatHTML(html || '');
        }
        this._updateCounts();
    };

    /**
     * Get plain text content
     */
    TextEditor.prototype.getText = function() {
        var html = this.dom.content.innerHTML;

        // Replace block-level tags with newlines
        var text = html
            // Replace <br> with newline
            .replace(/<br\s*\/?>/gi, '\n')
            // Add newline BEFORE opening block tags (to handle loose text before first <p>)
            .replace(/<(p|div|h[1-6]|blockquote)(?:\s[^>]*)?>(?!<\/\1>)/gi, '\n')
            // Replace closing block tags with newline
            .replace(/<\/(p|div|h[1-6]|tr|blockquote)>/gi, '\n')
            // Handle list items - add bullet/number (simplified: just use bullet)
            .replace(/<li[^>]*>/gi, '• ')
            .replace(/<\/li>/gi, '\n')
            // Remove all remaining HTML tags
            .replace(/<[^>]+>/g, '')
            // Decode common HTML entities
            .replace(/&nbsp;/g, ' ')
            .replace(/&amp;/g, '&')
            .replace(/&lt;/g, '<')
            .replace(/&gt;/g, '>')
            .replace(/&quot;/g, '"')
            .replace(/&#39;/g, "'")
            // Clean up multiple line breaks (3+ becomes 2)
            .replace(/\n{3,}/g, '\n\n')
            // Trim whitespace
            .trim();

        return text;
    };

    /**
     * Get word count
     */
    TextEditor.prototype.getWordCount = function() {
        return this.state.wordCount;
    };

    /**
     * Get character count
     */
    TextEditor.prototype.getCharCount = function() {
        return this.state.charCount;
    };

    /**
     * Focus the editor
     */
    TextEditor.prototype.focus = function() {
        this.dom.content.focus();
    };

    /**
     * Blur the editor
     */
    TextEditor.prototype.blur = function() {
        this.dom.content.blur();
    };

    /**
     * Insert HTML at cursor
     */
    TextEditor.prototype.insertHTML = function(html) {
        this.dom.content.focus();
        document.execCommand('insertHTML', false, html);
        this._onInput();
    };

    /**
     * Clear content
     */
    TextEditor.prototype.clear = function() {
        this.dom.content.innerHTML = '';
        this.dom.source.value = '';
        this._updateCounts();
    };

    /**
     * Destroy the editor
     */
    TextEditor.prototype.destroy = function() {
        // Remove dialogs from body
        if (this.dom.linkDialog && this.dom.linkDialog.parentNode) {
            this.dom.linkDialog.parentNode.removeChild(this.dom.linkDialog);
        }
        if (this.dom.imageDialog && this.dom.imageDialog.parentNode) {
            this.dom.imageDialog.parentNode.removeChild(this.dom.imageDialog);
        }
        if (this.dom.shortcutsOverlay && this.dom.shortcutsOverlay.parentNode) {
            this.dom.shortcutsOverlay.parentNode.removeChild(this.dom.shortcutsOverlay);
        }

        // Clear element
        this.element.innerHTML = '';
        this.element.classList.remove('rsl-text-editor');
        this.element.removeAttribute('data-editor-toolbar');
        this.element.removeAttribute('data-editor-read-only');
        this.element.removeAttribute('data-source-view');

        // Remove from instances
        instances.delete(this.element);
    };

    // ========================================================================
    // Public API (Static)
    // ========================================================================

    window.RSL.TextEditor = {
        /**
         * Initialize all editors on page
         */
        init: function() {
            var elements = document.querySelectorAll('[data-rsl-text-editor]');
            elements.forEach(function(el) {
                if (!instances.has(el)) {
                    instances.set(el, new TextEditor(el));
                }
            });
        },

        /**
         * Create editor on specific element
         */
        create: function(element, options) {
            if (typeof element === 'string') {
                element = document.querySelector(element);
            }
            if (!element) return null;

            if (instances.has(element)) {
                return instances.get(element);
            }

            var instance = new TextEditor(element, options);
            instances.set(element, instance);
            return instance;
        },

        /**
         * Get editor instance
         */
        getInstance: function(element) {
            if (typeof element === 'string') {
                element = document.querySelector(element);
            }
            return instances.get(element) || null;
        },

        /**
         * Destroy editor instance
         */
        destroy: function(element) {
            if (typeof element === 'string') {
                element = document.querySelector(element);
            }
            var instance = instances.get(element);
            if (instance) {
                instance.destroy();
            }
        }
    };

    // Auto-initialize on DOMContentLoaded
    document.addEventListener('DOMContentLoaded', function() {
        window.RSL.TextEditor.init();
    });

})();
