/* ============================================================
   RichTextBox Inline Toolbar Addon
   Adds bubble menu, link popup, image toolbar, and context menu
   to the RichTextBox editor control.
   ============================================================ */
(function () {
  'use strict';

  var ICONS = {
    bold: '<svg viewBox="0 0 20 20" fill="none"><path d="M6 4H11C12.66 4 14 5.34 14 7C14 8.1 13.4 9.06 12.5 9.6C13.6 10.1 14.5 11.2 14.5 12.5C14.5 14.43 12.93 16 11 16H6V4Z" fill="#1A1A1A"/><path d="M8.5 6V9H11C11.83 9 12.5 8.33 12.5 7.5C12.5 6.67 11.83 6 11 6H8.5Z" fill="white"/><path d="M8.5 11V14H11C12.1 14 13 13.1 13 12.5C13 11.67 12.33 11 11.5 11H8.5Z" fill="white"/></svg>',
    italic: '<svg viewBox="0 0 20 20" fill="none"><line x1="7" y1="16" x2="11" y2="16" stroke="#4A4A4A" stroke-width="1.8" stroke-linecap="round"/><line x1="9" y1="4" x2="13" y2="4" stroke="#4A4A4A" stroke-width="1.8" stroke-linecap="round"/><line x1="9" y1="16" x2="11" y2="4" stroke="#4A4A4A" stroke-width="2" stroke-linecap="round"/></svg>',
    underline: '<svg viewBox="0 0 20 20" fill="none"><path d="M6 4V10C6 12.21 7.79 14 10 14C12.21 14 14 12.21 14 10V4" stroke="#4A4A4A" stroke-width="2" stroke-linecap="round"/><line x1="5" y1="17" x2="15" y2="17" stroke="#4A90D9" stroke-width="1.8" stroke-linecap="round"/></svg>',
    strikethrough: '<svg viewBox="0 0 20 20" fill="none"><line x1="3" y1="10" x2="17" y2="10" stroke="#EF4444" stroke-width="1.5" stroke-linecap="round"/><path d="M7 10C7 7.8 8.34 6 10 6C11.3 6 12.4 6.9 12.8 8" stroke="#4A4A4A" stroke-width="1.8" stroke-linecap="round"/><path d="M13 10C13 12.2 11.66 14 10 14C8.7 14 7.6 13.1 7.2 12" stroke="#4A4A4A" stroke-width="1.8" stroke-linecap="round"/></svg>',
    insertlink: '<svg viewBox="0 0 20 20" fill="none"><path d="M8.5 11.5L11.5 8.5C12.88 7.12 15.12 7.12 16.5 8.5C17.88 9.88 17.88 12.12 16.5 13.5L14 16" stroke="#4A90D9" stroke-width="1.8" stroke-linecap="round"/><path d="M11.5 8.5L8.5 11.5C7.12 12.88 4.88 12.88 3.5 11.5C2.12 10.12 2.12 7.88 3.5 6.5L6 4" stroke="#4A90D9" stroke-width="1.8" stroke-linecap="round"/></svg>',
    unlink: '<svg viewBox="0 0 20 20" fill="none"><path d="M8.5 11.5L11.5 8.5C12.88 7.12 15.12 7.12 16.5 8.5C17.88 9.88 17.88 12.12 16.5 13.5L14 16" stroke="#9CA3AF" stroke-width="1.8" stroke-linecap="round"/><path d="M11.5 8.5L8.5 11.5C7.12 12.88 4.88 12.88 3.5 11.5C2.12 10.12 2.12 7.88 3.5 6.5L6 4" stroke="#9CA3AF" stroke-width="1.8" stroke-linecap="round"/><line x1="3" y1="17" x2="17" y2="3" stroke="#EF4444" stroke-width="1.8" stroke-linecap="round"/></svg>',
    forecolor: '<svg viewBox="0 0 20 20" fill="none"><path d="M6 14L10 3L14 14" stroke="#4A4A4A" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/><line x1="7.2" y1="11" x2="12.8" y2="11" stroke="#4A4A4A" stroke-width="1.5" stroke-linecap="round"/><rect x="3" y="16" width="14" height="2.5" rx="0.5" fill="#EF4444"/></svg>',
    backcolor: '<svg viewBox="0 0 20 20" fill="none"><rect x="3" y="3" width="14" height="12" rx="1" fill="#FEF3C7" stroke="#D97706" stroke-width="0.8"/><path d="M7 12L10 5L13 12" stroke="#4A4A4A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><line x1="8" y1="10" x2="12" y2="10" stroke="#4A4A4A" stroke-width="1.2" stroke-linecap="round"/><rect x="3" y="16" width="14" height="2.5" rx="0.5" fill="#FBBF24"/></svg>',
    editlink: '<svg viewBox="0 0 20 20" fill="none"><path d="M11.5 7.5L12.5 6.5C13.33 5.67 14.67 5.67 15.5 6.5C16.33 7.33 16.33 8.67 15.5 9.5L13 12C12.17 12.83 10.83 12.83 10 12L9.5 11.5" stroke="#4A90D9" stroke-width="1.5" stroke-linecap="round"/><path d="M8.5 12.5L7.5 13.5C6.67 14.33 5.33 14.33 4.5 13.5C3.67 12.67 3.67 11.33 4.5 10.5L7 8C7.83 7.17 9.17 7.17 10 8L10.5 8.5" stroke="#4A90D9" stroke-width="1.5" stroke-linecap="round"/><circle cx="15.5" cy="15.5" r="3.5" fill="white" stroke="#F59E0B" stroke-width="0.5"/><path d="M14 15.5L15 14.5L17 16.5L16 17.5L14 15.5Z" fill="#F59E0B"/></svg>',
    openlink: '<svg viewBox="0 0 20 20" fill="none"><path d="M15 11V15C15 16.1 14.1 17 13 17H5C3.9 17 3 16.1 3 15V7C3 5.9 3.9 5 5 5H9" stroke="#4A90D9" stroke-width="1.5" stroke-linecap="round"/><path d="M12 3H17V8" stroke="#4A90D9" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><line x1="17" y1="3" x2="9" y2="11" stroke="#4A90D9" stroke-width="1.5" stroke-linecap="round"/></svg>',
    imgalignleft: '<svg viewBox="0 0 20 20" fill="none"><rect x="2" y="3" width="8" height="7" rx="1" fill="#4A90D9" opacity="0.8"/><rect x="12" y="3" width="6" height="1.5" rx="0.5" fill="#D1D5DB"/><rect x="12" y="6" width="6" height="1.5" rx="0.5" fill="#D1D5DB"/><rect x="2" y="12" width="16" height="1.5" rx="0.5" fill="#D1D5DB"/><rect x="2" y="15" width="12" height="1.5" rx="0.5" fill="#D1D5DB"/></svg>',
    imgaligncenter: '<svg viewBox="0 0 20 20" fill="none"><rect x="5" y="2" width="10" height="8" rx="1" fill="#4A90D9" opacity="0.8"/><rect x="2" y="12" width="16" height="1.5" rx="0.5" fill="#D1D5DB"/><rect x="4" y="15" width="12" height="1.5" rx="0.5" fill="#D1D5DB"/></svg>',
    imgalignright: '<svg viewBox="0 0 20 20" fill="none"><rect x="10" y="3" width="8" height="7" rx="1" fill="#4A90D9" opacity="0.8"/><rect x="2" y="3" width="6" height="1.5" rx="0.5" fill="#D1D5DB"/><rect x="2" y="6" width="6" height="1.5" rx="0.5" fill="#D1D5DB"/><rect x="2" y="12" width="16" height="1.5" rx="0.5" fill="#D1D5DB"/><rect x="2" y="15" width="12" height="1.5" rx="0.5" fill="#D1D5DB"/></svg>',
    alttext: '<svg viewBox="0 0 20 20" fill="none"><rect x="2" y="4" width="16" height="12" rx="1.5" fill="#F0F4F8" stroke="#9CB4CA" stroke-width="0.8"/><text x="5" y="12" font-family="Arial" font-size="7" font-weight="bold" fill="#6B7280">Alt</text></svg>',
    imgdelete: '<svg viewBox="0 0 20 20" fill="none"><rect x="3" y="3" width="14" height="11" rx="1.5" fill="#F0F4F8" stroke="#9CB4CA" stroke-width="0.8"/><circle cx="7" cy="7" r="1.5" fill="#FBBF24"/><path d="M3 11L7 8L10 10L13 7L17 11V13C17 13.55 16.55 14 16 14H4C3.45 14 3 13.55 3 13V11Z" fill="#86EFAC" opacity="0.6"/><circle cx="15" cy="15" r="4" fill="#EF4444"/><line x1="13.5" y1="15" x2="16.5" y2="15" stroke="white" stroke-width="1.5" stroke-linecap="round"/></svg>',
    undo: '<svg viewBox="0 0 20 20" fill="none"><path d="M5 8L3 10L5 12" stroke="#4A90D9" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M3.5 10H12C14.21 10 16 11.79 16 14C16 16.21 14.21 18 12 18H9" stroke="#4A90D9" stroke-width="1.8" stroke-linecap="round"/></svg>',
    redo: '<svg viewBox="0 0 20 20" fill="none"><path d="M15 8L17 10L15 12" stroke="#4A90D9" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M16.5 10H8C5.79 10 4 11.79 4 14C4 16.21 5.79 18 8 18H11" stroke="#4A90D9" stroke-width="1.8" stroke-linecap="round"/></svg>',
    cut: '<svg viewBox="0 0 20 20" fill="none"><circle cx="7" cy="15" r="2.5" fill="none" stroke="#EF4444" stroke-width="1.5"/><circle cx="13" cy="15" r="2.5" fill="none" stroke="#EF4444" stroke-width="1.5"/><line x1="8.5" y1="13" x2="12" y2="3" stroke="#6B7280" stroke-width="1.5" stroke-linecap="round"/><line x1="11.5" y1="13" x2="8" y2="3" stroke="#6B7280" stroke-width="1.5" stroke-linecap="round"/></svg>',
    copy: '<svg viewBox="0 0 20 20" fill="none"><rect x="6" y="6" width="10" height="11" rx="1" fill="#F0F4F8" stroke="#4A90D9" stroke-width="1"/><path d="M4 13V4C4 3.45 4.45 3 5 3H12" stroke="#4A90D9" stroke-width="1" stroke-linecap="round"/></svg>',
    paste: '<svg viewBox="0 0 20 20" fill="none"><rect x="4" y="4" width="12" height="14" rx="1" fill="#FDE68A" stroke="#D97706" stroke-width="0.8"/><rect x="7" y="2" width="6" height="4" rx="1" fill="#FBBF24" stroke="#D97706" stroke-width="0.8"/><rect x="9" y="1.5" width="2" height="2" rx="1" fill="#92400E"/></svg>',
    selectall: '<svg viewBox="0 0 20 20" fill="none"><rect x="3" y="3" width="14" height="14" rx="2" fill="#DBEAFE" stroke="#4A90D9" stroke-width="1"/><path d="M7 10L9 12L13 8" stroke="#4A90D9" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>',
    removeformat: '<svg viewBox="0 0 20 20" fill="none"><path d="M5 4H15" stroke="#6B7280" stroke-width="1.8" stroke-linecap="round"/><path d="M10 4V16" stroke="#6B7280" stroke-width="1.8" stroke-linecap="round"/><line x1="4" y1="17" x2="17" y2="4" stroke="#EF4444" stroke-width="2" stroke-linecap="round"/></svg>'
  };

  function el(tag, attrs) {
    var e = document.createElement(tag);
    if (attrs) {
      for (var k in attrs) {
        if (k === 'className') e.className = attrs[k];
        else if (k === 'innerHTML') e.innerHTML = attrs[k];
        else if (k === 'textContent') e.textContent = attrs[k];
        else if (k === 'onClick') e.addEventListener('click', attrs[k]);
        else e.setAttribute(k, attrs[k]);
      }
    }
    return e;
  }

  function removeEl(e) { if (e && e.parentNode) e.parentNode.removeChild(e); }

  function getClosest(node, selector, doc) {
    if (!node) return null;
    if (node.nodeType === 3) node = node.parentNode;
    if (!node || !node.closest) return null;
    var result = node.closest(selector);
    if (result === doc.body) return null;
    return result;
  }

  function hookEditor(editor) {
    var iframe = editor.iframe;
    if (!iframe) return;
    var iframeDoc, iframeWin;
    try {
      iframeWin = iframe.contentWindow;
      iframeDoc = iframe.contentDocument || iframeWin.document;
    } catch (e) { return; }
    if (!iframeDoc || !iframeDoc.body) return;

    var bubbleEl = null, linkPopupEl = null, imageToolbarEl = null, contextMenuEl = null;

    function execCmd(cmd) {
      editor.focus();
      editor.execCommand(cmd);
    }

    function execCmdWithValue(cmd, value) {
      editor.focus();
      editor.execCommand(cmd, value);
    }

    // ── Bubble Menu ──
    function createBubbleMenu() {
      bubbleEl = el('div', { className: 'rit-bubble-menu' });
      bubbleEl.style.display = 'none';
      var cmds = [
        { cmd: 'bold', icon: 'bold', title: 'Bold' },
        { cmd: 'italic', icon: 'italic', title: 'Italic' },
        { cmd: 'underline', icon: 'underline', title: 'Underline' },
        { cmd: 'strikethrough', icon: 'strikethrough', title: 'Strikethrough' },
        'sep',
        { cmd: 'insertlink', icon: 'insertlink', title: 'Insert Link' },
        'sep',
        { cmd: 'forecolor', icon: 'forecolor', title: 'Text Color (Red)', defaultValue: 'red' },
        { cmd: 'backcolor', icon: 'backcolor', title: 'Highlight (Yellow)', defaultValue: 'yellow' }
      ];
      cmds.forEach(function (item) {
        if (item === 'sep') { bubbleEl.appendChild(el('div', { className: 'rit-bubble-sep' })); return; }
        bubbleEl.appendChild(el('button', {
          className: 'rit-btn', title: item.title, innerHTML: ICONS[item.icon],
          onClick: function (e) {
            e.preventDefault(); e.stopPropagation();
            if (item.defaultValue) {
              execCmdWithValue(item.cmd, item.defaultValue);
            } else {
              execCmd(item.cmd);
            }
          }
        }));
      });
      // Prevent mousedown on the bubble menu from stealing focus from the iframe.
      // This is critical: without it, the iframe selection is lost before the
      // click handler fires, and commands like forecolor that need a selection fail.
      bubbleEl.addEventListener('mousedown', function (e) {
        e.preventDefault();
      });
      document.body.appendChild(bubbleEl);
    }

    function showBubbleMenu() {
      var sel = iframeWin.getSelection();
      if (!sel || sel.isCollapsed || !sel.toString().trim()) { hideBubbleMenu(); return; }
      if (getClosest(sel.anchorNode, 'a', iframeDoc)) return;
      var range = sel.getRangeAt(0);
      var rect = range.getBoundingClientRect();
      var ir = iframe.getBoundingClientRect();
      bubbleEl.style.display = 'flex';
      bubbleEl.style.top = Math.max(0, ir.top + rect.top - 44) + 'px';
      bubbleEl.style.left = (ir.left + rect.left + rect.width / 2) + 'px';
      bubbleEl.style.transform = 'translateX(-50%)';
    }

    function hideBubbleMenu() { if (bubbleEl) bubbleEl.style.display = 'none'; }

    // ── Link Popup ──
    function showLinkPopup(link) {
      hideLinkPopup();
      linkPopupEl = el('div', { className: 'rit-link-popup' });
      linkPopupEl.appendChild(el('a', { href: link.href, textContent: link.href, target: '_blank', rel: 'noopener' }));
      linkPopupEl.appendChild(el('div', { className: 'rit-link-sep' }));
      linkPopupEl.appendChild(el('button', {
        className: 'rit-btn', title: 'Edit link', innerHTML: ICONS.editlink,
        onClick: function (e) {
          e.preventDefault(); hideLinkPopup();
          var r = iframeDoc.createRange(); r.selectNode(link);
          var s = iframeWin.getSelection(); s.removeAllRanges(); s.addRange(r);
          execCmd('insertlink');
        }
      }));
      linkPopupEl.appendChild(el('button', {
        className: 'rit-btn', title: 'Open link', innerHTML: ICONS.openlink,
        onClick: function (e) { e.preventDefault(); window.open(link.href, '_blank'); }
      }));
      linkPopupEl.appendChild(el('button', {
        className: 'rit-btn', title: 'Remove link', innerHTML: ICONS.unlink,
        onClick: function (e) {
          e.preventDefault(); hideLinkPopup();
          var r = iframeDoc.createRange(); r.selectNode(link);
          var s = iframeWin.getSelection(); s.removeAllRanges(); s.addRange(r);
          execCmd('unlink');
        }
      }));
      var lr = link.getBoundingClientRect(), ir = iframe.getBoundingClientRect();
      linkPopupEl.style.top = (ir.top + lr.bottom + 6) + 'px';
      linkPopupEl.style.left = (ir.left + lr.left) + 'px';
      linkPopupEl.addEventListener('mousedown', function (e) { e.preventDefault(); });
      document.body.appendChild(linkPopupEl);
    }

    function hideLinkPopup() { removeEl(linkPopupEl); linkPopupEl = null; }

    // ── Image Toolbar ──
    function showImageToolbar(img) {
      hideImageToolbar();
      imageToolbarEl = el('div', { className: 'rit-image-toolbar' });
      var aligns = [
        { icon: 'imgalignleft', title: 'Align left', fn: function () { img.style.cssFloat = 'left'; img.style.display = ''; img.style.margin = '0 12px 8px 0'; } },
        { icon: 'imgaligncenter', title: 'Center', fn: function () { img.style.cssFloat = 'none'; img.style.display = 'block'; img.style.margin = '8px auto'; } },
        { icon: 'imgalignright', title: 'Align right', fn: function () { img.style.cssFloat = 'right'; img.style.display = ''; img.style.margin = '0 0 8px 12px'; } }
      ];
      aligns.forEach(function (op) {
        imageToolbarEl.appendChild(el('button', {
          className: 'rit-btn', title: op.title, innerHTML: ICONS[op.icon],
          onClick: function (e) { e.preventDefault(); op.fn(); showImageToolbar(img); }
        }));
      });
      imageToolbarEl.appendChild(el('div', { className: 'rit-img-sep' }));
      imageToolbarEl.appendChild(el('span', { className: 'rit-img-size', textContent: (img.naturalWidth || '?') + 'x' + (img.naturalHeight || '?') }));
      imageToolbarEl.appendChild(el('div', { className: 'rit-img-sep' }));
      imageToolbarEl.appendChild(el('button', {
        className: 'rit-btn', title: 'Edit alt text', innerHTML: ICONS.alttext,
        onClick: function (e) { e.preventDefault(); var a = prompt('Alt text:', img.alt || ''); if (a !== null) img.alt = a; }
      }));
      imageToolbarEl.appendChild(el('button', {
        className: 'rit-btn', title: 'Delete image', innerHTML: ICONS.imgdelete,
        onClick: function (e) { e.preventDefault(); img.parentNode.removeChild(img); hideImageToolbar(); }
      }));
      var imgR = img.getBoundingClientRect(), ir = iframe.getBoundingClientRect();
      imageToolbarEl.style.top = (ir.top + imgR.top - 42) + 'px';
      imageToolbarEl.style.left = (ir.left + imgR.left + imgR.width / 2) + 'px';
      imageToolbarEl.style.transform = 'translateX(-50%)';
      document.body.appendChild(imageToolbarEl);
    }

    function hideImageToolbar() { removeEl(imageToolbarEl); imageToolbarEl = null; }

    // ── Context Menu ──
    function showContextMenu(e) {
      hideContextMenu();
      e.preventDefault();
      e.stopPropagation();
      var sel = iframeWin.getSelection();
      var hasSelection = sel && !sel.isCollapsed;
      var node = sel ? sel.anchorNode : null;
      var link = getClosest(node, 'a', iframeDoc);
      var img = e.target.tagName === 'IMG' ? e.target : null;
      var items = [];

      if (hasSelection) {
        items.push({ label: 'Cut', icon: 'cut', shortcut: 'Ctrl+X', cmd: 'cut' });
        items.push({ label: 'Copy', icon: 'copy', shortcut: 'Ctrl+C', cmd: 'copy' });
      }
      items.push({ label: 'Paste', icon: 'paste', shortcut: 'Ctrl+V', cmd: 'paste' });
      items.push('sep');
      items.push({ label: 'Undo', icon: 'undo', shortcut: 'Ctrl+Z', cmd: 'undo' });
      items.push({ label: 'Redo', icon: 'redo', shortcut: 'Ctrl+Y', cmd: 'redo' });
      items.push({ label: 'Select All', icon: 'selectall', shortcut: 'Ctrl+A', cmd: 'selectall' });
      items.push('sep');

      if (hasSelection) {
        items.push({ label: 'Bold', icon: 'bold', shortcut: 'Ctrl+B', cmd: 'bold' });
        items.push({ label: 'Italic', icon: 'italic', shortcut: 'Ctrl+I', cmd: 'italic' });
        items.push({ label: 'Underline', icon: 'underline', shortcut: 'Ctrl+U', cmd: 'underline' });
        items.push({ label: 'Remove Format', icon: 'removeformat', cmd: 'removeformat' });
        items.push('sep');
      }

      if (link) {
        items.push({ label: 'Edit Link', icon: 'editlink', cmd: 'insertlink' });
        items.push({ label: 'Open Link', icon: 'openlink', action: function () { window.open(link.href, '_blank'); } });
        items.push({ label: 'Remove Link', icon: 'unlink', cmd: 'unlink' });
        items.push('sep');
      }

      if (img) {
        items.push({ label: 'Align Left', icon: 'imgalignleft', action: function () { img.style.cssFloat = 'left'; img.style.display = ''; img.style.margin = '0 12px 8px 0'; } });
        items.push({ label: 'Center', icon: 'imgaligncenter', action: function () { img.style.cssFloat = 'none'; img.style.display = 'block'; img.style.margin = '8px auto'; } });
        items.push({ label: 'Align Right', icon: 'imgalignright', action: function () { img.style.cssFloat = 'right'; img.style.display = ''; img.style.margin = '0 0 8px 12px'; } });
        items.push({ label: 'Edit Alt Text', icon: 'alttext', action: function () { var a = prompt('Alt text:', img.alt || ''); if (a !== null) img.alt = a; } });
        items.push({ label: 'Delete Image', icon: 'imgdelete', action: function () { img.parentNode.removeChild(img); } });
        items.push('sep');
      }

      items.push({ label: 'Insert Link', icon: 'insertlink', cmd: 'insertlink' });
      while (items.length && items[items.length - 1] === 'sep') items.pop();

      contextMenuEl = el('div', { className: 'rit-context-menu' });
      items.forEach(function (item) {
        if (item === 'sep') { contextMenuEl.appendChild(el('div', { className: 'rit-context-separator' })); return; }
        var row = el('div', { className: 'rit-context-item' });
        row.appendChild(el('span', { className: 'rit-context-icon', innerHTML: ICONS[item.icon] || '' }));
        row.appendChild(el('span', { textContent: item.label }));
        if (item.shortcut) row.appendChild(el('span', { className: 'rit-context-shortcut', textContent: item.shortcut }));
        row.addEventListener('click', function (ev) {
          ev.preventDefault(); ev.stopPropagation(); hideContextMenu();
          if (item.action) item.action();
          else if (item.cmd) execCmd(item.cmd);
        });
        contextMenuEl.appendChild(row);
      });

      var ir = iframe.getBoundingClientRect();
      contextMenuEl.style.left = (ir.left + e.clientX) + 'px';
      contextMenuEl.style.top = (ir.top + e.clientY) + 'px';
      document.body.appendChild(contextMenuEl);
      var mr = contextMenuEl.getBoundingClientRect();
      if (mr.right > window.innerWidth) contextMenuEl.style.left = (window.innerWidth - mr.width - 4) + 'px';
      if (mr.bottom > window.innerHeight) contextMenuEl.style.top = (window.innerHeight - mr.height - 4) + 'px';
    }

    function hideContextMenu() { removeEl(contextMenuEl); contextMenuEl = null; }

    function hideAll() { hideBubbleMenu(); hideLinkPopup(); hideImageToolbar(); hideContextMenu(); }

    // ── Wire events ──
    createBubbleMenu();

    iframeDoc.addEventListener('mouseup', function (e) {
      hideContextMenu();
      setTimeout(function () {
        if (e.target.tagName === 'IMG') { hideBubbleMenu(); hideLinkPopup(); showImageToolbar(e.target); return; }
        var sel = iframeWin.getSelection();
        var node = sel ? sel.anchorNode : null;
        var link = getClosest(node, 'a', iframeDoc);
        if (link) { hideBubbleMenu(); hideImageToolbar(); showLinkPopup(link); return; }
        hideImageToolbar(); hideLinkPopup(); showBubbleMenu();
      }, 10);
    });

    iframeDoc.addEventListener('click', function (e) {
      if (e.target.tagName === 'IMG') { hideBubbleMenu(); hideLinkPopup(); showImageToolbar(e.target); }
    });

    iframeDoc.addEventListener('mousedown', function () { hideBubbleMenu(); hideLinkPopup(); hideContextMenu(); });
    iframeDoc.addEventListener('contextmenu', function (e) { showContextMenu(e); });
    document.addEventListener('mousedown', function (e) {
      if (bubbleEl && !bubbleEl.contains(e.target)) hideBubbleMenu();
      if (linkPopupEl && !linkPopupEl.contains(e.target)) hideLinkPopup();
      if (imageToolbarEl && !imageToolbarEl.contains(e.target)) hideImageToolbar();
      if (contextMenuEl && !contextMenuEl.contains(e.target)) hideContextMenu();
    });
    window.addEventListener('scroll', hideAll, true);
    window.addEventListener('resize', hideAll);
  }

  // ── Auto-detect and hook all RichTextBox editors ──
  function init() {
    var attempts = 0;
    var poll = setInterval(function () {
      attempts++;
      var editors = window.richTextBoxEditors;
      if (editors) {
        var keys = Object.keys(editors);
        for (var i = 0; i < keys.length; i++) {
          var ed = editors[keys[i]];
          if (ed && ed.iframe && !ed.__ritHooked) {
            ed.__ritHooked = true;
            hookEditor(ed);
          }
        }
      }
      // Keep polling for 30 seconds to catch late-loading editors
      if (attempts > 150) clearInterval(poll);
    }, 200);
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }
})();
