class CodemirrorEditor extends HTMLElement { constructor() { super(); this.instance = ''; this.host = window.location.origin; this.element = this.querySelector('textarea'); this.refresh = this.refresh.bind(this); // Observer instance to refresh the Editor when it become visible, eg after Tab switching this.intersectionObserver = new IntersectionObserver(entries => { if (entries[0].isIntersecting && this.instance) { this.instance.refresh(); } }, { threshold: 0 }); } static get observedAttributes() { return ['options']; } get options() { return JSON.parse(this.getAttribute('options')); } set options(value) { this.setAttribute('options', value); } attributeChangedCallback(attr, oldValue, newValue) { switch (attr) { case 'options': if (oldValue && newValue !== oldValue) { this.refresh(this.element); } break; } } async connectedCallback() { const cmPath = this.getAttribute('editor'); const addonsPath = this.getAttribute('addons'); await import(`${this.host}/${cmPath}`); if (this.options.keyMapUrl) { await import(`${this.host}/${this.options.keyMapUrl}`); } await import(`${this.host}/${addonsPath}`); const that = this; // For mode autoloading. window.CodeMirror.modeURL = this.getAttribute('mod-path'); // Fire this function any time an editor is created. window.CodeMirror.defineInitHook(editor => { // Try to set up the mode const mode = window.CodeMirror.findModeByName(editor.options.mode || '') || window.CodeMirror.findModeByExtension(editor.options.mode || ''); window.CodeMirror.autoLoadMode(editor, typeof mode === 'object' ? mode.mode : editor.options.mode); if (mode && mode.mime) { // Fix the x-php error if (['text/x-php', 'application/x-httpd-php', 'application/x-httpd-php-open'].includes(mode.mime)) { editor.setOption('mode', 'php'); } else { editor.setOption('mode', mode.mime); } } const toggleFullScreen = () => { that.instance.setOption('fullScreen', !that.instance.getOption('fullScreen')); const header = document.getElementById('subhead'); if (header) { const header1 = document.getElementById('header'); header1.classList.toggle('hidden'); header.classList.toggle('hidden'); that.instance.display.wrapper.style.top = `${header.getBoundingClientRect().height}px`; } }; const closeFullScreen = () => { that.instance.getOption('fullScreen'); that.instance.setOption('fullScreen', false); if (!that.instance.getOption('fullScreen')) { const header = document.getElementById('subhead'); if (header) { const header1 = document.getElementById('header'); header.classList.toggle('hidden'); header1.classList.toggle('hidden'); that.instance.display.wrapper.style.top = `${header.getBoundingClientRect().height}px`; } } }; const map = { 'Ctrl-Q': toggleFullScreen, [that.getAttribute('fs-combo')]: toggleFullScreen, Esc: closeFullScreen }; editor.addKeyMap(map); const makeMarker = () => { const marker = document.createElement('div'); marker.className = 'CodeMirror-markergutter-mark'; return marker; }; // Handle gutter clicks (place or remove a marker). editor.on('gutterClick', (ed, n, gutter) => { if (gutter !== 'CodeMirror-markergutter') { return; } const info = ed.lineInfo(n); const hasMarker = !!info.gutterMarkers && !!info.gutterMarkers['CodeMirror-markergutter']; ed.setGutterMarker(n, 'CodeMirror-markergutter', hasMarker ? null : makeMarker()); }); /* Some browsers do something weird with the fieldset which doesn't work well with CodeMirror. Fix it. */ if (that.parentNode.tagName.toLowerCase() === 'fieldset') { that.parentNode.style.minWidth = 0; } }); // Register Editor this.instance = window.CodeMirror.fromTextArea(this.element, this.options); this.instance.disable = disabled => this.setOption('readOnly', disabled ? 'nocursor' : false); Joomla.editors.instances[this.element.id] = this.instance; // Watch when the element in viewport, and refresh the editor this.intersectionObserver.observe(this); } disconnectedCallback() { // Remove from the Joomla API delete Joomla.editors.instances[this.element.id]; // Remove from observer this.intersectionObserver.unobserve(this); } refresh(element) { this.instance.fromTextArea(element, this.options); } } customElements.define('joomla-editor-codemirror', CodemirrorEditor);