primo commit

This commit is contained in:
2024-12-17 17:34:10 +01:00
commit e650f8df99
16435 changed files with 2451012 additions and 0 deletions

View File

@ -0,0 +1,153 @@
import { highlightSpecialChars, drawSelection, lineNumbers, EditorView, highlightActiveLineGutter, highlightActiveLine, keymap } from '@codemirror/view';
export { EditorView, keymap } from '@codemirror/view';
import { Compartment, EditorState } from '@codemirror/state';
export { EditorState } from '@codemirror/state';
import { syntaxHighlighting, defaultHighlightStyle, foldGutter } from '@codemirror/language';
import { history, defaultKeymap, historyKeymap, emacsStyleKeymap } from '@codemirror/commands';
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search';
import { closeBrackets } from '@codemirror/autocomplete';
import { oneDark } from '@codemirror/theme-one-dark';
/**
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
const minimalSetup = () => [highlightSpecialChars(), history(), drawSelection(), syntaxHighlighting(defaultHighlightStyle, {
fallback: true
})];
/**
* Configure and return list of extensions for given options
*
* @param {Object} options
* @returns {Promise<[]>}
*/
const optionsToExtensions = async options => {
const extensions = [];
const q = [];
// Load the language for syntax mode
if (options.mode) {
const {
mode
} = options;
const modeOptions = options[mode] || {};
// eslint-disable-next-line consistent-return
q.push(import(`@codemirror/lang-${options.mode}`).then(modeMod => {
// For html and php we need to configure selfClosingTags, to make code folding work correctly with <jdoc:include />
if (mode === 'php') {
return import('@codemirror/lang-html').then(({
html
}) => {
const htmlOptions = options.html || {
selfClosingTags: true
};
extensions.push(modeMod.php({
baseLanguage: html(htmlOptions).language
}));
});
}
if (mode === 'html') {
modeOptions.selfClosingTags = true;
}
extensions.push(modeMod[options.mode](modeOptions));
}).catch(error => {
// eslint-disable-next-line no-console
console.error(`Cannot create an extension for "${options.mode}" syntax mode.`, error);
}));
}
if (options.lineNumbers) {
extensions.push(lineNumbers());
}
if (options.lineWrapping) {
extensions.push(EditorView.lineWrapping);
}
if (options.activeLine) {
extensions.push(highlightActiveLineGutter(), highlightActiveLine());
}
if (options.highlightSelection) {
extensions.push(highlightSelectionMatches());
}
if (options.autoCloseBrackets) {
extensions.push(closeBrackets());
}
if (options.foldGutter) {
extensions.push(foldGutter());
}
// Keymaps
switch (options.keyMap) {
case 'emacs':
extensions.push(keymap.of([...emacsStyleKeymap, ...historyKeymap]));
break;
default:
extensions.push(keymap.of([...defaultKeymap, ...searchKeymap, ...historyKeymap]));
break;
}
// Configurable read only
const readOnly = new Compartment();
// Set a custom name so later on we can retrieve this Compartment from view.state.config.compartments
readOnly.$j_name = 'readOnly';
extensions.push(readOnly.of(EditorState.readOnly.of(!!options.readOnly)));
// Check for a skin that suits best for the active color scheme
// TODO: Use compartments to update on change of dark mode like: https://discuss.codemirror.net/t/dynamic-light-mode-dark-mode-how/4709
if ('colorSchemeOs' in document.documentElement.dataset && window.matchMedia('(prefers-color-scheme: dark)').matches || document.documentElement.dataset.colorScheme === 'dark') {
extensions.push(oneDark);
}
// Check for custom extensions,
// in format [['module1 name or URL', ['init method2']], ['module2 name or URL', ['init method2']], () => <return extension>]
if (options.customExtensions && options.customExtensions.length) {
options.customExtensions.forEach(extInfo => {
// Check whether we have a callable
if (extInfo instanceof Function) {
extensions.push(extInfo());
return;
}
// Import the module
const [module, methods] = extInfo;
q.push(import(module).then(modObject => {
// Call each method
methods.forEach(method => extensions.push(modObject[method]()));
}));
});
}
return Promise.all(q).then(() => extensions);
};
/**
* Create an editor instance for given textarea
*
* @param {HTMLTextAreaElement} textarea
* @param {Object} options
* @returns {Promise<EditorView>}
*/
async function createFromTextarea(textarea, options) {
const extensions = [minimalSetup(), await optionsToExtensions(options)];
const view = new EditorView({
doc: textarea.value,
root: options.root || null,
extensions
});
textarea.parentNode.insertBefore(view.dom, textarea);
textarea.style.display = 'none';
if (textarea.form) {
textarea.form.addEventListener('submit', () => {
textarea.value = view.state.doc.toString();
});
}
// Set up sizing
if (options.width) {
view.dom.style.width = options.width;
}
if (options.height) {
view.dom.style.height = options.height;
}
return view;
}
export { createFromTextarea, minimalSetup, optionsToExtensions };

View File

@ -0,0 +1,4 @@
import{highlightSpecialChars as f,drawSelection as g,lineNumbers as p,EditorView as a,highlightActiveLineGutter as y,highlightActiveLine as w,keymap as c}from"@codemirror/view";export{EditorView,keymap}from"@codemirror/view";import{Compartment as E,EditorState as S}from"@codemirror/state";export{EditorState}from"@codemirror/state";import{syntaxHighlighting as k,defaultHighlightStyle as b,foldGutter as v}from"@codemirror/language";import{history as O,defaultKeymap as C,historyKeymap as l,emacsStyleKeymap as x}from"@codemirror/commands";import{highlightSelectionMatches as L,searchKeymap as K}from"@codemirror/search";import{closeBrackets as T}from"@codemirror/autocomplete";import{oneDark as B}from"@codemirror/theme-one-dark";/**
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/const u=()=>[f(),O(),g(),k(b,{fallback:!0})],d=async e=>{const t=[],o=[];if(e.mode){const{mode:i}=e,h=e[i]||{};o.push(import(`@codemirror/lang-${e.mode}`).then(n=>{if(i==="php")return import("@codemirror/lang-html").then(({html:m})=>{const s=e.html||{selfClosingTags:!0};t.push(n.php({baseLanguage:m(s).language}))});i==="html"&&(h.selfClosingTags=!0),t.push(n[e.mode](h))}).catch(n=>{console.error(`Cannot create an extension for "${e.mode}" syntax mode.`,n)}))}switch(e.lineNumbers&&t.push(p()),e.lineWrapping&&t.push(a.lineWrapping),e.activeLine&&t.push(y(),w()),e.highlightSelection&&t.push(L()),e.autoCloseBrackets&&t.push(T()),e.foldGutter&&t.push(v()),e.keyMap){case"emacs":t.push(c.of([...x,...l]));break;default:t.push(c.of([...C,...K,...l]));break}const r=new E;return r.$j_name="readOnly",t.push(r.of(S.readOnly.of(!!e.readOnly))),("colorSchemeOs"in document.documentElement.dataset&&window.matchMedia("(prefers-color-scheme: dark)").matches||document.documentElement.dataset.colorScheme==="dark")&&t.push(B),e.customExtensions&&e.customExtensions.length&&e.customExtensions.forEach(i=>{if(i instanceof Function){t.push(i());return}const[h,n]=i;o.push(import(h).then(m=>{n.forEach(s=>t.push(m[s]()))}))}),Promise.all(o).then(()=>t)};async function G(e,t){const o=[u(),await d(t)],r=new a({doc:e.value,root:t.root||null,extensions:o});return e.parentNode.insertBefore(r.dom,e),e.style.display="none",e.form&&e.form.addEventListener("submit",()=>{e.value=r.state.doc.toString()}),t.width&&(r.dom.style.width=t.width),t.height&&(r.dom.style.height=t.height),r}export{G as createFromTextarea,u as minimalSetup,d as optionsToExtensions};

Binary file not shown.

View File

@ -0,0 +1,134 @@
import { JoomlaEditor, JoomlaEditorDecorator } from 'editor-api';
import { keymap, createFromTextarea, EditorState } from 'codemirror';
/**
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
/**
* Codemirror Decorator for JoomlaEditor
*/
// eslint-disable-next-line max-classes-per-file
class CodemirrorDecorator extends JoomlaEditorDecorator {
/**
* @returns {string}
*/
getValue() {
return this.instance.state.doc.toString();
}
/**
* @param {String} value
* @returns {CodemirrorDecorator}
*/
setValue(value) {
const editor = this.instance;
editor.dispatch({
changes: {
from: 0,
to: editor.state.doc.length,
insert: value
}
});
return this;
}
/**
* @returns {string}
*/
getSelection() {
const {
state
} = this.instance;
return state.sliceDoc(state.selection.main.from, state.selection.main.to);
}
replaceSelection(value) {
const v = this.instance.state.replaceSelection(value);
this.instance.dispatch(v);
return this;
}
disable(enable) {
const editor = this.instance;
editor.state.config.compartments.forEach((facet, compartment) => {
if (compartment.$j_name === 'readOnly') {
editor.dispatch({
effects: compartment.reconfigure(EditorState.readOnly.of(!enable))
});
}
});
return this;
}
}
class CodemirrorEditor extends HTMLElement {
constructor() {
super();
this.toggleFullScreen = () => {
if (!this.classList.contains('fullscreen')) {
this.classList.add('fullscreen');
document.documentElement.scrollTop = 0;
document.documentElement.style.overflow = 'hidden';
} else {
this.closeFullScreen();
}
};
this.closeFullScreen = () => {
this.classList.remove('fullscreen');
document.documentElement.style.overflow = '';
};
this.interactionCallback = () => {
JoomlaEditor.setActive(this.element.id);
};
}
get options() {
return JSON.parse(this.getAttribute('options'));
}
get fsCombo() {
return this.getAttribute('fs-combo');
}
async connectedCallback() {
const {
options
} = this;
// Configure full screen feature
if (this.fsCombo) {
options.customExtensions = options.customExtensions || [];
options.customExtensions.push(() => keymap.of([{
key: this.fsCombo,
run: this.toggleFullScreen
}, {
key: 'Escape',
run: this.closeFullScreen
}]));
// Relocate BS modals, to resolve z-index issue in full screen
this.bsModals = this.querySelectorAll('.joomla-modal.modal');
this.bsModals.forEach(modal => document.body.appendChild(modal));
}
// Create and register the Editor
this.element = this.querySelector('textarea');
this.instance = await createFromTextarea(this.element, options);
this.jEditor = new CodemirrorDecorator(this.instance, 'codemirror', this.element.id);
JoomlaEditor.register(this.jEditor);
// Find out when editor is interacted
this.addEventListener('click', this.interactionCallback);
}
disconnectedCallback() {
if (this.instance) {
this.element.style.display = '';
this.instance.destroy();
}
// Remove from the Joomla API
JoomlaEditor.unregister(this.element.id);
this.removeEventListener('click', this.interactionCallback);
// Restore modals
if (this.bsModals && this.bsModals.length) {
this.bsModals.forEach(modal => this.appendChild(modal));
}
}
}
customElements.define('joomla-editor-codemirror', CodemirrorEditor);

View File

@ -0,0 +1,4 @@
import{JoomlaEditor as s,JoomlaEditorDecorator as n}from"editor-api";import{keymap as c,createFromTextarea as r,EditorState as l}from"codemirror";/**
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/class a extends n{getValue(){return this.instance.state.doc.toString()}setValue(e){const t=this.instance;return t.dispatch({changes:{from:0,to:t.state.doc.length,insert:e}}),this}getSelection(){const{state:e}=this.instance;return e.sliceDoc(e.selection.main.from,e.selection.main.to)}replaceSelection(e){const t=this.instance.state.replaceSelection(e);return this.instance.dispatch(t),this}disable(e){const t=this.instance;return t.state.config.compartments.forEach((d,i)=>{i.$j_name==="readOnly"&&t.dispatch({effects:i.reconfigure(l.readOnly.of(!e))})}),this}}class h extends HTMLElement{constructor(){super(),this.toggleFullScreen=()=>{this.classList.contains("fullscreen")?this.closeFullScreen():(this.classList.add("fullscreen"),document.documentElement.scrollTop=0,document.documentElement.style.overflow="hidden")},this.closeFullScreen=()=>{this.classList.remove("fullscreen"),document.documentElement.style.overflow=""},this.interactionCallback=()=>{s.setActive(this.element.id)}}get options(){return JSON.parse(this.getAttribute("options"))}get fsCombo(){return this.getAttribute("fs-combo")}async connectedCallback(){const{options:e}=this;this.fsCombo&&(e.customExtensions=e.customExtensions||[],e.customExtensions.push(()=>c.of([{key:this.fsCombo,run:this.toggleFullScreen},{key:"Escape",run:this.closeFullScreen}])),this.bsModals=this.querySelectorAll(".joomla-modal.modal"),this.bsModals.forEach(t=>document.body.appendChild(t))),this.element=this.querySelector("textarea"),this.instance=await r(this.element,e),this.jEditor=new a(this.instance,"codemirror",this.element.id),s.register(this.jEditor),this.addEventListener("click",this.interactionCallback)}disconnectedCallback(){this.instance&&(this.element.style.display="",this.instance.destroy()),s.unregister(this.element.id),this.removeEventListener("click",this.interactionCallback),this.bsModals&&this.bsModals.length&&this.bsModals.forEach(e=>this.appendChild(e))}}customElements.define("joomla-editor-codemirror",h);