-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapi.js
147 lines (139 loc) · 4.67 KB
/
api.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Builds code editor replacing all <script type="code" /> elements with a monaco instance.
function buildEditor(element) {
// Get the form element (which should be parent of the script);
const form = element.parentElement;
// Get the initial source code.
const jsValue = element.innerText.trim();
// Replace script tag with a div that will be used as a container for monaco editor.
const container = document.createElement('div');
// Style it a little - those values were chosen by hand.
container.style.minHeight = '50px';
container.style.maxWidth = 'calc(100% - 50px)';
container.classList.add('code-editor');
form.replaceChild(container, element);
// Create JS monaco model - like a tab in the IDE.
const jsModel = monaco.editor.createModel(jsValue, 'javascript');
// Create IDE. Options here are only for styling and making editor look like a
// code snippet.
const editor = monaco.editor.create(container, {
model: jsModel,
automaticLayout: true,
fontSize: '13px',
wordWrap: 'on',
minimap: {
enabled: false,
},
lineNumbers: 'off',
glyphMargin: false,
folding: false,
// Undocumented see https://github.com/Microsoft/vscode/issues/30795#issuecomment-410998882
lineDecorationsWidth: 0,
lineNumbersMinChars: 0,
overviewRulerLanes: 0,
hideCursorInOverviewRuler: true,
renderLineHighlight: 'none',
scrollbar: {
vertical: 'hidden',
handleMouseWheel: false
},
overviewRulerBorder: false,
});
// Set tabSize - this can be done only after editor is created.
editor.getModel().updateOptions({tabSize: 2});
// Disable scrolling past the last line - we will expand editor if necessary.
editor.updateOptions({scrollBeyondLastLine: false});
// Auto-height algorithm see https://github.com/microsoft/monaco-editor/issues/794
editor.onDidChangeModelDecorations(() => {
updateEditorHeight(); // typing
requestAnimationFrame(updateEditorHeight); // folding
});
let prevHeight = 0;
const updateEditorHeight = () => {
const editorElement = editor.getDomNode();
if (!editorElement) {
return;
}
const lineHeight = editor.getOption(monaco.editor.EditorOption.lineHeight);
const lineCount = editor.getModel()?.getLineCount() || 1;
const height = editor.getTopForLineNumber(lineCount + 1) + lineHeight;
if (prevHeight !== height) {
prevHeight = height;
if (height < 1000) editorElement.style.height = `${height}px`;
editor.layout();
}
};
updateEditorHeight();
setTimeout(updateEditorHeight, 1);
buildOutput(form, jsModel);
}
function purge(element) {
while (element.firstChild) {
element.removeChild(element.firstChild);
}
}
function buildOutput(form, jsModel) {
// Get textarea element - it was only a placeholder where to put output.
const textarea = form.querySelector('.result');
// Replace it with a div - here we will render json viewer.
const jsonView = document.createElement('div');
form.replaceChild(jsonView, textarea);
// Create helper method to show json in this view.
function render(data) {
const formatter = new JSONFormatter(data, 2, {hoverPreviewEnabled: true});
purge(jsonView);
jsonView.appendChild(formatter.render());
}
// Attach onsubmit handler that will execute the code
form.onsubmit = function () {
async function run() {
try {
// Each snippet should declare example method in global scope.
delete window.example;
// Eval snippet in window scope - we expect that window.example method will be there.
window.eval(jsModel.getValue());
// if there is a example method
if (window.example) {
// Run the code and pass the output handler.
const result = await window.example({
output(data) {
render(data);
},
});
// Clear the output and display result.
purge(jsonView);
render(result);
}
} catch (err) {
purge(jsonView);
jsonView.innerHTML = err.message;
}
}
void run();
// Prevent submit.
return false;
};
}
function init() {
// Import definitions from api_deps.js
monaco.languages.typescript.javascriptDefaults.addExtraLib(definition, 'plugin.d.ts');
// Declare global grist namespace.
monaco.languages.typescript.javascriptDefaults.addExtraLib(
`
import * as Grist from "grist"
declare global {
interface Window {
var grist: typeof Grist;
}
}
export {}
`,
'main.d.ts'
);
}
window.onload = () => {
init();
const codes = Array.from(document.querySelectorAll("script[type='code']"));
for (const code of codes) {
buildEditor(code);
}
};