174 lines
5.5 KiB
JavaScript
174 lines
5.5 KiB
JavaScript
|
|
import {json} from '@codemirror/lang-json';
|
||
|
|
import {StreamLanguage} from '@codemirror/language';
|
||
|
|
import {lua} from '@codemirror/legacy-modes/mode/lua';
|
||
|
|
import {EditorView, placeholder} from '@codemirror/view';
|
||
|
|
import {nord} from 'cm6-theme-nord';
|
||
|
|
import {basicSetup} from 'codemirror';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Waits until the webpage loads and then it calls the
|
||
|
|
* anonymous function, which calls main.
|
||
|
|
*/
|
||
|
|
window.onload = () => {
|
||
|
|
main();
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Configures the code editors and populating published data buttons.
|
||
|
|
*/
|
||
|
|
function main() {
|
||
|
|
const /** !HTMLElement */ scriptInput =
|
||
|
|
document.getElementById('script-input');
|
||
|
|
|
||
|
|
const /** !HTMLElement */ publishedDataInput =
|
||
|
|
document.getElementById('published-data-input');
|
||
|
|
|
||
|
|
const /** !HTMLElement */ savedStateInput =
|
||
|
|
document.getElementById('saved-state-input');
|
||
|
|
|
||
|
|
// Adds a custom theme to the CodeMirrors. Makes it so that the CodeMirror
|
||
|
|
// takes up the space of its parent container, defines the behavior of the
|
||
|
|
// contents if they overflow past the parent container to add a scroll-bar and
|
||
|
|
// customizes the scroll-bar appearence.
|
||
|
|
const /** !Extension */ customTheme = EditorView.theme({
|
||
|
|
'&': {
|
||
|
|
height: '100%',
|
||
|
|
width: 'auto',
|
||
|
|
},
|
||
|
|
'.cm-scroller': {overflow: 'auto'},
|
||
|
|
});
|
||
|
|
|
||
|
|
configureCodeMirror(scriptInput, [
|
||
|
|
basicSetup,
|
||
|
|
nord,
|
||
|
|
StreamLanguage.define(lua),
|
||
|
|
placeholder(scriptInput.placeholder),
|
||
|
|
customTheme,
|
||
|
|
EditorView.lineWrapping,
|
||
|
|
]);
|
||
|
|
|
||
|
|
const /** !EditorView */ publishedDataEditorView =
|
||
|
|
configureCodeMirror(publishedDataInput, [
|
||
|
|
basicSetup,
|
||
|
|
nord,
|
||
|
|
json(),
|
||
|
|
placeholder(publishedDataInput.placeholder),
|
||
|
|
customTheme,
|
||
|
|
EditorView.lineWrapping,
|
||
|
|
]);
|
||
|
|
|
||
|
|
configureCodeMirror(savedStateInput, [
|
||
|
|
basicSetup,
|
||
|
|
nord,
|
||
|
|
json(),
|
||
|
|
placeholder(savedStateInput.placeholder),
|
||
|
|
customTheme,
|
||
|
|
EditorView.lineWrapping,
|
||
|
|
]);
|
||
|
|
|
||
|
|
setupPublishedDataButtonGroup(publishedDataEditorView);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Adds a CodeMirror with the given extensions in place of the textArea.
|
||
|
|
* Additionally links the input of the CodeMirror with the textArea.
|
||
|
|
* @param {!HTMLTextAreaElement} textArea The TextArea that is replaced with a
|
||
|
|
* CodeMirror.
|
||
|
|
* @param {!Array<!Extension>} extensions Extensions to add to the configuration
|
||
|
|
* of the CodeMirror. Possible extensions are here
|
||
|
|
* https://codemirror.net/docs/extensions/.
|
||
|
|
* @return {!EditorView} The newly configured EditorView.
|
||
|
|
*/
|
||
|
|
function configureCodeMirror(textArea, extensions) {
|
||
|
|
const /** !EditorView */ editor =
|
||
|
|
new EditorView({doc: textArea.value, extensions: extensions});
|
||
|
|
|
||
|
|
// Replaces textArea with code editor.
|
||
|
|
textArea.parentNode.insertBefore(editor.dom, textArea);
|
||
|
|
|
||
|
|
// The code editor and textArea have no reference of each other,
|
||
|
|
// so value must be supplied from the editor when submitting.
|
||
|
|
textArea.form.addEventListener('submit', () => {
|
||
|
|
textArea.value = editor.state.doc.toString();
|
||
|
|
});
|
||
|
|
|
||
|
|
return editor;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Populates the Published Data button group with buttons corresponding to the
|
||
|
|
* available published data types.
|
||
|
|
*
|
||
|
|
* @param {!EditorView} publishedDataEditorView EditorView for the published
|
||
|
|
* data.
|
||
|
|
*/
|
||
|
|
function setupPublishedDataButtonGroup(publishedDataEditorView) {
|
||
|
|
fetch('/get_published_data_file_names_and_content', {method: 'POST'})
|
||
|
|
.then((response) => {
|
||
|
|
// json() is necessary here to transform the response to an object
|
||
|
|
// usable by Javascript.
|
||
|
|
return response.json();
|
||
|
|
})
|
||
|
|
.then((responseObject) => {
|
||
|
|
const /** !Array<string> */ fileNames = responseObject['file_names'];
|
||
|
|
const /** !HTMLElement */ buttonGroup =
|
||
|
|
document.getElementById('published-data-button-group');
|
||
|
|
|
||
|
|
for (const [i, fileName] of fileNames.entries()) {
|
||
|
|
buttonGroup.appendChild(createPopulatingDataButton(
|
||
|
|
fileName, publishedDataEditorView, responseObject[fileName]));
|
||
|
|
|
||
|
|
if (i !== fileNames.length - 1) {
|
||
|
|
const /** !HTMLSpanElement */ separator =
|
||
|
|
document.createElement('span');
|
||
|
|
separator.classList.add('mdc-theme--on-surface');
|
||
|
|
separator.textContent = '·';
|
||
|
|
buttonGroup.appendChild(separator);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Creates a button that replaces the contents of the
|
||
|
|
* given EditorView on click with the data passed in.
|
||
|
|
*
|
||
|
|
* Each button follows a similar layout in HTML:
|
||
|
|
* <button type="button" class="mdc-button">
|
||
|
|
* <span class="mdc-button__ripple"></span>
|
||
|
|
* <span class="mdc-button__label">memory_publisher</span>
|
||
|
|
* </button>
|
||
|
|
*
|
||
|
|
* @param {string} label The label of the button.
|
||
|
|
* @param {!EditorView} editorView EditorView that represents the
|
||
|
|
* target of the data.
|
||
|
|
* @param {string} newContent The data used to replace contents of EditorView.
|
||
|
|
* @return {!HTMLButtonElement} The newly created button.
|
||
|
|
*/
|
||
|
|
function createPopulatingDataButton(label, editorView, newContent) {
|
||
|
|
const /** !HTMLButtonElement */ button = document.createElement('button');
|
||
|
|
button.type = 'button';
|
||
|
|
button.classList.add('mdc-button');
|
||
|
|
|
||
|
|
// Material design component that creates ripple effect onClick.
|
||
|
|
const /** !HTMLSpanElement */ rippleSpan = document.createElement('span');
|
||
|
|
rippleSpan.classList.add('mdc-button__ripple');
|
||
|
|
|
||
|
|
const /** !HTMLSpanElement */ labelSpan = document.createElement('span');
|
||
|
|
labelSpan.textContent = label;
|
||
|
|
button.appendChild(rippleSpan);
|
||
|
|
button.appendChild(labelSpan);
|
||
|
|
|
||
|
|
button.addEventListener('click', () => {
|
||
|
|
editorView.dispatch({
|
||
|
|
changes: {
|
||
|
|
from: 0,
|
||
|
|
to: editorView.state.doc.length,
|
||
|
|
insert: newContent,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
})
|
||
|
|
|
||
|
|
return button;
|
||
|
|
}
|