unplugged-system/packages/services/Car/tools/telemetry/lua-interpreter/static/js/index.js

174 lines
5.5 KiB
JavaScript
Raw Normal View History

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;
}