1
+ // Supported Evaluation Types for Context
2
+ globalThis . EvalTypes = Object . freeze ( {
3
+ Interactive : 'interactive' ,
4
+ Setup : 'setup' ,
5
+ Output : 'output' ,
6
+ } ) ;
7
+
8
+ // Function that dispatches the creation request
9
+ globalThis . qwebrCreateHTMLElement = function (
10
+ cellData
11
+ ) {
12
+
13
+ // Extract key components
14
+ const evalType = cellData . options . context ;
15
+ const qwebrCounter = cellData . id ;
16
+
17
+ // We make an assumption that insertion points are defined by the Lua filter as:
18
+ // qwebr-insertion-location-{qwebrCounter}
19
+ const elementLocator = document . getElementById ( `qwebr-insertion-location-${ qwebrCounter } ` ) ;
20
+
21
+ // Figure out the routine to use to insert the element.
22
+ let qwebrElement ;
23
+ switch ( evalType ) {
24
+ case EvalTypes . Interactive :
25
+ qwebrElement = qwebrCreateInteractiveElement ( qwebrCounter , cellData . options ) ;
26
+ break ;
27
+ case EvalTypes . Output :
28
+ qwebrElement = qwebrCreateNonInteractiveOutputElement ( qwebrCounter , cellData . options ) ;
29
+ break ;
30
+ case EvalTypes . Setup :
31
+ qwebrElement = qwebrCreateNonInteractiveSetupElement ( qwebrCounter , cellData . options ) ;
32
+ break ;
33
+ default :
34
+ qwebrElement = document . createElement ( 'div' ) ;
35
+ qwebrElement . textContent = 'Error creating `quarto-webr` element' ;
36
+ }
37
+
38
+ // Insert the dynamically generated object at the document location.
39
+ elementLocator . appendChild ( qwebrElement ) ;
40
+ } ;
41
+
42
+ // Function that setups the interactive element creation
43
+ globalThis . qwebrCreateInteractiveElement = function ( qwebrCounter , qwebrOptions ) {
44
+
45
+ // Create main div element
46
+ var mainDiv = document . createElement ( 'div' ) ;
47
+ mainDiv . id = 'qwebr-interactive-area-' + qwebrCounter ;
48
+ mainDiv . className = `qwebr-interactive-area` ;
49
+ if ( qwebrOptions . classes ) {
50
+ mainDiv . className += " " + qwebrOptions . classes
51
+ }
52
+
53
+ // Add a unique cell identifier that users can customize
54
+ if ( qwebrOptions . label ) {
55
+ mainDiv . setAttribute ( 'data-id' , qwebrOptions . label ) ;
56
+ }
57
+
58
+ // Create toolbar div
59
+ var toolbarDiv = document . createElement ( 'div' ) ;
60
+ toolbarDiv . className = 'qwebr-editor-toolbar' ;
61
+ toolbarDiv . id = 'qwebr-editor-toolbar-' + qwebrCounter ;
62
+
63
+ // Create a div to hold the left buttons
64
+ var leftButtonsDiv = document . createElement ( 'div' ) ;
65
+ leftButtonsDiv . className = 'qwebr-editor-toolbar-left-buttons' ;
66
+
67
+ // Create a div to hold the right buttons
68
+ var rightButtonsDiv = document . createElement ( 'div' ) ;
69
+ rightButtonsDiv . className = 'qwebr-editor-toolbar-right-buttons' ;
70
+
71
+ // Create Run Code button
72
+ var runCodeButton = document . createElement ( 'button' ) ;
73
+ runCodeButton . className = 'btn btn-default qwebr-button qwebr-button-run' ;
74
+ runCodeButton . disabled = true ;
75
+ runCodeButton . type = 'button' ;
76
+ runCodeButton . id = 'qwebr-button-run-' + qwebrCounter ;
77
+ runCodeButton . textContent = '🟡 Loading webR...' ;
78
+ runCodeButton . title = `Run code (Shift + Enter)` ;
79
+
80
+ // Append buttons to the leftButtonsDiv
81
+ leftButtonsDiv . appendChild ( runCodeButton ) ;
82
+
83
+ // Create Reset button
84
+ var resetButton = document . createElement ( 'button' ) ;
85
+ resetButton . className = 'btn btn-light btn-xs qwebr-button qwebr-button-reset' ;
86
+ resetButton . type = 'button' ;
87
+ resetButton . id = 'qwebr-button-reset-' + qwebrCounter ;
88
+ resetButton . title = 'Start over' ;
89
+ resetButton . innerHTML = '<i class="fa-solid fa-arrows-rotate"></i>' ;
90
+
91
+ // Create Copy button
92
+ var copyButton = document . createElement ( 'button' ) ;
93
+ copyButton . className = 'btn btn-light btn-xs qwebr-button qwebr-button-copy' ;
94
+ copyButton . type = 'button' ;
95
+ copyButton . id = 'qwebr-button-copy-' + qwebrCounter ;
96
+ copyButton . title = 'Copy code' ;
97
+ copyButton . innerHTML = '<i class="fa-regular fa-copy"></i>' ;
98
+
99
+ // Append buttons to the rightButtonsDiv
100
+ rightButtonsDiv . appendChild ( resetButton ) ;
101
+ rightButtonsDiv . appendChild ( copyButton ) ;
102
+
103
+ // Create console area div
104
+ var consoleAreaDiv = document . createElement ( 'div' ) ;
105
+ consoleAreaDiv . id = 'qwebr-console-area-' + qwebrCounter ;
106
+ consoleAreaDiv . className = 'qwebr-console-area' ;
107
+
108
+ // Create editor div
109
+ var editorDiv = document . createElement ( 'div' ) ;
110
+ editorDiv . id = 'qwebr-editor-' + qwebrCounter ;
111
+ editorDiv . className = 'qwebr-editor' ;
112
+
113
+ // Create output code area div
114
+ var outputCodeAreaDiv = document . createElement ( 'div' ) ;
115
+ outputCodeAreaDiv . id = 'qwebr-output-code-area-' + qwebrCounter ;
116
+ outputCodeAreaDiv . className = 'qwebr-output-code-area' ;
117
+ outputCodeAreaDiv . setAttribute ( 'aria-live' , 'assertive' ) ;
118
+
119
+ // Create pre element inside output code area
120
+ var preElement = document . createElement ( 'pre' ) ;
121
+ preElement . style . visibility = 'hidden' ;
122
+ outputCodeAreaDiv . appendChild ( preElement ) ;
123
+
124
+ // Create output graph area div
125
+ var outputGraphAreaDiv = document . createElement ( 'div' ) ;
126
+ outputGraphAreaDiv . id = 'qwebr-output-graph-area-' + qwebrCounter ;
127
+ outputGraphAreaDiv . className = 'qwebr-output-graph-area' ;
128
+
129
+ // Append buttons to the toolbar
130
+ toolbarDiv . appendChild ( leftButtonsDiv ) ;
131
+ toolbarDiv . appendChild ( rightButtonsDiv ) ;
132
+
133
+ // Append all elements to the main div
134
+ mainDiv . appendChild ( toolbarDiv ) ;
135
+ consoleAreaDiv . appendChild ( editorDiv ) ;
136
+ consoleAreaDiv . appendChild ( outputCodeAreaDiv ) ;
137
+ mainDiv . appendChild ( consoleAreaDiv ) ;
138
+ mainDiv . appendChild ( outputGraphAreaDiv ) ;
139
+
140
+ return mainDiv ;
141
+ }
142
+
143
+ // Function that adds output structure for non-interactive output
144
+ globalThis . qwebrCreateNonInteractiveOutputElement = function ( qwebrCounter , qwebrOptions ) {
145
+ // Create main div element
146
+ var mainDiv = document . createElement ( 'div' ) ;
147
+ mainDiv . id = 'qwebr-noninteractive-area-' + qwebrCounter ;
148
+ mainDiv . className = `qwebr-noninteractive-area` ;
149
+ if ( qwebrOptions . classes ) {
150
+ mainDiv . className += " " + qwebrOptions . classes
151
+ }
152
+
153
+ // Add a unique cell identifier that users can customize
154
+ if ( qwebrOptions . label ) {
155
+ mainDiv . setAttribute ( 'data-id' , qwebrOptions . label ) ;
156
+ }
157
+
158
+ // Create a status container div
159
+ var statusContainer = createLoadingContainer ( qwebrCounter ) ;
160
+
161
+ // Create output code area div
162
+ var outputCodeAreaDiv = document . createElement ( 'div' ) ;
163
+ outputCodeAreaDiv . id = 'qwebr-output-code-area-' + qwebrCounter ;
164
+ outputCodeAreaDiv . className = 'qwebr-output-code-area' ;
165
+ outputCodeAreaDiv . setAttribute ( 'aria-live' , 'assertive' ) ;
166
+
167
+ // Create pre element inside output code area
168
+ var preElement = document . createElement ( 'pre' ) ;
169
+ preElement . style . visibility = 'hidden' ;
170
+ outputCodeAreaDiv . appendChild ( preElement ) ;
171
+
172
+ // Create output graph area div
173
+ var outputGraphAreaDiv = document . createElement ( 'div' ) ;
174
+ outputGraphAreaDiv . id = 'qwebr-output-graph-area-' + qwebrCounter ;
175
+ outputGraphAreaDiv . className = 'qwebr-output-graph-area' ;
176
+
177
+ // Append all elements to the main div
178
+ mainDiv . appendChild ( statusContainer ) ;
179
+ mainDiv . appendChild ( outputCodeAreaDiv ) ;
180
+ mainDiv . appendChild ( outputGraphAreaDiv ) ;
181
+
182
+ return mainDiv ;
183
+ } ;
184
+
185
+ // Function that adds a stub in the page to indicate a setup cell was used.
186
+ globalThis . qwebrCreateNonInteractiveSetupElement = function ( qwebrCounter , qwebrOptions ) {
187
+ // Create main div element
188
+ var mainDiv = document . createElement ( 'div' ) ;
189
+ mainDiv . id = `qwebr-noninteractive-setup-area-${ qwebrCounter } ` ;
190
+ mainDiv . className = `qwebr-noninteractive-setup-area` ;
191
+ if ( qwebrOptions . classes ) {
192
+ mainDiv . className += " " + qwebrOptions . classes
193
+ }
194
+
195
+
196
+ // Add a unique cell identifier that users can customize
197
+ if ( qwebrOptions . label ) {
198
+ mainDiv . setAttribute ( 'data-id' , qwebrOptions . label ) ;
199
+ }
200
+
201
+ // Create a status container div
202
+ var statusContainer = createLoadingContainer ( qwebrCounter ) ;
203
+
204
+ // Append status onto the main div
205
+ mainDiv . appendChild ( statusContainer ) ;
206
+
207
+ return mainDiv ;
208
+ }
209
+
210
+
211
+ // Function to create loading container with specified ID
212
+ globalThis . createLoadingContainer = function ( qwebrCounter ) {
213
+
214
+ // Create a status container
215
+ const container = document . createElement ( 'div' ) ;
216
+ container . id = `qwebr-non-interactive-loading-container-${ qwebrCounter } ` ;
217
+ container . className = 'qwebr-non-interactive-loading-container qwebr-cell-needs-evaluation' ;
218
+
219
+ // Create an R project logo to indicate its a code space
220
+ const rProjectIcon = document . createElement ( 'i' ) ;
221
+ rProjectIcon . className = 'fa-brands fa-r-project fa-3x qwebr-r-project-logo' ;
222
+
223
+ // Setup a loading icon from font awesome
224
+ const spinnerIcon = document . createElement ( 'i' ) ;
225
+ spinnerIcon . className = 'fa-solid fa-spinner fa-spin fa-1x qwebr-icon-status-spinner' ;
226
+
227
+ // Add a section for status text
228
+ const statusText = document . createElement ( 'p' ) ;
229
+ statusText . id = `qwebr-status-text-${ qwebrCounter } ` ;
230
+ statusText . className = `qwebr-status-text qwebr-cell-needs-evaluation` ;
231
+ statusText . innerText = 'Loading webR...' ;
232
+
233
+ // Incorporate an inner container
234
+ const innerContainer = document . createElement ( 'div' ) ;
235
+
236
+ // Append elements to the inner container
237
+ innerContainer . appendChild ( spinnerIcon ) ;
238
+ innerContainer . appendChild ( statusText ) ;
239
+
240
+ // Append elements to the main container
241
+ container . appendChild ( rProjectIcon ) ;
242
+ container . appendChild ( innerContainer ) ;
243
+
244
+ return container ;
245
+ }
0 commit comments