Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@ PLG_CODEMIRROR_FIELD_VIM_KEYBINDING_LABEL="Vim Keybinding"
PLG_CODEMIRROR_FIELDSET_APPEARANCE_OPTIONS_LABEL="Appearance Options"
PLG_CODEMIRROR_FIELDSET_TOOLBAR_OPTIONS_LABEL="Toolbar Options"
PLG_CODEMIRROR_TOGGLE_FULL_SCREEN="Press %1$s %2$s to toggle Full Screen editing."
PLG_CODEMIRROR_TOGGLE_FULL_SCREEN_READONLY="Press %1$s %2$s to toggle Full Screen readonly."
PLG_CODEMIRROR_XML_DESCRIPTION="This plugin loads the CodeMirror editor."
PLG_EDITORS_CODEMIRROR="Editor - CodeMirror"
293 changes: 148 additions & 145 deletions build/webcomponents/js/editor-codemirror/editor-codemirror.js
Original file line number Diff line number Diff line change
@@ -1,147 +1,150 @@

customElements.define('joomla-editor-codemirror', class extends HTMLElement {
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;
}
}

constructor() {
super();

this.instance = '';
this.cm = '';
this.file = document.currentScript;
this.element = this.querySelector('textarea');
this.host = window.location.origin;

// Append the editor script
if (!document.head.querySelector('#cm-editor')) {
const cmPath = this.getAttribute('editor');
const script1 = document.createElement('script');

script1.src = `${this.host}/${cmPath}`;
script1.id = 'cm-editor';
script1.setAttribute('async', false);
document.head.insertBefore(script1, this.file);
}

this.toggleFullScreen = this.toggleFullScreen.bind(this);
this.closeFullScreen = this.closeFullScreen.bind(this);
}

connectedCallback() {
const buttons = [].slice.call(this.querySelectorAll('.editor-xtd-buttons .xtd-button'));
this.checkElement('CodeMirror')
.then(() => {
// Append the addons script
if (!document.head.querySelector('#cm-addons')) {
const addonsPath = this.getAttribute('addons');
const script2 = document.createElement('script');

script2.src = `${this.host}/${addonsPath}`;
script2.id = 'cm-addons';
script2.setAttribute('async', false);
document.head.insertBefore(script2, this.file)
}

this.checkElement('CodeMirror', 'findModeByName')
.then(() => {
window.CodeMirror.keyMap.default["Ctrl-Q"] = this.toggleFullScreen;
window.CodeMirror.keyMap.default[this.getAttribute('fs-combo')] = this.toggleFullScreen;
window.CodeMirror.keyMap.default["Esc"] = this.closeFullScreen;

// 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 || '');

if (mode) {
window.CodeMirror.autoLoadMode(editor, mode.mode);
editor.setOption('mode', mode.mime);
} else {
window.CodeMirror.autoLoadMode(editor, editor.options.mode);
}

// Handle gutter clicks (place or remove a marker).
editor.on("gutterClick", function (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 : this.makeMarker());
});

// Some browsers do something weird with the fieldset which doesn't work well with CodeMirror. Fix it.
if (this.parentNode.tagName.toLowerCase() === 'fieldset') {
this.parentNode.style.minWidth = 0;
}
});

/** Register Editor */
this.instance = window.CodeMirror.fromTextArea(this.element, this.options);
Joomla.editors.instances[this.element.id] = this.instance;
});
});
}

disconnectedCallback() {
// Remove from the Joomla API
delete Joomla.editors.instances[this.element.id];
}

refresh(element) {
this.instance = window.CodeMirror.fromTextArea(element, this.options);
}

rafAsync() {
return new Promise(resolve => {
requestAnimationFrame(resolve);
});
}

async checkElement(string1, string2) {
if (string2) {
while (typeof window[string1][string2] !== 'function') {
await this.rafAsync()
}
} else {
while (typeof window[string1] !== 'function') {
await this.rafAsync()
}
}

return true;
}

toggleFullScreen() {
this.instance.setOption("fullScreen", !this.instance.getOption("fullScreen"));
}

closeFullScreen() {
this.instance.getOption("fullScreen") && this.instance.setOption("fullScreen", false);
}

makeMarker() {
const marker = document.createElement("div");
marker.className = "CodeMirror-markergutter-mark";
return marker;
}
constructor() {
super();

this.instance = '';
this.cm = '';
this.host = window.location.origin;
this.element = this.querySelector('textarea');
this.refresh = this.refresh.bind(this);
this.toggleFullScreen = this.toggleFullScreen.bind(this);
this.closeFullScreen = this.closeFullScreen.bind(this);

// Append the editor script
if (!document.head.querySelector('#cm-editor')) {
const cmPath = this.getAttribute('editor');
const script1 = document.createElement('script');

script1.src = `${this.host}/${cmPath}`;
script1.id = 'cm-editor';
script1.setAttribute('async', false);
document.head.insertBefore(script1, this.file);
}
}

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;
default:
// Do nothing
}
}

connectedCallback() {
const that = this;
this.checkElement('CodeMirror')
.then(() => {
// Append the addons script
if (!document.head.querySelector('#cm-addons')) {
const addonsPath = this.getAttribute('addons');
const script2 = document.createElement('script');

script2.src = `${this.host}/${addonsPath}`;
script2.id = 'cm-addons';
script2.setAttribute('async', false);
document.head.insertBefore(script2, this.file);
}

this.checkElement('CodeMirror', 'findModeByName')
.then(() => {
// 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(that.options.mode || '');

if (mode) {
window.CodeMirror.autoLoadMode(editor, mode.mode);
editor.setOption('mode', mode.mime);
} else {
window.CodeMirror.autoLoadMode(editor, that.options.mode);
}

const map = {
'Ctrl-Q': that.toggleFullScreen,
[that.getAttribute('fs-combo')]: that.toggleFullScreen,
Esc: that.closeFullScreen,
};

editor.addKeyMap(map);

// 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 : this.makeMarker());
});

/* Some browsers do something weird with the fieldset which doesn't
work well with CodeMirror. Fix it. */
if (this.parentNode.tagName.toLowerCase() === 'fieldset') {
this.parentNode.style.minWidth = 0;
}
});

// Register Editor
this.instance = window.CodeMirror.fromTextArea(this.element, this.options);
Joomla.editors.instances[this.element.id] = this.instance;
});
});
}

disconnectedCallback() {
// Remove from the Joomla API
delete Joomla.editors.instances[this.element.id];
}

refresh(element) {
this.instance = window.CodeMirror.fromTextArea(element, this.options);
}

rafAsync() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected 'this' to be used by class method 'rafAsync' class-methods-use-this

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected 'this' to be used by class method 'rafAsync' class-methods-use-this

return new Promise(resolve => requestAnimationFrame(resolve));
}

async checkElement(string1, string2) {
if (string2) {
while (typeof window[string1][string2] !== 'function') {
await this.rafAsync();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected await inside a loop no-await-in-loop

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected await inside a loop no-await-in-loop

}
} else {
while (typeof window[string1] !== 'function') {
await this.rafAsync();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected await inside a loop no-await-in-loop

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected await inside a loop no-await-in-loop

}
}

return true;
}

toggleFullScreen() {
this.instance.setOption('fullScreen', !this.instance.getOption('fullScreen'));
}

closeFullScreen() {
this.instance.getOption('fullScreen');
this.instance.setOption('fullScreen', false);
}

static makeMarker() {
const marker = document.createElement('div');
marker.className = 'CodeMirror-markergutter-mark';
return marker;
}
});
Loading