@@ -39,6 +39,9 @@ context Patient`;
3939
4040let data = syntheticPatient ;
4141
42+ // Libraries array to store additional CQL libraries
43+ let libraries = [ ] ;
44+
4245let results = '' ;
4346
4447// Helper functions:
@@ -81,6 +84,16 @@ function bindButtonActions() {
8184 . addEventListener ( 'click' , function ( e ) {
8285 showDataTab ( ) ;
8386 } ) ;
87+ document . getElementById ( 'librariesTabButton' )
88+ . addEventListener ( 'click' , function ( e ) {
89+ showLibrariesTab ( ) ;
90+ } ) ;
91+ document . getElementById ( 'addLibrary' )
92+ . addEventListener ( 'click' , function ( e ) {
93+ addLibrary ( ) ;
94+ } ) ;
95+ document . getElementById ( 'uploadLibrary' )
96+ . addEventListener ( 'change' , handleFileUpload ) ;
8497}
8598
8699/**
@@ -98,7 +111,15 @@ function runCQL() {
98111 } ;
99112 xhr . open ( 'POST' , '/eval_cql' , true ) ;
100113 xhr . setRequestHeader ( 'Content-Type' , 'text/json' ) ;
101- xhr . send ( JSON . stringify ( { 'cql' : code , 'data' : data } ) ) ;
114+
115+ // Collect library content to send with request
116+ const libraryContents = libraries . map ( lib => lib . content ) ;
117+
118+ xhr . send ( JSON . stringify ( {
119+ 'cql' : code ,
120+ 'data' : data ,
121+ 'libraries' : libraryContents
122+ } ) ) ;
102123}
103124
104125/**
@@ -107,9 +128,11 @@ function runCQL() {
107128function showDataTab ( ) {
108129 document . getElementById ( 'cqlEntry' ) . style . display = 'none' ;
109130 document . getElementById ( 'dataEntry' ) . style . display = 'block' ;
131+ document . getElementById ( 'librariesEntry' ) . style . display = 'none' ;
110132
111- document . getElementById ( 'dataTabButton' ) . className + = 'active' ;
133+ document . getElementById ( 'dataTabButton' ) . className = 'active' ;
112134 document . getElementById ( 'cqlTabButton' ) . className = '' ;
135+ document . getElementById ( 'librariesTabButton' ) . className = '' ;
113136}
114137
115138/**
@@ -118,9 +141,134 @@ function showDataTab() {
118141function showCQLTab ( ) {
119142 document . getElementById ( 'cqlEntry' ) . style . display = 'block' ;
120143 document . getElementById ( 'dataEntry' ) . style . display = 'none' ;
144+ document . getElementById ( 'librariesEntry' ) . style . display = 'none' ;
121145
122- document . getElementById ( 'cqlTabButton' ) . className + = 'active' ;
146+ document . getElementById ( 'cqlTabButton' ) . className = 'active' ;
123147 document . getElementById ( 'dataTabButton' ) . className = '' ;
148+ document . getElementById ( 'librariesTabButton' ) . className = '' ;
149+ }
150+
151+ /**
152+ * showLibrariesTab shows the Libraries tab and hides other tabs.
153+ */
154+ function showLibrariesTab ( ) {
155+ document . getElementById ( 'cqlEntry' ) . style . display = 'none' ;
156+ document . getElementById ( 'dataEntry' ) . style . display = 'none' ;
157+ document . getElementById ( 'librariesEntry' ) . style . display = 'block' ;
158+
159+ document . getElementById ( 'librariesTabButton' ) . className = 'active' ;
160+ document . getElementById ( 'cqlTabButton' ) . className = '' ;
161+ document . getElementById ( 'dataTabButton' ) . className = '' ;
162+ }
163+
164+ /**
165+ * addLibrary adds a new library to the libraries list and updates the UI.
166+ */
167+ function addLibrary ( name = '' , content = '' ) {
168+ const libraryId = Date . now ( ) ; // Unique ID for the library
169+
170+ // Add to libraries array
171+ libraries . push ( {
172+ id : libraryId ,
173+ name : name ,
174+ content : content
175+ } ) ;
176+
177+ // Update the UI
178+ renderLibraries ( ) ;
179+
180+ // Save to localStorage
181+ saveLibrariesToLocalStorage ( ) ;
182+ }
183+
184+ /**
185+ * removeLibrary removes a library from the libraries list and updates the UI.
186+ */
187+ function removeLibrary ( libraryId ) {
188+ libraries = libraries . filter ( lib => lib . id !== libraryId ) ;
189+ renderLibraries ( ) ;
190+ saveLibrariesToLocalStorage ( ) ;
191+ }
192+
193+ /**
194+ * renderLibraries updates the libraries UI with the current libraries.
195+ */
196+ function renderLibraries ( ) {
197+ const container = document . getElementById ( 'librariesContainer' ) ;
198+ container . innerHTML = '' ;
199+
200+ libraries . forEach ( library => {
201+ const libraryContainer = document . createElement ( 'div' ) ;
202+ libraryContainer . className = 'libraryContainer' ;
203+
204+ const headerDiv = document . createElement ( 'div' ) ;
205+ headerDiv . className = 'libraryHeader' ;
206+
207+ // Create label for the library name input
208+ const nameLabel = document . createElement ( 'div' ) ;
209+ nameLabel . className = 'libraryNameLabel' ;
210+
211+ const nameInput = document . createElement ( 'input' ) ;
212+ nameInput . type = 'text' ;
213+ nameInput . value = library . name ;
214+ nameInput . placeholder = 'Library Name' ;
215+ nameInput . className = 'libraryNameInput' ;
216+ nameInput . oninput = function ( e ) {
217+ library . name = e . target . value ;
218+ saveLibrariesToLocalStorage ( ) ;
219+ } ;
220+
221+ nameLabel . appendChild ( document . createTextNode ( 'Library Name:' ) ) ;
222+ nameLabel . appendChild ( nameInput ) ;
223+
224+ const removeButton = document . createElement ( 'button' ) ;
225+ removeButton . className = 'removeLibraryButton' ;
226+ removeButton . textContent = 'Remove' ;
227+ removeButton . onclick = function ( ) {
228+ removeLibrary ( library . id ) ;
229+ } ;
230+
231+ headerDiv . appendChild ( nameLabel ) ;
232+ headerDiv . appendChild ( removeButton ) ;
233+
234+ const editorDiv = document . createElement ( 'div' ) ;
235+ editorDiv . className = 'codeInputContainer' ;
236+
237+ const codeInput = document . createElement ( 'code-input' ) ;
238+ codeInput . setAttribute ( 'lang' , 'cql' ) ;
239+ codeInput . setAttribute ( 'placeholder' , 'Type CQL Library Here' ) ;
240+ codeInput . className = 'codeInput' ;
241+ codeInput . value = library . content ;
242+ codeInput . onchange = function ( e ) {
243+ library . content = e . target . value ;
244+ saveLibrariesToLocalStorage ( ) ;
245+ } ;
246+
247+ editorDiv . appendChild ( codeInput ) ;
248+
249+ libraryContainer . appendChild ( headerDiv ) ;
250+ libraryContainer . appendChild ( editorDiv ) ;
251+
252+ container . appendChild ( libraryContainer ) ;
253+ } ) ;
254+ }
255+
256+ /**
257+ * saveLibrariesToLocalStorage saves the libraries to localStorage.
258+ */
259+ function saveLibrariesToLocalStorage ( ) {
260+ localStorage . setItem ( 'cqlLibraries' , JSON . stringify ( libraries ) ) ;
261+ }
262+
263+ /**
264+ * loadLibrariesFromLocalStorage loads the libraries from localStorage.
265+ */
266+ function loadLibrariesFromLocalStorage ( ) {
267+ const storedLibraries = localStorage . getItem ( 'cqlLibraries' ) ;
268+ if ( storedLibraries ) {
269+ libraries = JSON . parse ( storedLibraries ) ;
270+ renderLibraries ( ) ;
271+ }
124272}
125273
126274/**
@@ -150,6 +298,33 @@ function setupPrism() {
150298 'syntax-highlighted' , codeInput . templates . prism ( Prism , [ ] ) ) ;
151299}
152300
301+ /**
302+ * handleFileUpload processes uploaded CQL library files
303+ */
304+ function handleFileUpload ( event ) {
305+ const fileList = event . target . files ;
306+ if ( fileList . length === 0 ) {
307+ return ; // No file selected
308+ }
309+
310+ const file = fileList [ 0 ] ;
311+ const reader = new FileReader ( ) ;
312+
313+ reader . onload = function ( e ) {
314+ const content = e . target . result ;
315+ // Extract library name from filename (remove .cql extension)
316+ const fileName = file . name . replace ( / \. c q l $ / i, '' ) ;
317+
318+ // Add the library with the file content
319+ addLibrary ( fileName , content ) ;
320+ } ;
321+
322+ reader . readAsText ( file ) ;
323+
324+ // Reset the file input so the same file can be selected again
325+ event . target . value = '' ;
326+ }
327+
153328/**
154329 * main is the entrypoint for the script.
155330 */
@@ -159,8 +334,15 @@ function main() {
159334 bindInputsOnChange ( ) ;
160335 bindButtonActions ( ) ;
161336
162- // Initially hide dataEntry tab:
337+ // Load libraries from localStorage
338+ loadLibrariesFromLocalStorage ( ) ;
339+
340+ // Initially hide non-CQL tabs
163341 document . getElementById ( 'dataEntry' ) . style . display = 'none' ;
342+ document . getElementById ( 'librariesEntry' ) . style . display = 'none' ;
343+
344+ // Set CQL tab as active
345+ document . getElementById ( 'cqlTabButton' ) . className = 'active' ;
164346}
165347
166348main ( ) ; // All code actually executed when the script is loaded by the HTML.
0 commit comments