From b840d886a03f6314c71582ce07c09ed45e340343 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 28 Jun 2024 10:04:36 +0100 Subject: [PATCH 001/136] Create draft of visualization page --- Lara-JS/package.json | 2 ++ Lara-JS/src-api/visualization/index.html | 12 ++++++++++ Lara-JS/src-api/visualization/launch.cjs | 30 ++++++++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 Lara-JS/src-api/visualization/index.html create mode 100644 Lara-JS/src-api/visualization/launch.cjs diff --git a/Lara-JS/package.json b/Lara-JS/package.json index 2aebbd405..e432b8f0f 100644 --- a/Lara-JS/package.json +++ b/Lara-JS/package.json @@ -58,8 +58,10 @@ "dependencies": { "chokidar": "^3.6.0", "debug": "^4.3.5", + "express": "^4.19.2", "java": "^0.14.0", "supports-color": "^9.4.0", + "ws": "^8.17.1", "yargs": "^17.7.2" }, "devDependencies": { diff --git a/Lara-JS/src-api/visualization/index.html b/Lara-JS/src-api/visualization/index.html new file mode 100644 index 000000000..58455488a --- /dev/null +++ b/Lara-JS/src-api/visualization/index.html @@ -0,0 +1,12 @@ + + + + + + AST Visualization + + +

AST Visualization

+

AST Visualization

+ + \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/launch.cjs b/Lara-JS/src-api/visualization/launch.cjs new file mode 100644 index 000000000..a6c3234a8 --- /dev/null +++ b/Lara-JS/src-api/visualization/launch.cjs @@ -0,0 +1,30 @@ +const express = require('express'); +const http = require('http'); +const WebSocket = require('ws'); + +const port = 3000; +const domain = 'localhost'; + +const app = express(); +const server = http.createServer(app); +const wss = new WebSocket.Server({ server: server }); + +wss.on('connection', (ws) => { + console.log('[server]: Client connected'); + + ws.on('message', (message) => { + console.log(`[server]: Received message => ${message}`); + }); + + ws.send('Hello! Message from server!'); + + ws.on('close', () => { + console.log('[server]: Client disconnected'); + }); +}); + +app.use(express.static(__dirname)); + +app.listen(port, () => { + console.log(`[server]: Server is running at http://${domain}:${port}`); +}); \ No newline at end of file From b36bbc5aab0a68482924c121a4f377fe551dd5bf Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 28 Jun 2024 11:01:07 +0100 Subject: [PATCH 002/136] Fix WebSocket connection --- Lara-JS/src-api/visualization/launch.cjs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Lara-JS/src-api/visualization/launch.cjs b/Lara-JS/src-api/visualization/launch.cjs index a6c3234a8..73098e28e 100644 --- a/Lara-JS/src-api/visualization/launch.cjs +++ b/Lara-JS/src-api/visualization/launch.cjs @@ -1,5 +1,6 @@ const express = require('express'); const http = require('http'); +const path = require('path'); const WebSocket = require('ws'); const port = 3000; @@ -9,6 +10,12 @@ const app = express(); const server = http.createServer(app); const wss = new WebSocket.Server({ server: server }); +app.use(express.static(path.join(__dirname, 'public'))); + +server.listen(port, () => { + console.log(`[server]: Server is running at http://${domain}:${port}`); +}); + wss.on('connection', (ws) => { console.log('[server]: Client connected'); @@ -16,15 +23,7 @@ wss.on('connection', (ws) => { console.log(`[server]: Received message => ${message}`); }); - ws.send('Hello! Message from server!'); - ws.on('close', () => { console.log('[server]: Client disconnected'); }); -}); - -app.use(express.static(__dirname)); - -app.listen(port, () => { - console.log(`[server]: Server is running at http://${domain}:${port}`); }); \ No newline at end of file From 231bba264c80912f762a1229f54cab8ad87d0c14 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 28 Jun 2024 11:01:54 +0100 Subject: [PATCH 003/136] Move page files to public folder --- Lara-JS/src-api/visualization/{ => public}/index.html | 2 ++ Lara-JS/src-api/visualization/public/js/visualization.js | 5 +++++ 2 files changed, 7 insertions(+) rename Lara-JS/src-api/visualization/{ => public}/index.html (75%) create mode 100644 Lara-JS/src-api/visualization/public/js/visualization.js diff --git a/Lara-JS/src-api/visualization/index.html b/Lara-JS/src-api/visualization/public/index.html similarity index 75% rename from Lara-JS/src-api/visualization/index.html rename to Lara-JS/src-api/visualization/public/index.html index 58455488a..0396a0d51 100644 --- a/Lara-JS/src-api/visualization/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -3,10 +3,12 @@ + AST Visualization

AST Visualization

AST Visualization

+ \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/visualization.js b/Lara-JS/src-api/visualization/public/js/visualization.js new file mode 100644 index 000000000..bcb07b903 --- /dev/null +++ b/Lara-JS/src-api/visualization/public/js/visualization.js @@ -0,0 +1,5 @@ +const socket = new WebSocket('ws://localhost:3000'); + +socket.onmessage = (event) => { + console.log(`[client]: Received message => ${event.data}`); +} \ No newline at end of file From 45748421f364a4c158324c5cab1ade5936b588e0 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 28 Jun 2024 11:44:38 +0100 Subject: [PATCH 004/136] Create simple textareas for the AST and code --- .../visualization/public/css/styles.css | 9 + .../src-api/visualization/public/index.html | 392 +++++++++++++++++- 2 files changed, 399 insertions(+), 2 deletions(-) create mode 100644 Lara-JS/src-api/visualization/public/css/styles.css diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css new file mode 100644 index 000000000..a263e1ce1 --- /dev/null +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -0,0 +1,9 @@ +textarea { + width: 50em; + height: 80em; + color: black; +} + +textarea:not(:first-of-type) { + margin-left: 5em; +} \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 0396a0d51..7c17fce02 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -3,12 +3,400 @@ + AST Visualization

AST Visualization

-

AST Visualization

- + + \ No newline at end of file From 7634afb82ed6bdf8d3dada716b083067d58e4391 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 28 Jun 2024 13:12:24 +0100 Subject: [PATCH 005/136] Improve AST and code containers --- .../visualization/public/css/styles.css | 18 ++++++++++----- .../src-api/visualization/public/index.html | 22 +++++++++++-------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index a263e1ce1..8a3d2facc 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -1,9 +1,17 @@ -textarea { - width: 50em; - height: 80em; - color: black; +#ast-code-visualization > article { + display: inline-block; } -textarea:not(:first-of-type) { +#ast-code-visualization > article:not(:first-of-type) { margin-left: 5em; +} + +pre { + width: 50em; + height: 80em; + overflow: auto; + border: 1px solid black; + border-radius: 2px; + cursor: default; + user-select: none; } \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 7c17fce02..6c60ad42f 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -8,9 +8,11 @@ AST Visualization -

AST Visualization

- - +} + + \ No newline at end of file From 5131cbb36de2cda8092d623d2e1a86de3a964514 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 28 Jun 2024 13:28:31 +0100 Subject: [PATCH 006/136] Fix missing favicon error --- Lara-JS/src-api/visualization/public/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 6c60ad42f..1d56fc52b 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -3,6 +3,7 @@ + AST Visualization From 41832e1948d7ce70308c6b0460fbd0c98bf78f18 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 28 Jun 2024 14:00:45 +0100 Subject: [PATCH 007/136] Highlight AST node on hover --- Lara-JS/src-api/visualization/public/css/styles.css | 9 +++++++++ Lara-JS/src-api/visualization/public/index.html | 2 +- .../src-api/visualization/public/js/visualization.js | 11 ++++++++--- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 8a3d2facc..4369ba160 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -14,4 +14,13 @@ pre { border-radius: 2px; cursor: default; user-select: none; +} + +pre * { + transition: background-color 0.1s ease-out; + border-radius: 4px; +} + +.highlighted { + background-color: yellow; } \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 1d56fc52b..388a221ee 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -20,7 +20,7 @@

AST

Joinpoint 'include' Joinpoint 'comment' Joinpoint 'comment' - Joinpoint 'function' + Joinpoint 'function' Joinpoint 'param' Joinpoint 'param' Joinpoint 'param' diff --git a/Lara-JS/src-api/visualization/public/js/visualization.js b/Lara-JS/src-api/visualization/public/js/visualization.js index bcb07b903..be43397b3 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.js +++ b/Lara-JS/src-api/visualization/public/js/visualization.js @@ -1,5 +1,10 @@ -const socket = new WebSocket('ws://localhost:3000'); +const astNodes = document.querySelectorAll('.ast-node'); -socket.onmessage = (event) => { - console.log(`[client]: Received message => ${event.data}`); +for (const node of astNodes) { + node.addEventListener('mouseover', (event) => { + event.target.classList.toggle('highlighted'); + }); + node.addEventListener('mouseout', (event) => { + event.target.classList.remove('highlighted'); + }); } \ No newline at end of file From cbbc70019d8f2c0691d56e0b6738b7bdbcc4fbb8 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 28 Jun 2024 16:50:04 +0100 Subject: [PATCH 008/136] Highlight code of AST node when node hovered --- .../visualization/public/css/styles.css | 1 - .../src-api/visualization/public/index.html | 1 + .../src-api/visualization/public/js/utils.js | 5 ++ .../visualization/public/js/visualization.js | 59 +++++++++++++++---- 4 files changed, 55 insertions(+), 11 deletions(-) create mode 100644 Lara-JS/src-api/visualization/public/js/utils.js diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 4369ba160..ae3e3d411 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -17,7 +17,6 @@ pre { } pre * { - transition: background-color 0.1s ease-out; border-radius: 4px; } diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 388a221ee..43bdc415e 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -5,6 +5,7 @@ + AST Visualization diff --git a/Lara-JS/src-api/visualization/public/js/utils.js b/Lara-JS/src-api/visualization/public/js/utils.js new file mode 100644 index 000000000..32583cb5e --- /dev/null +++ b/Lara-JS/src-api/visualization/public/js/utils.js @@ -0,0 +1,5 @@ +if (String.prototype.splice === undefined) { + String.prototype.splice = function(index, length, replacement) { + return this.slice(0, index) + replacement + this.slice(index + length); + }; +} \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/visualization.js b/Lara-JS/src-api/visualization/public/js/visualization.js index be43397b3..54970e0a7 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.js +++ b/Lara-JS/src-api/visualization/public/js/visualization.js @@ -1,10 +1,49 @@ -const astNodes = document.querySelectorAll('.ast-node'); - -for (const node of astNodes) { - node.addEventListener('mouseover', (event) => { - event.target.classList.toggle('highlighted'); - }); - node.addEventListener('mouseout', (event) => { - event.target.classList.remove('highlighted'); - }); -} \ No newline at end of file +const codeContainer = document.querySelector('#code code'); + +const highlightNodeAndCode = (node, nodeCode, nodeCodeStart) => { + node.classList.add('highlighted'); + + let codeContainerContents = codeContainer.innerHTML; + codeContainerContents = codeContainerContents.splice( + nodeCodeStart, + nodeCode.length, + '' + nodeCode + '', + ); + console.log(nodeCodeStart, nodeCode.length); + codeContainer.innerHTML = codeContainerContents; +}; + + +const unhighlightNodeAndCode = (node, nodeCode, nodeCodeStart) => { + node.classList.remove('highlighted'); + + const wrapperLen = ''.length + ''.length; + + let codeContainerContents = codeContainer.innerHTML; + codeContainerContents = codeContainerContents.splice( + nodeCodeStart, + nodeCode.length + wrapperLen, + nodeCode, + ); + console.log(nodeCodeStart, nodeCode.length); + codeContainer.innerHTML = codeContainerContents; +} + +const addEventListenersToAstNodes = () => { + const astNodes = document.querySelectorAll('.ast-node'); + + for (const node of astNodes) { + const nodeCode = "void matrix_mult(double const *A, double const *B, double *C, int const N, int const M, int const K) {"; + + const nodeCodeStart = codeContainer.innerHTML.indexOf(nodeCode); + if (nodeCodeStart === -1) { + console.warn('Node code not found in code container'); + continue; + } + + node.addEventListener('mouseover', () => highlightNodeAndCode(node, nodeCode, nodeCodeStart)); + node.addEventListener('mouseout', () => unhighlightNodeAndCode(node, nodeCode, nodeCodeStart)); + } +}; + +addEventListenersToAstNodes(); From bfd487fc0a451f0b6aaa109e8bde55e7eae31662 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 28 Jun 2024 16:59:01 +0100 Subject: [PATCH 009/136] Fix highlight of code with special characters --- .../src-api/visualization/public/js/utils.js | 12 +++++++++++ .../visualization/public/js/visualization.js | 21 ++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/utils.js b/Lara-JS/src-api/visualization/public/js/utils.js index 32583cb5e..702b1b81c 100644 --- a/Lara-JS/src-api/visualization/public/js/utils.js +++ b/Lara-JS/src-api/visualization/public/js/utils.js @@ -2,4 +2,16 @@ if (String.prototype.splice === undefined) { String.prototype.splice = function(index, length, replacement) { return this.slice(0, index) + replacement + this.slice(index + length); }; +} + +function escapeHtml(text) { + var specialCharMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + + return text.replace(/[&<>"']/g, (match) => specialCharMap[match]); } \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/visualization.js b/Lara-JS/src-api/visualization/public/js/visualization.js index 54970e0a7..14d455f2a 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.js +++ b/Lara-JS/src-api/visualization/public/js/visualization.js @@ -1,5 +1,6 @@ const codeContainer = document.querySelector('#code code'); + const highlightNodeAndCode = (node, nodeCode, nodeCodeStart) => { node.classList.add('highlighted'); @@ -9,7 +10,6 @@ const highlightNodeAndCode = (node, nodeCode, nodeCodeStart) => { nodeCode.length, '' + nodeCode + '', ); - console.log(nodeCodeStart, nodeCode.length); codeContainer.innerHTML = codeContainerContents; }; @@ -25,15 +25,30 @@ const unhighlightNodeAndCode = (node, nodeCode, nodeCodeStart) => { nodeCode.length + wrapperLen, nodeCode, ); - console.log(nodeCodeStart, nodeCode.length); codeContainer.innerHTML = codeContainerContents; } + const addEventListenersToAstNodes = () => { const astNodes = document.querySelectorAll('.ast-node'); for (const node of astNodes) { - const nodeCode = "void matrix_mult(double const *A, double const *B, double *C, int const N, int const M, int const K) {"; + const nodeCode = escapeHtml(`void matrix_mult(double const *A, double const *B, double *C, int const N, int const M, int const K) { + for(int ii = 0; ii < N; ii++) { + for(int jj = 0; jj < K; jj++) { + //C[i][j] = 0; + C[K * ii + jj] = 0; + } + } + for(int i = 0; i < N; i++) { + for(int l = 0; l < M; l++) { + for(int j = 0; j < K; j++) { + //C[i][j] += A[i][l]*B[l][j]; + C[K * i + j] += A[M * i + l] * B[K * l + j]; + } + } + } +}`); // TODO: Use real node code const nodeCodeStart = codeContainer.innerHTML.indexOf(nodeCode); if (nodeCodeStart === -1) { From e4eca73bd1c275a63dd2a12f5e8b185b3c041a37 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 1 Jul 2024 11:49:58 +0100 Subject: [PATCH 010/136] Create AST import script --- .../src-api/visualization/public/index.html | 332 +---------------- .../visualization/public/js/ast-import.js | 335 ++++++++++++++++++ 2 files changed, 346 insertions(+), 321 deletions(-) create mode 100644 Lara-JS/src-api/visualization/public/js/ast-import.js diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 43bdc415e..25bb08776 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -6,6 +6,7 @@ + AST Visualization @@ -14,318 +15,7 @@

AST Visualization

AST

-
Joinpoint 'program'
-    Joinpoint 'file'
-        Joinpoint 'include'
-        Joinpoint 'include'
-        Joinpoint 'include'
-        Joinpoint 'comment'
-        Joinpoint 'comment'
-        Joinpoint 'function'  
-            Joinpoint 'param'
-            Joinpoint 'param'
-            Joinpoint 'param'
-            Joinpoint 'param'
-            Joinpoint 'param'
-            Joinpoint 'param'
-            Joinpoint 'body'
-                Joinpoint 'loop'
-                Joinpoint 'declStmt'
-                    Joinpoint 'vardecl'
-                        Joinpoint 'intLiteral'
-                Joinpoint 'exprStmt'
-                    Joinpoint 'binaryOp'
-                        Joinpoint 'varref'
-                        Joinpoint 'varref'
-                Joinpoint 'exprStmt'
-                    Joinpoint 'unaryOp'
-                        Joinpoint 'varref'
-                Joinpoint 'body'
-                    Joinpoint 'loop'
-                        Joinpoint 'declStmt'
-                            Joinpoint 'vardecl'
-                            Joinpoint 'intLiteral'
-                        Joinpoint 'exprStmt'
-                            Joinpoint 'binaryOp'
-                            Joinpoint 'varref'
-                            Joinpoint 'varref'
-                        Joinpoint 'exprStmt'
-                            Joinpoint 'unaryOp'
-                            Joinpoint 'varref'
-                        Joinpoint 'body'
-                            Joinpoint 'wrapperStmt'
-                            Joinpoint 'comment'
-                            Joinpoint 'exprStmt'
-                            Joinpoint 'binaryOp'
-                                Joinpoint 'arrayAccess'
-                                    Joinpoint 'varref'
-                                    Joinpoint 'binaryOp'
-                                        Joinpoint 'binaryOp'
-                                        Joinpoint 'varref'
-                                        Joinpoint 'varref'
-                                        Joinpoint 'varref'
-                                Joinpoint 'intLiteral'
-                Joinpoint 'loop'
-                Joinpoint 'declStmt'
-                    Joinpoint 'vardecl'
-                        Joinpoint 'intLiteral'
-                Joinpoint 'exprStmt'
-                    Joinpoint 'binaryOp'
-                        Joinpoint 'varref'
-                        Joinpoint 'varref'
-                Joinpoint 'exprStmt'
-                    Joinpoint 'unaryOp'
-                        Joinpoint 'varref'
-                Joinpoint 'body'
-                    Joinpoint 'loop'
-                        Joinpoint 'declStmt'
-                            Joinpoint 'vardecl'
-                            Joinpoint 'intLiteral'
-                        Joinpoint 'exprStmt'
-                            Joinpoint 'binaryOp'
-                            Joinpoint 'varref'
-                            Joinpoint 'varref'
-                        Joinpoint 'exprStmt'
-                            Joinpoint 'unaryOp'
-                            Joinpoint 'varref'
-                        Joinpoint 'body'
-                            Joinpoint 'loop'
-                            Joinpoint 'declStmt'
-                                Joinpoint 'vardecl'
-                                    Joinpoint 'intLiteral'
-                            Joinpoint 'exprStmt'
-                                Joinpoint 'binaryOp'
-                                    Joinpoint 'varref'
-                                    Joinpoint 'varref'
-                            Joinpoint 'exprStmt'
-                                Joinpoint 'unaryOp'
-                                    Joinpoint 'varref'
-                            Joinpoint 'body'
-                                Joinpoint 'wrapperStmt'
-                                    Joinpoint 'comment'
-                                Joinpoint 'exprStmt'
-                                    Joinpoint 'binaryOp'
-                                        Joinpoint 'arrayAccess'
-                                        Joinpoint 'varref'
-                                        Joinpoint 'binaryOp'
-                                            Joinpoint 'binaryOp'
-                                                Joinpoint 'varref'
-                                                Joinpoint 'varref'
-                                            Joinpoint 'varref'
-                                        Joinpoint 'binaryOp'
-                                        Joinpoint 'arrayAccess'
-                                            Joinpoint 'varref'
-                                            Joinpoint 'binaryOp'
-                                                Joinpoint 'binaryOp'
-                                                    Joinpoint 'varref'
-                                                    Joinpoint 'varref'
-                                                Joinpoint 'varref'
-                                        Joinpoint 'arrayAccess'
-                                            Joinpoint 'varref'
-                                            Joinpoint 'binaryOp'
-                                                Joinpoint 'binaryOp'
-                                                    Joinpoint 'varref'
-                                                    Joinpoint 'varref'
-                                                Joinpoint 'varref'
-        Joinpoint 'comment'
-        Joinpoint 'function'
-            Joinpoint 'param'
-            Joinpoint 'param'
-            Joinpoint 'param'
-            Joinpoint 'body'
-                Joinpoint 'loop'
-                Joinpoint 'declStmt'
-                    Joinpoint 'vardecl'
-                        Joinpoint 'intLiteral'
-                Joinpoint 'exprStmt'
-                    Joinpoint 'binaryOp'
-                        Joinpoint 'varref'
-                        Joinpoint 'varref'
-                Joinpoint 'exprStmt'
-                    Joinpoint 'unaryOp'
-                        Joinpoint 'varref'
-                Joinpoint 'body'
-                    Joinpoint 'loop'
-                        Joinpoint 'declStmt'
-                            Joinpoint 'vardecl'
-                            Joinpoint 'intLiteral'
-                        Joinpoint 'exprStmt'
-                            Joinpoint 'binaryOp'
-                            Joinpoint 'varref'
-                            Joinpoint 'varref'
-                        Joinpoint 'exprStmt'
-                            Joinpoint 'unaryOp'
-                            Joinpoint 'varref'
-                        Joinpoint 'body'
-                            Joinpoint 'wrapperStmt'
-                            Joinpoint 'comment'
-                            Joinpoint 'exprStmt'
-                            Joinpoint 'binaryOp'
-                                Joinpoint 'arrayAccess'
-                                    Joinpoint 'varref'
-                                    Joinpoint 'binaryOp'
-                                        Joinpoint 'binaryOp'
-                                        Joinpoint 'varref'
-                                        Joinpoint 'varref'
-                                        Joinpoint 'varref'
-                                Joinpoint 'binaryOp'
-                                    Joinpoint 'parenExpr'
-                                        Joinpoint 'cast'
-                                        Joinpoint 'call'
-                                            Joinpoint 'varref'
-                                    Joinpoint 'cast'
-                                        Joinpoint 'intLiteral'
-        Joinpoint 'function'
-            Joinpoint 'param'
-            Joinpoint 'param'
-            Joinpoint 'param'
-            Joinpoint 'body'
-                Joinpoint 'declStmt'
-                Joinpoint 'vardecl'
-                    Joinpoint 'floatLiteral'
-                Joinpoint 'if'
-                Joinpoint 'binaryOp'
-                    Joinpoint 'varref'
-                    Joinpoint 'intLiteral'
-                Joinpoint 'body'
-                    Joinpoint 'exprStmt'
-                        Joinpoint 'call'
-                            Joinpoint 'varref'
-                            Joinpoint 'varref'
-                            Joinpoint 'binaryOp'
-                            Joinpoint 'varref'
-                            Joinpoint 'intLiteral'
-                            Joinpoint 'varref'
-                Joinpoint 'loop'
-                Joinpoint 'declStmt'
-                    Joinpoint 'vardecl'
-                        Joinpoint 'intLiteral'
-                Joinpoint 'exprStmt'
-                    Joinpoint 'binaryOp'
-                        Joinpoint 'varref'
-                        Joinpoint 'varref'
-                Joinpoint 'exprStmt'
-                    Joinpoint 'unaryOp'
-                        Joinpoint 'varref'
-                Joinpoint 'body'
-                    Joinpoint 'loop'
-                        Joinpoint 'declStmt'
-                            Joinpoint 'vardecl'
-                            Joinpoint 'intLiteral'
-                        Joinpoint 'exprStmt'
-                            Joinpoint 'binaryOp'
-                            Joinpoint 'varref'
-                            Joinpoint 'varref'
-                        Joinpoint 'exprStmt'
-                            Joinpoint 'unaryOp'
-                            Joinpoint 'varref'
-                        Joinpoint 'body'
-                            Joinpoint 'wrapperStmt'
-                            Joinpoint 'comment'
-                            Joinpoint 'exprStmt'
-                            Joinpoint 'binaryOp'
-                                Joinpoint 'varref'
-                                Joinpoint 'arrayAccess'
-                                    Joinpoint 'varref'
-                                    Joinpoint 'binaryOp'
-                                        Joinpoint 'binaryOp'
-                                        Joinpoint 'varref'
-                                        Joinpoint 'varref'
-                                        Joinpoint 'varref'
-                Joinpoint 'exprStmt'
-                Joinpoint 'call'
-                    Joinpoint 'varref'
-                    Joinpoint 'literal'
-                    Joinpoint 'varref'
-        Joinpoint 'function'
-            Joinpoint 'body'
-                Joinpoint 'declStmt'
-                Joinpoint 'vardecl'
-                    Joinpoint 'intLiteral'
-                Joinpoint 'declStmt'
-                Joinpoint 'vardecl'
-                    Joinpoint 'intLiteral'
-                Joinpoint 'declStmt'
-                Joinpoint 'vardecl'
-                    Joinpoint 'intLiteral'
-                Joinpoint 'wrapperStmt'
-                Joinpoint 'comment'
-                Joinpoint 'wrapperStmt'
-                Joinpoint 'comment'
-                Joinpoint 'wrapperStmt'
-                Joinpoint 'comment'
-                Joinpoint 'declStmt'
-                Joinpoint 'vardecl'
-                    Joinpoint 'cast'
-                        Joinpoint 'call'
-                            Joinpoint 'varref'
-                            Joinpoint 'binaryOp'
-                            Joinpoint 'binaryOp'
-                                Joinpoint 'varref'
-                                Joinpoint 'varref'
-                            Joinpoint 'unaryExprOrType'
-                Joinpoint 'declStmt'
-                Joinpoint 'vardecl'
-                    Joinpoint 'cast'
-                        Joinpoint 'call'
-                            Joinpoint 'varref'
-                            Joinpoint 'binaryOp'
-                            Joinpoint 'binaryOp'
-                                Joinpoint 'varref'
-                                Joinpoint 'varref'
-                            Joinpoint 'unaryExprOrType'
-                Joinpoint 'declStmt'
-                Joinpoint 'vardecl'
-                    Joinpoint 'cast'
-                        Joinpoint 'call'
-                            Joinpoint 'varref'
-                            Joinpoint 'binaryOp'
-                            Joinpoint 'binaryOp'
-                                Joinpoint 'varref'
-                                Joinpoint 'varref'
-                            Joinpoint 'unaryExprOrType'
-                Joinpoint 'wrapperStmt'
-                Joinpoint 'comment'
-                Joinpoint 'exprStmt'
-                Joinpoint 'call'
-                    Joinpoint 'varref'
-                    Joinpoint 'varref'
-                    Joinpoint 'varref'
-                    Joinpoint 'varref'
-                Joinpoint 'exprStmt'
-                Joinpoint 'call'
-                    Joinpoint 'varref'
-                    Joinpoint 'varref'
-                    Joinpoint 'varref'
-                    Joinpoint 'varref'
-                Joinpoint 'wrapperStmt'
-                Joinpoint 'comment'
-                Joinpoint 'exprStmt'
-                Joinpoint 'call'
-                    Joinpoint 'varref'
-                    Joinpoint 'varref'
-                    Joinpoint 'varref'
-                    Joinpoint 'varref'
-                    Joinpoint 'varref'
-                    Joinpoint 'varref'
-                    Joinpoint 'varref'
-                Joinpoint 'exprStmt'
-                Joinpoint 'call'
-                    Joinpoint 'varref'
-                    Joinpoint 'varref'
-                    Joinpoint 'varref'
-                    Joinpoint 'varref'
-        Joinpoint 'function'
-            Joinpoint 'body'
-                Joinpoint 'wrapperStmt'
-                Joinpoint 'comment'
-                Joinpoint 'exprStmt'
-                Joinpoint 'call'
-                    Joinpoint 'varref'
-                    Joinpoint 'intLiteral'
-                Joinpoint 'exprStmt'
-                Joinpoint 'call'
-                    Joinpoint 'varref'
+

Code

@@ -339,15 +29,15 @@

Code

matrix multiplication */ void matrix_mult(double const *A, double const *B, double *C, int const N, int const M, int const K) { - for(int ii = 0; ii < N; ii++) { - for(int jj = 0; jj < K; jj++) { + for(int ii = 0; ii < N; ii++) { + for(int jj = 0; jj < K; jj++) { //C[i][j] = 0; C[K * ii + jj] = 0; } } - for(int i = 0; i < N; i++) { - for(int l = 0; l < M; l++) { - for(int j = 0; j < K; j++) { + for(int i = 0; i < N; i++) { + for(int l = 0; l < M; l++) { + for(int j = 0; j < K; j++) { //C[i][j] += A[i][l]*B[l][j]; C[K * i + j] += A[M * i + l] * B[K * l + j]; } @@ -359,8 +49,8 @@

Code

* Set an N by M matrix A to random values */ void init_matrix(double *A, int const N, int const M) { - for(int i = 0; i < N; ++i) { - for(int j = 0; j < M; ++j) { + for(int i = 0; i < N; ++i) { + for(int j = 0; j < M; ++j) { //A[i][j] = ((double) rand()) / (double) RAND_MAX; A[M * i + j] = ((double) rand()) / (double) 2147483647; } @@ -370,8 +60,8 @@

Code

void print_matrix_result(double *A, int const N, int const K) { double acc = 0.0; if(N == 1) print_matrix_result(A, N - 1, K); - for(int i = 0; i < N; ++i) { - for(int j = 0; j < K; ++j) { + for(int i = 0; i < N; ++i) { + for(int j = 0; j < K; ++j) { //acc += A[i][j]; acc += A[K * i + j]; } diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.js b/Lara-JS/src-api/visualization/public/js/ast-import.js new file mode 100644 index 000000000..601bc2757 --- /dev/null +++ b/Lara-JS/src-api/visualization/public/js/ast-import.js @@ -0,0 +1,335 @@ +const sampleAst = `Joinpoint 'program' + Joinpoint 'file' + Joinpoint 'include' + Joinpoint 'include' + Joinpoint 'include' + Joinpoint 'comment' + Joinpoint 'comment' + Joinpoint 'function' + Joinpoint 'param' + Joinpoint 'param' + Joinpoint 'param' + Joinpoint 'param' + Joinpoint 'param' + Joinpoint 'param' + Joinpoint 'body' + Joinpoint 'loop' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'unaryOp' + Joinpoint 'varref' + Joinpoint 'body' + Joinpoint 'loop' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'unaryOp' + Joinpoint 'varref' + Joinpoint 'body' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'arrayAccess' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'intLiteral' + Joinpoint 'loop' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'unaryOp' + Joinpoint 'varref' + Joinpoint 'body' + Joinpoint 'loop' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'unaryOp' + Joinpoint 'varref' + Joinpoint 'body' + Joinpoint 'loop' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'unaryOp' + Joinpoint 'varref' + Joinpoint 'body' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'arrayAccess' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'arrayAccess' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'arrayAccess' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'comment' + Joinpoint 'function' + Joinpoint 'param' + Joinpoint 'param' + Joinpoint 'param' + Joinpoint 'body' + Joinpoint 'loop' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'unaryOp' + Joinpoint 'varref' + Joinpoint 'body' + Joinpoint 'loop' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'unaryOp' + Joinpoint 'varref' + Joinpoint 'body' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'arrayAccess' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'parenExpr' + Joinpoint 'cast' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'cast' + Joinpoint 'intLiteral' + Joinpoint 'function' + Joinpoint 'param' + Joinpoint 'param' + Joinpoint 'param' + Joinpoint 'body' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'floatLiteral' + Joinpoint 'if' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'intLiteral' + Joinpoint 'body' + Joinpoint 'exprStmt' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'intLiteral' + Joinpoint 'varref' + Joinpoint 'loop' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'unaryOp' + Joinpoint 'varref' + Joinpoint 'body' + Joinpoint 'loop' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'unaryOp' + Joinpoint 'varref' + Joinpoint 'body' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'arrayAccess' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'literal' + Joinpoint 'varref' + Joinpoint 'function' + Joinpoint 'body' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'cast' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'unaryExprOrType' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'cast' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'unaryExprOrType' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'cast' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'unaryExprOrType' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'exprStmt' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'exprStmt' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'function' + Joinpoint 'body' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'exprStmt' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'call' + Joinpoint 'varref'`; // TODO: Import from Lara + +const fillAstContainer = (ast) => { + const astContainer = document.querySelector('#ast code'); + + for (const node of ast.split('\n')) { + const matches = node.match(/(\s*)Joinpoint '(.+)'/, ''); + if (matches == null) { + console.warn(`Invalid node: "${node}"`); + continue; + } + const [, indentation, nodeName] = matches; + + const nodeElement = document.createElement('span'); + nodeElement.classList.add('ast-node'); // TODO: Add joinpoint info + nodeElement.textContent = nodeName; + + astContainer.innerHTML += indentation + ' - '; + astContainer.appendChild(nodeElement); + astContainer.appendChild(document.createElement('br')); + } +} + +fillAstContainer(sampleAst); From 61ada127ffc3845f55fda47ed47c16d93f50463f Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 1 Jul 2024 15:07:52 +0100 Subject: [PATCH 011/136] Refactor AST node indentation --- Lara-JS/src-api/visualization/public/css/styles.css | 4 ++++ Lara-JS/src-api/visualization/public/js/ast-import.js | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index ae3e3d411..c02dbd761 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -22,4 +22,8 @@ pre * { .highlighted { background-color: yellow; +} + +.ast-node::before { + content: ' - '; } \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.js b/Lara-JS/src-api/visualization/public/js/ast-import.js index 601bc2757..530d8e419 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.js +++ b/Lara-JS/src-api/visualization/public/js/ast-import.js @@ -324,9 +324,9 @@ const fillAstContainer = (ast) => { const nodeElement = document.createElement('span'); nodeElement.classList.add('ast-node'); // TODO: Add joinpoint info + nodeElement.style.marginLeft = (indentation.length / 2) + "em"; nodeElement.textContent = nodeName; - astContainer.innerHTML += indentation + ' - '; astContainer.appendChild(nodeElement); astContainer.appendChild(document.createElement('br')); } From 025c8f90ab1e8b162d2455bcc9e70a506a6381b6 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 2 Jul 2024 11:45:40 +0100 Subject: [PATCH 012/136] Refactor node to code linking --- .../visualization/public/js/ast-import.js | 101 ++++++++++++++---- .../src-api/visualization/public/js/utils.js | 6 -- .../visualization/public/js/visualization.js | 60 ++--------- 3 files changed, 87 insertions(+), 80 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.js b/Lara-JS/src-api/visualization/public/js/ast-import.js index 530d8e419..af320bba1 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.js +++ b/Lara-JS/src-api/visualization/public/js/ast-import.js @@ -1,4 +1,75 @@ -const sampleAst = `Joinpoint 'program' +const createAstNodeElements = (ast) => { + let id = 0; // TODO: Refactor identification (e.g. using astId) + const nodeElements = []; + + for (const node of ast.split('\n')) { + const matches = node.match(/(\s*)Joinpoint '(.+)'/, ''); + if (matches == null) { + console.warn(`Invalid node: "${node}"`); + continue; + } + const [, indentation, nodeName] = matches; + + const nodeElement = document.createElement('span'); + nodeElement.classList.add('ast-node'); // TODO: Add joinpoint info + nodeElement.dataset.nodeId = id++; + nodeElement.style.marginLeft = (indentation.length / 2) + "em"; + nodeElement.textContent = nodeName; + + nodeElements.push(nodeElement); + } + + return nodeElements; +}; + +const fillAstContainer = (nodeElements, astContainer) => { + for (const nodeElement of nodeElements) { + astContainer.appendChild(nodeElement); + astContainer.appendChild(document.createElement('br')); + } +} + +const linkCodeToAstNodes = (nodeElements, codeContainer) => { + for (const nodeElement of nodeElements) { + const nodeCode = `void matrix_mult(double const *A, double const *B, double *C, int const N, int const M, int const K) { + for(int ii = 0; ii < N; ii++) { + for(int jj = 0; jj < K; jj++) { + //C[i][j] = 0; + C[K * ii + jj] = 0; + } + } + for(int i = 0; i < N; i++) { + for(int l = 0; l < M; l++) { + for(int j = 0; j < K; j++) { + //C[i][j] += A[i][l]*B[l][j]; + C[K * i + j] += A[M * i + l] * B[K * l + j]; + } + } + } +}`; // TODO: Use real node code + const nodeCodeHtml = escapeHtml(nodeCode); + + const nodeCodeStart = codeContainer.innerHTML.indexOf(nodeCodeHtml); + if (nodeCodeStart === -1) { + console.warn(`Node code not found in code container: "${nodeCode}"`); + continue; + } + + const nodeCodeWrapper = document.createElement('span'); + nodeCodeWrapper.classList.add('node-code'); + nodeCodeWrapper.dataset.nodeId = nodeElement.dataset.nodeId; + nodeCodeWrapper.textContent = nodeCode; + + codeContainer.innerHTML = codeContainer.innerHTML.replace(nodeCodeHtml, nodeCodeWrapper.outerHTML); + } +} + + +(function() { + const astContainer = document.querySelector('#ast code'); + const codeContainer = document.querySelector('#code code'); + + const sampleAst = `Joinpoint 'program' Joinpoint 'file' Joinpoint 'include' Joinpoint 'include' @@ -311,25 +382,9 @@ const sampleAst = `Joinpoint 'program' Joinpoint 'call' Joinpoint 'varref'`; // TODO: Import from Lara -const fillAstContainer = (ast) => { - const astContainer = document.querySelector('#ast code'); - - for (const node of ast.split('\n')) { - const matches = node.match(/(\s*)Joinpoint '(.+)'/, ''); - if (matches == null) { - console.warn(`Invalid node: "${node}"`); - continue; - } - const [, indentation, nodeName] = matches; - - const nodeElement = document.createElement('span'); - nodeElement.classList.add('ast-node'); // TODO: Add joinpoint info - nodeElement.style.marginLeft = (indentation.length / 2) + "em"; - nodeElement.textContent = nodeName; - - astContainer.appendChild(nodeElement); - astContainer.appendChild(document.createElement('br')); - } -} - -fillAstContainer(sampleAst); + if (astContainer != null) { + const nodeElements = createAstNodeElements(sampleAst); + fillAstContainer(nodeElements, astContainer); + linkCodeToAstNodes(nodeElements, codeContainer); + } +})(); diff --git a/Lara-JS/src-api/visualization/public/js/utils.js b/Lara-JS/src-api/visualization/public/js/utils.js index 702b1b81c..b8147094e 100644 --- a/Lara-JS/src-api/visualization/public/js/utils.js +++ b/Lara-JS/src-api/visualization/public/js/utils.js @@ -1,9 +1,3 @@ -if (String.prototype.splice === undefined) { - String.prototype.splice = function(index, length, replacement) { - return this.slice(0, index) + replacement + this.slice(index + length); - }; -} - function escapeHtml(text) { var specialCharMap = { '&': '&', diff --git a/Lara-JS/src-api/visualization/public/js/visualization.js b/Lara-JS/src-api/visualization/public/js/visualization.js index 14d455f2a..49cc9548a 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.js +++ b/Lara-JS/src-api/visualization/public/js/visualization.js @@ -1,63 +1,21 @@ const codeContainer = document.querySelector('#code code'); - -const highlightNodeAndCode = (node, nodeCode, nodeCodeStart) => { - node.classList.add('highlighted'); - - let codeContainerContents = codeContainer.innerHTML; - codeContainerContents = codeContainerContents.splice( - nodeCodeStart, - nodeCode.length, - '' + nodeCode + '', - ); - codeContainer.innerHTML = codeContainerContents; +const highlightNode = (node) => { + document.querySelectorAll(`span[data-node-id="${node.dataset.nodeId}"]`) + .forEach(element => element.classList.add('highlighted')); }; - -const unhighlightNodeAndCode = (node, nodeCode, nodeCodeStart) => { - node.classList.remove('highlighted'); - - const wrapperLen = ''.length + ''.length; - - let codeContainerContents = codeContainer.innerHTML; - codeContainerContents = codeContainerContents.splice( - nodeCodeStart, - nodeCode.length + wrapperLen, - nodeCode, - ); - codeContainer.innerHTML = codeContainerContents; +const unhighlightNode = (node) => { + document.querySelectorAll(`span[data-node-id="${node.dataset.nodeId}"]`) + .forEach(element => element.classList.remove('highlighted')); } - const addEventListenersToAstNodes = () => { const astNodes = document.querySelectorAll('.ast-node'); - for (const node of astNodes) { - const nodeCode = escapeHtml(`void matrix_mult(double const *A, double const *B, double *C, int const N, int const M, int const K) { - for(int ii = 0; ii < N; ii++) { - for(int jj = 0; jj < K; jj++) { - //C[i][j] = 0; - C[K * ii + jj] = 0; - } - } - for(int i = 0; i < N; i++) { - for(int l = 0; l < M; l++) { - for(int j = 0; j < K; j++) { - //C[i][j] += A[i][l]*B[l][j]; - C[K * i + j] += A[M * i + l] * B[K * l + j]; - } - } - } -}`); // TODO: Use real node code - - const nodeCodeStart = codeContainer.innerHTML.indexOf(nodeCode); - if (nodeCodeStart === -1) { - console.warn('Node code not found in code container'); - continue; - } - - node.addEventListener('mouseover', () => highlightNodeAndCode(node, nodeCode, nodeCodeStart)); - node.addEventListener('mouseout', () => unhighlightNodeAndCode(node, nodeCode, nodeCodeStart)); + for (const nodeElement of astNodes) { + nodeElement.addEventListener('mouseover', () => highlightNode(nodeElement)); + nodeElement.addEventListener('mouseout', () => unhighlightNode(nodeElement)); } }; From 7cb33b9f77d1ec7f064cee7aae5fcd8f5fd1e9e0 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 2 Jul 2024 11:49:16 +0100 Subject: [PATCH 013/136] Refactor visualization script --- .../visualization/public/js/visualization.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/visualization.js b/Lara-JS/src-api/visualization/public/js/visualization.js index 49cc9548a..fb437a994 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.js +++ b/Lara-JS/src-api/visualization/public/js/visualization.js @@ -1,5 +1,3 @@ -const codeContainer = document.querySelector('#code code'); - const highlightNode = (node) => { document.querySelectorAll(`span[data-node-id="${node.dataset.nodeId}"]`) .forEach(element => element.classList.add('highlighted')); @@ -10,13 +8,14 @@ const unhighlightNode = (node) => { .forEach(element => element.classList.remove('highlighted')); } -const addEventListenersToAstNodes = () => { - const astNodes = document.querySelectorAll('.ast-node'); - - for (const nodeElement of astNodes) { +const addEventListenersToAstNodes = (nodes) => { + for (const nodeElement of nodes) { nodeElement.addEventListener('mouseover', () => highlightNode(nodeElement)); nodeElement.addEventListener('mouseout', () => unhighlightNode(nodeElement)); } }; -addEventListenersToAstNodes(); +(function () { + const astNodes = document.querySelectorAll('.ast-node'); + addEventListenersToAstNodes(astNodes); +})(); From eafa60f0feb2909a1d4441d365c45b37a61094c3 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 2 Jul 2024 12:20:08 +0100 Subject: [PATCH 014/136] Implement highlighting of nodes and code when code is hovered --- .../visualization/public/js/visualization.js | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/visualization.js b/Lara-JS/src-api/visualization/public/js/visualization.js index fb437a994..3532437a1 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.js +++ b/Lara-JS/src-api/visualization/public/js/visualization.js @@ -1,17 +1,24 @@ -const highlightNode = (node) => { - document.querySelectorAll(`span[data-node-id="${node.dataset.nodeId}"]`) - .forEach(element => element.classList.add('highlighted')); +const getElementsWithNodeId = (id) => { + return document.querySelectorAll(`span[data-node-id="${id}"]`); +} + +const highlightElements = (elements) => { + elements.forEach(element => element.classList.add('highlighted')); }; -const unhighlightNode = (node) => { - document.querySelectorAll(`span[data-node-id="${node.dataset.nodeId}"]`) - .forEach(element => element.classList.remove('highlighted')); +const unhighlightElements = (elements) => { + elements.forEach(element => element.classList.remove('highlighted')); } const addEventListenersToAstNodes = (nodes) => { for (const nodeElement of nodes) { - nodeElement.addEventListener('mouseover', () => highlightNode(nodeElement)); - nodeElement.addEventListener('mouseout', () => unhighlightNode(nodeElement)); + const nodeId = nodeElement.dataset.nodeId; + const nodeRelatedElements = getElementsWithNodeId(nodeId); + + for (const nodeRelatedElement of nodeRelatedElements) { + nodeRelatedElement.addEventListener('mouseover', () => highlightElements(nodeRelatedElements)); + nodeRelatedElement.addEventListener('mouseout', () => unhighlightElements(nodeRelatedElements)); + } } }; From b3f0777ba44e1b6adfb8025b01f9e7d4cb181e92 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 2 Jul 2024 15:21:42 +0100 Subject: [PATCH 015/136] Make tool highlight all possible code correspondences --- Lara-JS/src-api/visualization/public/js/ast-import.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.js b/Lara-JS/src-api/visualization/public/js/ast-import.js index af320bba1..95159e89e 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.js +++ b/Lara-JS/src-api/visualization/public/js/ast-import.js @@ -60,7 +60,8 @@ const linkCodeToAstNodes = (nodeElements, codeContainer) => { nodeCodeWrapper.dataset.nodeId = nodeElement.dataset.nodeId; nodeCodeWrapper.textContent = nodeCode; - codeContainer.innerHTML = codeContainer.innerHTML.replace(nodeCodeHtml, nodeCodeWrapper.outerHTML); + codeContainer.innerHTML = codeContainer.innerHTML.replaceAll(nodeCodeHtml, nodeCodeWrapper.outerHTML); + // TODO: Associate only the real match (this associates all code fragments that are identical to the node code) } } From c2f54e611271201b94212bc323c0eadc3c996832 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 2 Jul 2024 15:23:28 +0100 Subject: [PATCH 016/136] Convert launch script into a module --- .../visualization/{launch.cjs => launch.js} | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) rename Lara-JS/src-api/visualization/{launch.cjs => launch.js} (57%) diff --git a/Lara-JS/src-api/visualization/launch.cjs b/Lara-JS/src-api/visualization/launch.js similarity index 57% rename from Lara-JS/src-api/visualization/launch.cjs rename to Lara-JS/src-api/visualization/launch.js index 73098e28e..2cf55c97e 100644 --- a/Lara-JS/src-api/visualization/launch.cjs +++ b/Lara-JS/src-api/visualization/launch.js @@ -1,16 +1,20 @@ -const express = require('express'); -const http = require('http'); -const path = require('path'); -const WebSocket = require('ws'); +import express from 'express'; +import http from 'http'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import { WebSocketServer } from 'ws'; const port = 3000; const domain = 'localhost'; const app = express(); const server = http.createServer(app); -const wss = new WebSocket.Server({ server: server }); +const wss = new WebSocketServer({ server: server }); -app.use(express.static(path.join(__dirname, 'public'))); +const filename = fileURLToPath(import.meta.url); +const dirname = path.dirname(filename); + +app.use(express.static(path.join(dirname, 'public'))); server.listen(port, () => { console.log(`[server]: Server is running at http://${domain}:${port}`); From 46681ffbc236398e524d5da64a8981f811d2a204 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 3 Jul 2024 15:04:18 +0100 Subject: [PATCH 017/136] Copy tool page folder to api --- Lara-JS/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lara-JS/package.json b/Lara-JS/package.json index e432b8f0f..dfa1cdf50 100644 --- a/Lara-JS/package.json +++ b/Lara-JS/package.json @@ -21,7 +21,7 @@ "tsconfig.json" ], "scripts": { - "build": "node scripts/copy-folder.js -i src-api/libs -o api/libs && npx tsc -b src-api src-code", + "build": "node scripts/copy-folder.js -i src-api/libs -o api/libs && node scripts/copy-folder.js -i src-api/visualization/public -o api/visualization/public && npx tsc -b src-api src-code", "build:api": "npx tsc -b src-api", "build:code": "npx tsc -b src-code", "build:watch": "npm run build -- --watch", From ff11659ecc1437d8926d5c5d39c4e1f3f76efc88 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 3 Jul 2024 15:11:48 +0100 Subject: [PATCH 018/136] Export launcher script to TypeScript --- Lara-JS/src-api/visualization/launch.js | 33 -- .../src-api/visualization/tool-launcher.ts | 34 ++ .../up/fe/specs/lara/LaraApiJsResource.java | 4 + .../visualization/public/js/ast-import.js | 391 ++++++++++++++++++ .../src-lara/visualization/public/js/utils.js | 11 + .../visualization/public/js/visualization.js | 28 ++ .../src-lara/visualization/tool-launcher.js | 27 ++ 7 files changed, 495 insertions(+), 33 deletions(-) delete mode 100644 Lara-JS/src-api/visualization/launch.js create mode 100644 Lara-JS/src-api/visualization/tool-launcher.ts create mode 100644 LaraApi/src-lara/visualization/public/js/ast-import.js create mode 100644 LaraApi/src-lara/visualization/public/js/utils.js create mode 100644 LaraApi/src-lara/visualization/public/js/visualization.js create mode 100644 LaraApi/src-lara/visualization/tool-launcher.js diff --git a/Lara-JS/src-api/visualization/launch.js b/Lara-JS/src-api/visualization/launch.js deleted file mode 100644 index 2cf55c97e..000000000 --- a/Lara-JS/src-api/visualization/launch.js +++ /dev/null @@ -1,33 +0,0 @@ -import express from 'express'; -import http from 'http'; -import path from 'path'; -import { fileURLToPath } from 'url'; -import { WebSocketServer } from 'ws'; - -const port = 3000; -const domain = 'localhost'; - -const app = express(); -const server = http.createServer(app); -const wss = new WebSocketServer({ server: server }); - -const filename = fileURLToPath(import.meta.url); -const dirname = path.dirname(filename); - -app.use(express.static(path.join(dirname, 'public'))); - -server.listen(port, () => { - console.log(`[server]: Server is running at http://${domain}:${port}`); -}); - -wss.on('connection', (ws) => { - console.log('[server]: Client connected'); - - ws.on('message', (message) => { - console.log(`[server]: Received message => ${message}`); - }); - - ws.on('close', () => { - console.log('[server]: Client disconnected'); - }); -}); \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/tool-launcher.ts b/Lara-JS/src-api/visualization/tool-launcher.ts new file mode 100644 index 000000000..1106f0d02 --- /dev/null +++ b/Lara-JS/src-api/visualization/tool-launcher.ts @@ -0,0 +1,34 @@ +import express from 'express'; +import http from 'http'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import WebSocket, { WebSocketServer } from 'ws'; + +const launchVisualizationTool = (domain: string, port: number) => { + const app = express(); + const server = http.createServer(app); + const wss = new WebSocketServer({ server: server }); + + const filename = fileURLToPath(import.meta.url); + const dirname = path.dirname(filename); + + app.use(express.static(path.join(dirname, 'public'))); + + server.listen(port, () => { + console.log(`[server]: Server is running at http://${domain}:${port}`); + }); + + wss.on('connection', (ws: WebSocket) => { + console.log('[server]: Client connected'); + + ws.on('message', (message: string) => { + console.log(`[server]: Received message => ${message}`); + }); + + ws.on('close', () => { + console.log('[server]: Client disconnected'); + }); + }); +}; + +launchVisualizationTool('localhost', 3000); \ No newline at end of file diff --git a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java index a382ca4e5..a6f17aff2 100644 --- a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java +++ b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java @@ -105,6 +105,10 @@ public enum LaraApiJsResource implements LaraResourceProvider { TIMEUNITS_JS("lara/util/TimeUnits.js"), TUPLEID_JS("lara/util/TupleId.js"), CYTOSCAPE_3_26_0_JS("libs/cytoscape-3.26.0.js"), + AST_IMPORT_JS("visualization/public/js/ast-import.js"), + UTILS_JS("visualization/public/js/utils.js"), + VISUALIZATION_JS("visualization/public/js/visualization.js"), + TOOL_LAUNCHER_JS("visualization/tool-launcher.js"), AST_JS("weaver/Ast.js"), JOINPOINTS_JS("weaver/JoinPoints.js"), QUERY_JS("weaver/Query.js"), diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js new file mode 100644 index 000000000..95159e89e --- /dev/null +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -0,0 +1,391 @@ +const createAstNodeElements = (ast) => { + let id = 0; // TODO: Refactor identification (e.g. using astId) + const nodeElements = []; + + for (const node of ast.split('\n')) { + const matches = node.match(/(\s*)Joinpoint '(.+)'/, ''); + if (matches == null) { + console.warn(`Invalid node: "${node}"`); + continue; + } + const [, indentation, nodeName] = matches; + + const nodeElement = document.createElement('span'); + nodeElement.classList.add('ast-node'); // TODO: Add joinpoint info + nodeElement.dataset.nodeId = id++; + nodeElement.style.marginLeft = (indentation.length / 2) + "em"; + nodeElement.textContent = nodeName; + + nodeElements.push(nodeElement); + } + + return nodeElements; +}; + +const fillAstContainer = (nodeElements, astContainer) => { + for (const nodeElement of nodeElements) { + astContainer.appendChild(nodeElement); + astContainer.appendChild(document.createElement('br')); + } +} + +const linkCodeToAstNodes = (nodeElements, codeContainer) => { + for (const nodeElement of nodeElements) { + const nodeCode = `void matrix_mult(double const *A, double const *B, double *C, int const N, int const M, int const K) { + for(int ii = 0; ii < N; ii++) { + for(int jj = 0; jj < K; jj++) { + //C[i][j] = 0; + C[K * ii + jj] = 0; + } + } + for(int i = 0; i < N; i++) { + for(int l = 0; l < M; l++) { + for(int j = 0; j < K; j++) { + //C[i][j] += A[i][l]*B[l][j]; + C[K * i + j] += A[M * i + l] * B[K * l + j]; + } + } + } +}`; // TODO: Use real node code + const nodeCodeHtml = escapeHtml(nodeCode); + + const nodeCodeStart = codeContainer.innerHTML.indexOf(nodeCodeHtml); + if (nodeCodeStart === -1) { + console.warn(`Node code not found in code container: "${nodeCode}"`); + continue; + } + + const nodeCodeWrapper = document.createElement('span'); + nodeCodeWrapper.classList.add('node-code'); + nodeCodeWrapper.dataset.nodeId = nodeElement.dataset.nodeId; + nodeCodeWrapper.textContent = nodeCode; + + codeContainer.innerHTML = codeContainer.innerHTML.replaceAll(nodeCodeHtml, nodeCodeWrapper.outerHTML); + // TODO: Associate only the real match (this associates all code fragments that are identical to the node code) + } +} + + +(function() { + const astContainer = document.querySelector('#ast code'); + const codeContainer = document.querySelector('#code code'); + + const sampleAst = `Joinpoint 'program' + Joinpoint 'file' + Joinpoint 'include' + Joinpoint 'include' + Joinpoint 'include' + Joinpoint 'comment' + Joinpoint 'comment' + Joinpoint 'function' + Joinpoint 'param' + Joinpoint 'param' + Joinpoint 'param' + Joinpoint 'param' + Joinpoint 'param' + Joinpoint 'param' + Joinpoint 'body' + Joinpoint 'loop' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'unaryOp' + Joinpoint 'varref' + Joinpoint 'body' + Joinpoint 'loop' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'unaryOp' + Joinpoint 'varref' + Joinpoint 'body' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'arrayAccess' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'intLiteral' + Joinpoint 'loop' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'unaryOp' + Joinpoint 'varref' + Joinpoint 'body' + Joinpoint 'loop' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'unaryOp' + Joinpoint 'varref' + Joinpoint 'body' + Joinpoint 'loop' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'unaryOp' + Joinpoint 'varref' + Joinpoint 'body' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'arrayAccess' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'arrayAccess' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'arrayAccess' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'comment' + Joinpoint 'function' + Joinpoint 'param' + Joinpoint 'param' + Joinpoint 'param' + Joinpoint 'body' + Joinpoint 'loop' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'unaryOp' + Joinpoint 'varref' + Joinpoint 'body' + Joinpoint 'loop' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'unaryOp' + Joinpoint 'varref' + Joinpoint 'body' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'arrayAccess' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'parenExpr' + Joinpoint 'cast' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'cast' + Joinpoint 'intLiteral' + Joinpoint 'function' + Joinpoint 'param' + Joinpoint 'param' + Joinpoint 'param' + Joinpoint 'body' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'floatLiteral' + Joinpoint 'if' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'intLiteral' + Joinpoint 'body' + Joinpoint 'exprStmt' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'intLiteral' + Joinpoint 'varref' + Joinpoint 'loop' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'unaryOp' + Joinpoint 'varref' + Joinpoint 'body' + Joinpoint 'loop' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'unaryOp' + Joinpoint 'varref' + Joinpoint 'body' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'exprStmt' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'arrayAccess' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'literal' + Joinpoint 'varref' + Joinpoint 'function' + Joinpoint 'body' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'intLiteral' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'cast' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'unaryExprOrType' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'cast' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'unaryExprOrType' + Joinpoint 'declStmt' + Joinpoint 'vardecl' + Joinpoint 'cast' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'binaryOp' + Joinpoint 'binaryOp' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'unaryExprOrType' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'exprStmt' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'exprStmt' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'exprStmt' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'varref' + Joinpoint 'function' + Joinpoint 'body' + Joinpoint 'wrapperStmt' + Joinpoint 'comment' + Joinpoint 'exprStmt' + Joinpoint 'call' + Joinpoint 'varref' + Joinpoint 'intLiteral' + Joinpoint 'exprStmt' + Joinpoint 'call' + Joinpoint 'varref'`; // TODO: Import from Lara + + if (astContainer != null) { + const nodeElements = createAstNodeElements(sampleAst); + fillAstContainer(nodeElements, astContainer); + linkCodeToAstNodes(nodeElements, codeContainer); + } +})(); diff --git a/LaraApi/src-lara/visualization/public/js/utils.js b/LaraApi/src-lara/visualization/public/js/utils.js new file mode 100644 index 000000000..b8147094e --- /dev/null +++ b/LaraApi/src-lara/visualization/public/js/utils.js @@ -0,0 +1,11 @@ +function escapeHtml(text) { + var specialCharMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + + return text.replace(/[&<>"']/g, (match) => specialCharMap[match]); +} \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js new file mode 100644 index 000000000..3532437a1 --- /dev/null +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -0,0 +1,28 @@ +const getElementsWithNodeId = (id) => { + return document.querySelectorAll(`span[data-node-id="${id}"]`); +} + +const highlightElements = (elements) => { + elements.forEach(element => element.classList.add('highlighted')); +}; + +const unhighlightElements = (elements) => { + elements.forEach(element => element.classList.remove('highlighted')); +} + +const addEventListenersToAstNodes = (nodes) => { + for (const nodeElement of nodes) { + const nodeId = nodeElement.dataset.nodeId; + const nodeRelatedElements = getElementsWithNodeId(nodeId); + + for (const nodeRelatedElement of nodeRelatedElements) { + nodeRelatedElement.addEventListener('mouseover', () => highlightElements(nodeRelatedElements)); + nodeRelatedElement.addEventListener('mouseout', () => unhighlightElements(nodeRelatedElements)); + } + } +}; + +(function () { + const astNodes = document.querySelectorAll('.ast-node'); + addEventListenersToAstNodes(astNodes); +})(); diff --git a/LaraApi/src-lara/visualization/tool-launcher.js b/LaraApi/src-lara/visualization/tool-launcher.js new file mode 100644 index 000000000..eab1f7705 --- /dev/null +++ b/LaraApi/src-lara/visualization/tool-launcher.js @@ -0,0 +1,27 @@ +import express from 'express'; +import http from 'http'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import { WebSocketServer } from 'ws'; +const launchVisualizationTool = (domain, port) => { + const app = express(); + const server = http.createServer(app); + const wss = new WebSocketServer({ server: server }); + const filename = fileURLToPath(import.meta.url); + const dirname = path.dirname(filename); + app.use(express.static(path.join(dirname, 'public'))); + server.listen(port, () => { + console.log(`[server]: Server is running at http://${domain}:${port}`); + }); + wss.on('connection', (ws) => { + console.log('[server]: Client connected'); + ws.on('message', (message) => { + console.log(`[server]: Received message => ${message}`); + }); + ws.on('close', () => { + console.log('[server]: Client disconnected'); + }); + }); +}; +launchVisualizationTool('localhost', 3000); +//# sourceMappingURL=tool-launcher.js.map \ No newline at end of file From 9d2a4e9799aaa413ecde7e8f66b7647fe81dcf7e Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 3 Jul 2024 16:54:07 +0100 Subject: [PATCH 019/136] Convert launch script to VisualizationTool class --- .../visualization/VisualizationTool.ts | 52 +++++++++++++++++++ .../src-api/visualization/tool-launcher.ts | 34 ------------ .../up/fe/specs/lara/LaraApiJsResource.java | 2 +- .../visualization/VisualizationTool.js | 41 +++++++++++++++ 4 files changed, 94 insertions(+), 35 deletions(-) create mode 100644 Lara-JS/src-api/visualization/VisualizationTool.ts delete mode 100644 Lara-JS/src-api/visualization/tool-launcher.ts create mode 100644 LaraApi/src-lara/visualization/VisualizationTool.js diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts new file mode 100644 index 000000000..648fef011 --- /dev/null +++ b/Lara-JS/src-api/visualization/VisualizationTool.ts @@ -0,0 +1,52 @@ +import express from 'express'; +import http from 'http'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import WebSocket, { WebSocketServer } from 'ws'; + +export default class VisualizationTool { + host: string; + port: number; + + constructor(host: string, port: number) { + this.host = host; + this.port = port; + } + + getDomain(): string { + return this.host; + } + + getPort(): number { + return this.port; + } + + launch(): void { + const app = express(); + const server = http.createServer(app); + const wss = new WebSocketServer({ server: server }); + + const filename = fileURLToPath(import.meta.url); + const dirname = path.dirname(filename); + + app.use(express.static(path.join(dirname, 'public'))); + + server.listen(this.port, () => { + console.log(`[server]: Server is running at http://${this.host}:${this.port}`); + }); + + wss.on('connection', (ws: WebSocket) => { + console.log('[server]: Client connected'); + + ws.on('message', (message: string) => { + console.log(`[server]: Received message => ${message}`); + }); + + ws.on('close', () => { + console.log('[server]: Client disconnected'); + }); + }); + } +} + +new VisualizationTool('localhost', 3000).launch(); \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/tool-launcher.ts b/Lara-JS/src-api/visualization/tool-launcher.ts deleted file mode 100644 index 1106f0d02..000000000 --- a/Lara-JS/src-api/visualization/tool-launcher.ts +++ /dev/null @@ -1,34 +0,0 @@ -import express from 'express'; -import http from 'http'; -import path from 'path'; -import { fileURLToPath } from 'url'; -import WebSocket, { WebSocketServer } from 'ws'; - -const launchVisualizationTool = (domain: string, port: number) => { - const app = express(); - const server = http.createServer(app); - const wss = new WebSocketServer({ server: server }); - - const filename = fileURLToPath(import.meta.url); - const dirname = path.dirname(filename); - - app.use(express.static(path.join(dirname, 'public'))); - - server.listen(port, () => { - console.log(`[server]: Server is running at http://${domain}:${port}`); - }); - - wss.on('connection', (ws: WebSocket) => { - console.log('[server]: Client connected'); - - ws.on('message', (message: string) => { - console.log(`[server]: Received message => ${message}`); - }); - - ws.on('close', () => { - console.log('[server]: Client disconnected'); - }); - }); -}; - -launchVisualizationTool('localhost', 3000); \ No newline at end of file diff --git a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java index a6f17aff2..be1cb94d4 100644 --- a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java +++ b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java @@ -105,10 +105,10 @@ public enum LaraApiJsResource implements LaraResourceProvider { TIMEUNITS_JS("lara/util/TimeUnits.js"), TUPLEID_JS("lara/util/TupleId.js"), CYTOSCAPE_3_26_0_JS("libs/cytoscape-3.26.0.js"), + VISUALIZATIONTOOL_JS("visualization/VisualizationTool.js"), AST_IMPORT_JS("visualization/public/js/ast-import.js"), UTILS_JS("visualization/public/js/utils.js"), VISUALIZATION_JS("visualization/public/js/visualization.js"), - TOOL_LAUNCHER_JS("visualization/tool-launcher.js"), AST_JS("weaver/Ast.js"), JOINPOINTS_JS("weaver/JoinPoints.js"), QUERY_JS("weaver/Query.js"), diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js new file mode 100644 index 000000000..03ab01a35 --- /dev/null +++ b/LaraApi/src-lara/visualization/VisualizationTool.js @@ -0,0 +1,41 @@ +import express from 'express'; +import http from 'http'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import { WebSocketServer } from 'ws'; +export default class VisualizationTool { + host; + port; + constructor(host, port) { + this.host = host; + this.port = port; + } + getDomain() { + return this.host; + } + getPort() { + return this.port; + } + launch() { + const app = express(); + const server = http.createServer(app); + const wss = new WebSocketServer({ server: server }); + const filename = fileURLToPath(import.meta.url); + const dirname = path.dirname(filename); + app.use(express.static(path.join(dirname, 'public'))); + server.listen(this.port, () => { + console.log(`[server]: Server is running at http://${this.host}:${this.port}`); + }); + wss.on('connection', (ws) => { + console.log('[server]: Client connected'); + ws.on('message', (message) => { + console.log(`[server]: Received message => ${message}`); + }); + ws.on('close', () => { + console.log('[server]: Client disconnected'); + }); + }); + } +} +new VisualizationTool('localhost', 3000).launch(); +//# sourceMappingURL=VisualizationTool.js.map \ No newline at end of file From 207cd678ab62a6a8bfde9026f3340be7f44ef7e4 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 4 Jul 2024 12:13:14 +0100 Subject: [PATCH 020/136] Convert page scripts to TypeScript --- .../src-api/visualization/public/index.html | 6 +- .../js/{ast-import.js => ast-import.ts} | 16 +-- .../public/js/{utils.js => utils.ts} | 8 +- .../js/{visualization.js => visualization.ts} | 16 ++- .../visualization/public/js/ast-import.js | 113 ++++++++---------- .../src-lara/visualization/public/js/utils.js | 21 ++-- .../visualization/public/js/visualization.js | 38 +++--- 7 files changed, 108 insertions(+), 110 deletions(-) rename Lara-JS/src-api/visualization/public/js/{ast-import.js => ast-import.ts} (96%) rename Lara-JS/src-api/visualization/public/js/{utils.js => utils.ts} (57%) rename Lara-JS/src-api/visualization/public/js/{visualization.js => visualization.ts} (57%) diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 25bb08776..3d7def0ce 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -5,9 +5,9 @@ - - - + + + AST Visualization diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.js b/Lara-JS/src-api/visualization/public/js/ast-import.ts similarity index 96% rename from Lara-JS/src-api/visualization/public/js/ast-import.js rename to Lara-JS/src-api/visualization/public/js/ast-import.ts index 95159e89e..e3d414a0b 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.js +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -1,4 +1,6 @@ -const createAstNodeElements = (ast) => { +import { escapeHtml } from './utils.js'; + +const createAstNodeElements = (ast: any): HTMLElement[] => { let id = 0; // TODO: Refactor identification (e.g. using astId) const nodeElements = []; @@ -12,7 +14,7 @@ const createAstNodeElements = (ast) => { const nodeElement = document.createElement('span'); nodeElement.classList.add('ast-node'); // TODO: Add joinpoint info - nodeElement.dataset.nodeId = id++; + nodeElement.dataset.nodeId = (id++).toString(); nodeElement.style.marginLeft = (indentation.length / 2) + "em"; nodeElement.textContent = nodeName; @@ -22,14 +24,14 @@ const createAstNodeElements = (ast) => { return nodeElements; }; -const fillAstContainer = (nodeElements, astContainer) => { +const fillAstContainer = (nodeElements: HTMLElement[], astContainer: HTMLElement): void => { for (const nodeElement of nodeElements) { astContainer.appendChild(nodeElement); astContainer.appendChild(document.createElement('br')); } } -const linkCodeToAstNodes = (nodeElements, codeContainer) => { +const linkCodeToAstNodes = (nodeElements: HTMLElement[], codeContainer: HTMLElement): void => { for (const nodeElement of nodeElements) { const nodeCode = `void matrix_mult(double const *A, double const *B, double *C, int const N, int const M, int const K) { for(int ii = 0; ii < N; ii++) { @@ -67,8 +69,8 @@ const linkCodeToAstNodes = (nodeElements, codeContainer) => { (function() { - const astContainer = document.querySelector('#ast code'); - const codeContainer = document.querySelector('#code code'); + const astContainer = document.querySelector('#ast code'); + const codeContainer = document.querySelector('#code code'); const sampleAst = `Joinpoint 'program' Joinpoint 'file' @@ -383,7 +385,7 @@ const linkCodeToAstNodes = (nodeElements, codeContainer) => { Joinpoint 'call' Joinpoint 'varref'`; // TODO: Import from Lara - if (astContainer != null) { + if (astContainer != null && codeContainer != null) { const nodeElements = createAstNodeElements(sampleAst); fillAstContainer(nodeElements, astContainer); linkCodeToAstNodes(nodeElements, codeContainer); diff --git a/Lara-JS/src-api/visualization/public/js/utils.js b/Lara-JS/src-api/visualization/public/js/utils.ts similarity index 57% rename from Lara-JS/src-api/visualization/public/js/utils.js rename to Lara-JS/src-api/visualization/public/js/utils.ts index b8147094e..f3d39b1e9 100644 --- a/Lara-JS/src-api/visualization/public/js/utils.js +++ b/Lara-JS/src-api/visualization/public/js/utils.ts @@ -1,5 +1,5 @@ -function escapeHtml(text) { - var specialCharMap = { +function escapeHtml(text: string): string { + var specialCharMap: { [char: string]: string } = { '&': '&', '<': '<', '>': '>', @@ -8,4 +8,6 @@ function escapeHtml(text) { }; return text.replace(/[&<>"']/g, (match) => specialCharMap[match]); -} \ No newline at end of file +} + +export { escapeHtml }; \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/visualization.js b/Lara-JS/src-api/visualization/public/js/visualization.ts similarity index 57% rename from Lara-JS/src-api/visualization/public/js/visualization.js rename to Lara-JS/src-api/visualization/public/js/visualization.ts index 3532437a1..dcad53d1f 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.js +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -1,18 +1,22 @@ -const getElementsWithNodeId = (id) => { +const getElementsWithNodeId = (id: number): NodeListOf => { return document.querySelectorAll(`span[data-node-id="${id}"]`); } -const highlightElements = (elements) => { +const highlightElements = (elements: NodeListOf): void => { elements.forEach(element => element.classList.add('highlighted')); }; -const unhighlightElements = (elements) => { +const unhighlightElements = (elements: NodeListOf): void => { elements.forEach(element => element.classList.remove('highlighted')); } -const addEventListenersToAstNodes = (nodes) => { +const addEventListenersToAstNodes = (nodes: NodeListOf): void => { for (const nodeElement of nodes) { - const nodeId = nodeElement.dataset.nodeId; + if (!nodeElement.dataset.nodeId) { + continue; + } + + const nodeId = parseInt(nodeElement.dataset.nodeId!); const nodeRelatedElements = getElementsWithNodeId(nodeId); for (const nodeRelatedElement of nodeRelatedElements) { @@ -23,6 +27,6 @@ const addEventListenersToAstNodes = (nodes) => { }; (function () { - const astNodes = document.querySelectorAll('.ast-node'); + const astNodes = document.querySelectorAll('.ast-node'); addEventListenersToAstNodes(astNodes); })(); diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index 95159e89e..a3efea486 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -1,37 +1,32 @@ +import { escapeHtml } from './utils.js'; const createAstNodeElements = (ast) => { - let id = 0; // TODO: Refactor identification (e.g. using astId) - const nodeElements = []; - - for (const node of ast.split('\n')) { - const matches = node.match(/(\s*)Joinpoint '(.+)'/, ''); - if (matches == null) { - console.warn(`Invalid node: "${node}"`); - continue; - } - const [, indentation, nodeName] = matches; - - const nodeElement = document.createElement('span'); - nodeElement.classList.add('ast-node'); // TODO: Add joinpoint info - nodeElement.dataset.nodeId = id++; - nodeElement.style.marginLeft = (indentation.length / 2) + "em"; - nodeElement.textContent = nodeName; - - nodeElements.push(nodeElement); - } - - return nodeElements; + let id = 0; // TODO: Refactor identification (e.g. using astId) + const nodeElements = []; + for (const node of ast.split('\n')) { + const matches = node.match(/(\s*)Joinpoint '(.+)'/, ''); + if (matches == null) { + console.warn(`Invalid node: "${node}"`); + continue; + } + const [, indentation, nodeName] = matches; + const nodeElement = document.createElement('span'); + nodeElement.classList.add('ast-node'); // TODO: Add joinpoint info + nodeElement.dataset.nodeId = (id++).toString(); + nodeElement.style.marginLeft = (indentation.length / 2) + "em"; + nodeElement.textContent = nodeName; + nodeElements.push(nodeElement); + } + return nodeElements; }; - const fillAstContainer = (nodeElements, astContainer) => { - for (const nodeElement of nodeElements) { - astContainer.appendChild(nodeElement); - astContainer.appendChild(document.createElement('br')); - } -} - + for (const nodeElement of nodeElements) { + astContainer.appendChild(nodeElement); + astContainer.appendChild(document.createElement('br')); + } +}; const linkCodeToAstNodes = (nodeElements, codeContainer) => { - for (const nodeElement of nodeElements) { - const nodeCode = `void matrix_mult(double const *A, double const *B, double *C, int const N, int const M, int const K) { + for (const nodeElement of nodeElements) { + const nodeCode = `void matrix_mult(double const *A, double const *B, double *C, int const N, int const M, int const K) { for(int ii = 0; ii < N; ii++) { for(int jj = 0; jj < K; jj++) { //C[i][j] = 0; @@ -46,31 +41,25 @@ const linkCodeToAstNodes = (nodeElements, codeContainer) => { } } } -}`; // TODO: Use real node code - const nodeCodeHtml = escapeHtml(nodeCode); - - const nodeCodeStart = codeContainer.innerHTML.indexOf(nodeCodeHtml); - if (nodeCodeStart === -1) { - console.warn(`Node code not found in code container: "${nodeCode}"`); - continue; - } - - const nodeCodeWrapper = document.createElement('span'); - nodeCodeWrapper.classList.add('node-code'); - nodeCodeWrapper.dataset.nodeId = nodeElement.dataset.nodeId; - nodeCodeWrapper.textContent = nodeCode; - - codeContainer.innerHTML = codeContainer.innerHTML.replaceAll(nodeCodeHtml, nodeCodeWrapper.outerHTML); - // TODO: Associate only the real match (this associates all code fragments that are identical to the node code) - } -} - - -(function() { - const astContainer = document.querySelector('#ast code'); - const codeContainer = document.querySelector('#code code'); - - const sampleAst = `Joinpoint 'program' +}`; // TODO: Use real node code + const nodeCodeHtml = escapeHtml(nodeCode); + const nodeCodeStart = codeContainer.innerHTML.indexOf(nodeCodeHtml); + if (nodeCodeStart === -1) { + console.warn(`Node code not found in code container: "${nodeCode}"`); + continue; + } + const nodeCodeWrapper = document.createElement('span'); + nodeCodeWrapper.classList.add('node-code'); + nodeCodeWrapper.dataset.nodeId = nodeElement.dataset.nodeId; + nodeCodeWrapper.textContent = nodeCode; + codeContainer.innerHTML = codeContainer.innerHTML.replaceAll(nodeCodeHtml, nodeCodeWrapper.outerHTML); + // TODO: Associate only the real match (this associates all code fragments that are identical to the node code) + } +}; +(function () { + const astContainer = document.querySelector('#ast code'); + const codeContainer = document.querySelector('#code code'); + const sampleAst = `Joinpoint 'program' Joinpoint 'file' Joinpoint 'include' Joinpoint 'include' @@ -381,11 +370,11 @@ const linkCodeToAstNodes = (nodeElements, codeContainer) => { Joinpoint 'intLiteral' Joinpoint 'exprStmt' Joinpoint 'call' - Joinpoint 'varref'`; // TODO: Import from Lara - - if (astContainer != null) { - const nodeElements = createAstNodeElements(sampleAst); - fillAstContainer(nodeElements, astContainer); - linkCodeToAstNodes(nodeElements, codeContainer); - } + Joinpoint 'varref'`; // TODO: Import from Lara + if (astContainer != null && codeContainer != null) { + const nodeElements = createAstNodeElements(sampleAst); + fillAstContainer(nodeElements, astContainer); + linkCodeToAstNodes(nodeElements, codeContainer); + } })(); +//# sourceMappingURL=ast-import.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/utils.js b/LaraApi/src-lara/visualization/public/js/utils.js index b8147094e..1c1ee1fb5 100644 --- a/LaraApi/src-lara/visualization/public/js/utils.js +++ b/LaraApi/src-lara/visualization/public/js/utils.js @@ -1,11 +1,12 @@ function escapeHtml(text) { - var specialCharMap = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' - }; - - return text.replace(/[&<>"']/g, (match) => specialCharMap[match]); -} \ No newline at end of file + var specialCharMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + return text.replace(/[&<>"']/g, (match) => specialCharMap[match]); +} +export { escapeHtml }; +//# sourceMappingURL=utils.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index 3532437a1..a5d68956c 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -1,28 +1,28 @@ const getElementsWithNodeId = (id) => { - return document.querySelectorAll(`span[data-node-id="${id}"]`); -} - + return document.querySelectorAll(`span[data-node-id="${id}"]`); +}; const highlightElements = (elements) => { - elements.forEach(element => element.classList.add('highlighted')); + elements.forEach(element => element.classList.add('highlighted')); }; - const unhighlightElements = (elements) => { - elements.forEach(element => element.classList.remove('highlighted')); -} - + elements.forEach(element => element.classList.remove('highlighted')); +}; const addEventListenersToAstNodes = (nodes) => { - for (const nodeElement of nodes) { - const nodeId = nodeElement.dataset.nodeId; - const nodeRelatedElements = getElementsWithNodeId(nodeId); - - for (const nodeRelatedElement of nodeRelatedElements) { - nodeRelatedElement.addEventListener('mouseover', () => highlightElements(nodeRelatedElements)); - nodeRelatedElement.addEventListener('mouseout', () => unhighlightElements(nodeRelatedElements)); + for (const nodeElement of nodes) { + if (!nodeElement.dataset.nodeId) { + continue; + } + const nodeId = parseInt(nodeElement.dataset.nodeId); + const nodeRelatedElements = getElementsWithNodeId(nodeId); + for (const nodeRelatedElement of nodeRelatedElements) { + nodeRelatedElement.addEventListener('mouseover', () => highlightElements(nodeRelatedElements)); + nodeRelatedElement.addEventListener('mouseout', () => unhighlightElements(nodeRelatedElements)); + } } - } }; - (function () { - const astNodes = document.querySelectorAll('.ast-node'); - addEventListenersToAstNodes(astNodes); + const astNodes = document.querySelectorAll('.ast-node'); + addEventListenersToAstNodes(astNodes); })(); +export {}; +//# sourceMappingURL=visualization.js.map \ No newline at end of file From e15052096062c497480e98af2ead6983a93d18de Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 4 Jul 2024 12:23:51 +0100 Subject: [PATCH 021/136] Add VisualizationTool method to create a WebSocket connection --- Lara-JS/src-api/visualization/VisualizationTool.ts | 8 ++++++-- LaraApi/src-lara/visualization/VisualizationTool.js | 7 +++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts index 648fef011..a64ce4eea 100644 --- a/Lara-JS/src-api/visualization/VisualizationTool.ts +++ b/Lara-JS/src-api/visualization/VisualizationTool.ts @@ -8,9 +8,9 @@ export default class VisualizationTool { host: string; port: number; - constructor(host: string, port: number) { + constructor(host: string, port: number | undefined) { this.host = host; - this.port = port; + this.port = port ?? 80; } getDomain(): string { @@ -47,6 +47,10 @@ export default class VisualizationTool { }); }); } + + createWebSocketClient(): WebSocket { + return new WebSocket(`ws://${this.host}:${this.port}`); + } } new VisualizationTool('localhost', 3000).launch(); \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js index 03ab01a35..3d15dfb39 100644 --- a/LaraApi/src-lara/visualization/VisualizationTool.js +++ b/LaraApi/src-lara/visualization/VisualizationTool.js @@ -2,13 +2,13 @@ import express from 'express'; import http from 'http'; import path from 'path'; import { fileURLToPath } from 'url'; -import { WebSocketServer } from 'ws'; +import WebSocket, { WebSocketServer } from 'ws'; export default class VisualizationTool { host; port; constructor(host, port) { this.host = host; - this.port = port; + this.port = port ?? 80; } getDomain() { return this.host; @@ -36,6 +36,9 @@ export default class VisualizationTool { }); }); } + createWebSocketClient() { + return new WebSocket(`ws://${this.host}:${this.port}`); + } } new VisualizationTool('localhost', 3000).launch(); //# sourceMappingURL=VisualizationTool.js.map \ No newline at end of file From e904b0fa4371b4ca96e682b80f42231126c85cc7 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 4 Jul 2024 14:20:33 +0100 Subject: [PATCH 022/136] Allow dynamic port allocation --- .../visualization/VisualizationTool.ts | 52 ++++++++++++++----- .../src-api/visualization/public/js/utils.ts | 2 +- .../visualization/VisualizationTool.js | 37 ++++++++++--- .../src-lara/visualization/public/js/utils.js | 4 +- 4 files changed, 73 insertions(+), 22 deletions(-) diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts index a64ce4eea..ca0f64cf7 100644 --- a/Lara-JS/src-api/visualization/VisualizationTool.ts +++ b/Lara-JS/src-api/visualization/VisualizationTool.ts @@ -3,25 +3,26 @@ import http from 'http'; import path from 'path'; import { fileURLToPath } from 'url'; import WebSocket, { WebSocketServer } from 'ws'; +import { AddressInfo } from 'net'; export default class VisualizationTool { - host: string; - port: number; + private host: string; + private port: number | undefined; - constructor(host: string, port: number | undefined) { - this.host = host; - this.port = port ?? 80; + constructor(host?: string, port?: number) { + this.host = host ?? '127.0.0.1'; + this.port = port; } - getDomain(): string { + public getHost(): string { return this.host; } - getPort(): number { + public getPort(): number | undefined { return this.port; } - launch(): void { + public async launch(): Promise { const app = express(); const server = http.createServer(app); const wss = new WebSocketServer({ server: server }); @@ -31,8 +32,22 @@ export default class VisualizationTool { app.use(express.static(path.join(dirname, 'public'))); - server.listen(this.port, () => { - console.log(`[server]: Server is running at http://${this.host}:${this.port}`); + wss.on('error', error => { + switch ((error as any).code) { + case 'EADDRINUSE': + console.error(`[server]: Port ${this.port} is already in use`); + break; + + case 'EACCES': + console.error(`[server]: Permission denied to use port ${this.port}`); + break; + + default: + console.error(`[server]: Unknown error occurred: ${error.message}`); + break; + }; + + server.close(); }); wss.on('connection', (ws: WebSocket) => { @@ -46,11 +61,24 @@ export default class VisualizationTool { console.log('[server]: Client disconnected'); }); }); + + return new Promise(res => { + server.listen(this.port, this.host, () => { + const addressInfo = server.address() as AddressInfo; + this.port = addressInfo.port; + + console.log(`[server]: Server is running at http://${this.host}:${this.port}`); + res(); + }); + }); } - createWebSocketClient(): WebSocket { + public createWebSocketClient(): WebSocket { return new WebSocket(`ws://${this.host}:${this.port}`); } } -new VisualizationTool('localhost', 3000).launch(); \ No newline at end of file +(() => { + const tool = new VisualizationTool(); + tool.launch(); +})(); \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/utils.ts b/Lara-JS/src-api/visualization/public/js/utils.ts index f3d39b1e9..be6d4172b 100644 --- a/Lara-JS/src-api/visualization/public/js/utils.ts +++ b/Lara-JS/src-api/visualization/public/js/utils.ts @@ -1,4 +1,4 @@ -function escapeHtml(text: string): string { +const escapeHtml = (text: string): string => { var specialCharMap: { [char: string]: string } = { '&': '&', '<': '<', diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js index 3d15dfb39..e6bcb7a4b 100644 --- a/LaraApi/src-lara/visualization/VisualizationTool.js +++ b/LaraApi/src-lara/visualization/VisualizationTool.js @@ -7,24 +7,36 @@ export default class VisualizationTool { host; port; constructor(host, port) { - this.host = host; - this.port = port ?? 80; + this.host = host ?? '127.0.0.1'; + this.port = port; } - getDomain() { + getHost() { return this.host; } getPort() { return this.port; } - launch() { + async launch() { const app = express(); const server = http.createServer(app); const wss = new WebSocketServer({ server: server }); const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); app.use(express.static(path.join(dirname, 'public'))); - server.listen(this.port, () => { - console.log(`[server]: Server is running at http://${this.host}:${this.port}`); + wss.on('error', error => { + switch (error.code) { + case 'EADDRINUSE': + console.error(`[server]: Port ${this.port} is already in use`); + break; + case 'EACCES': + console.error(`[server]: Permission denied to use port ${this.port}`); + break; + default: + console.error(`[server]: Unknown error occurred: ${error.message}`); + break; + } + ; + server.close(); }); wss.on('connection', (ws) => { console.log('[server]: Client connected'); @@ -35,10 +47,21 @@ export default class VisualizationTool { console.log('[server]: Client disconnected'); }); }); + return new Promise(res => { + server.listen(this.port, this.host, () => { + const addressInfo = server.address(); + this.port = addressInfo.port; + console.log(`[server]: Server is running at http://${this.host}:${this.port}`); + res(); + }); + }); } createWebSocketClient() { return new WebSocket(`ws://${this.host}:${this.port}`); } } -new VisualizationTool('localhost', 3000).launch(); +(() => { + const tool = new VisualizationTool(); + tool.launch(); +})(); //# sourceMappingURL=VisualizationTool.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/utils.js b/LaraApi/src-lara/visualization/public/js/utils.js index 1c1ee1fb5..854cf0233 100644 --- a/LaraApi/src-lara/visualization/public/js/utils.js +++ b/LaraApi/src-lara/visualization/public/js/utils.js @@ -1,4 +1,4 @@ -function escapeHtml(text) { +const escapeHtml = (text) => { var specialCharMap = { '&': '&', '<': '<', @@ -7,6 +7,6 @@ function escapeHtml(text) { "'": ''' }; return text.replace(/[&<>"']/g, (match) => specialCharMap[match]); -} +}; export { escapeHtml }; //# sourceMappingURL=utils.js.map \ No newline at end of file From edc592059b7b0621773782d67af75f02ac8d6a95 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 4 Jul 2024 14:28:44 +0100 Subject: [PATCH 023/136] Specify default port --- Lara-JS/src-api/visualization/VisualizationTool.ts | 6 +++--- Lara-JS/src-api/visualization/public/js/ast-import.ts | 2 +- Lara-JS/src-api/visualization/public/js/visualization.ts | 2 +- LaraApi/src-lara/visualization/VisualizationTool.js | 6 +++--- LaraApi/src-lara/visualization/public/js/ast-import.js | 2 +- LaraApi/src-lara/visualization/public/js/visualization.js | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts index ca0f64cf7..8714f4200 100644 --- a/Lara-JS/src-api/visualization/VisualizationTool.ts +++ b/Lara-JS/src-api/visualization/VisualizationTool.ts @@ -9,8 +9,8 @@ export default class VisualizationTool { private host: string; private port: number | undefined; - constructor(host?: string, port?: number) { - this.host = host ?? '127.0.0.1'; + constructor(host: string = '127.0.0.1', port?: number) { + this.host = host; this.port = port; } @@ -79,6 +79,6 @@ export default class VisualizationTool { } (() => { - const tool = new VisualizationTool(); + const tool = new VisualizationTool('127.0.0.1', 3000); tool.launch(); })(); \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index e3d414a0b..64b1c1de2 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -68,7 +68,7 @@ const linkCodeToAstNodes = (nodeElements: HTMLElement[], codeContainer: HTMLElem } -(function() { +(() => { const astContainer = document.querySelector('#ast code'); const codeContainer = document.querySelector('#code code'); diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index dcad53d1f..a13284f0b 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -26,7 +26,7 @@ const addEventListenersToAstNodes = (nodes: NodeListOf): void => { } }; -(function () { +(() => { const astNodes = document.querySelectorAll('.ast-node'); addEventListenersToAstNodes(astNodes); })(); diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js index e6bcb7a4b..61e66ff34 100644 --- a/LaraApi/src-lara/visualization/VisualizationTool.js +++ b/LaraApi/src-lara/visualization/VisualizationTool.js @@ -6,8 +6,8 @@ import WebSocket, { WebSocketServer } from 'ws'; export default class VisualizationTool { host; port; - constructor(host, port) { - this.host = host ?? '127.0.0.1'; + constructor(host = '127.0.0.1', port) { + this.host = host; this.port = port; } getHost() { @@ -61,7 +61,7 @@ export default class VisualizationTool { } } (() => { - const tool = new VisualizationTool(); + const tool = new VisualizationTool('127.0.0.1', 3000); tool.launch(); })(); //# sourceMappingURL=VisualizationTool.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index a3efea486..e04bdad28 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -56,7 +56,7 @@ const linkCodeToAstNodes = (nodeElements, codeContainer) => { // TODO: Associate only the real match (this associates all code fragments that are identical to the node code) } }; -(function () { +(() => { const astContainer = document.querySelector('#ast code'); const codeContainer = document.querySelector('#code code'); const sampleAst = `Joinpoint 'program' diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index a5d68956c..7b6c141f5 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -20,7 +20,7 @@ const addEventListenersToAstNodes = (nodes) => { } } }; -(function () { +(() => { const astNodes = document.querySelectorAll('.ast-node'); addEventListenersToAstNodes(astNodes); })(); From e905584f52c52218d0cf6d5506ec354a4b40ebad Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 4 Jul 2024 16:35:25 +0100 Subject: [PATCH 024/136] Launch visualization tool on startup --- Lara-JS/src-api/visualization/VisualizationTool.ts | 11 ++++------- Lara-JS/src-code/WeaverLauncher.ts | 2 ++ LaraApi/src-lara/visualization/VisualizationTool.js | 8 +++----- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts index 8714f4200..d493861df 100644 --- a/Lara-JS/src-api/visualization/VisualizationTool.ts +++ b/Lara-JS/src-api/visualization/VisualizationTool.ts @@ -67,7 +67,9 @@ export default class VisualizationTool { const addressInfo = server.address() as AddressInfo; this.port = addressInfo.port; - console.log(`[server]: Server is running at http://${this.host}:${this.port}`); + console.log(`\nVisualization tool is running at http://${this.host}:${this.port}\n`); + // child.exec(`xdg-open http://${this.host}:${this.port}`); + // TODO: See if opening automatically is a good idea res(); }); }); @@ -76,9 +78,4 @@ export default class VisualizationTool { public createWebSocketClient(): WebSocket { return new WebSocket(`ws://${this.host}:${this.port}`); } -} - -(() => { - const tool = new VisualizationTool('127.0.0.1', 3000); - tool.launch(); -})(); \ No newline at end of file +} \ No newline at end of file diff --git a/Lara-JS/src-code/WeaverLauncher.ts b/Lara-JS/src-code/WeaverLauncher.ts index 95627f837..279646877 100644 --- a/Lara-JS/src-code/WeaverLauncher.ts +++ b/Lara-JS/src-code/WeaverLauncher.ts @@ -13,6 +13,7 @@ import { } from "./ChildProcessHandling.js"; import WeaverConfiguration from "./WeaverConfiguration.js"; import WeaverMessageFromLauncher from "./WeaverMessageFromLauncher.js"; +import VisualizationTool from "../api/visualization/VisualizationTool.js"; listenForTerminationSignals(); @@ -38,6 +39,7 @@ export default class WeaverLauncher { protected main(args: Arguments): void { this.debug(`${this.config.weaverPrettyName} execution arguments: %O`, args); + new VisualizationTool().launch(); void this.executeWeaver(args); if (args.watch) { diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js index 61e66ff34..ec0cc48ca 100644 --- a/LaraApi/src-lara/visualization/VisualizationTool.js +++ b/LaraApi/src-lara/visualization/VisualizationTool.js @@ -51,7 +51,9 @@ export default class VisualizationTool { server.listen(this.port, this.host, () => { const addressInfo = server.address(); this.port = addressInfo.port; - console.log(`[server]: Server is running at http://${this.host}:${this.port}`); + console.log(`\nVisualization tool is running at http://${this.host}:${this.port}\n`); + // child.exec(`xdg-open http://${this.host}:${this.port}`); + // TODO: See if opening automatically is a good idea res(); }); }); @@ -60,8 +62,4 @@ export default class VisualizationTool { return new WebSocket(`ws://${this.host}:${this.port}`); } } -(() => { - const tool = new VisualizationTool('127.0.0.1', 3000); - tool.launch(); -})(); //# sourceMappingURL=VisualizationTool.js.map \ No newline at end of file From 1e4216f52aab4b8fec46f36bcebaefb40f5f583e Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 4 Jul 2024 18:28:35 +0100 Subject: [PATCH 025/136] Convert VisualizationTool to a singleton --- .../visualization/VisualizationTool.ts | 29 ++++++++++++------- Lara-JS/src-code/WeaverLauncher.ts | 2 -- .../up/fe/specs/lara/LaraApiJsResource.java | 1 + .../visualization/VisualizationTool.js | 29 +++++++++++-------- .../visualization/VisualizationToolUpdate.js | 9 ++++++ 5 files changed, 45 insertions(+), 25 deletions(-) create mode 100644 LaraApi/src-lara/visualization/VisualizationToolUpdate.js diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts index d493861df..b2bb0225a 100644 --- a/Lara-JS/src-api/visualization/VisualizationTool.ts +++ b/Lara-JS/src-api/visualization/VisualizationTool.ts @@ -3,26 +3,32 @@ import http from 'http'; import path from 'path'; import { fileURLToPath } from 'url'; import WebSocket, { WebSocketServer } from 'ws'; +import Query from '../weaver/Query.js'; import { AddressInfo } from 'net'; export default class VisualizationTool { - private host: string; - private port: number | undefined; + private static host: string | undefined; + private static port: number | undefined; - constructor(host: string = '127.0.0.1', port?: number) { - this.host = host; - this.port = port; + public static isLaunched(): boolean { + return this.port !== undefined; } - public getHost(): string { + public static getHost(): string | undefined { return this.host; } - public getPort(): number | undefined { + public static getPort(): number | undefined { return this.port; } - public async launch(): Promise { + + public static async launch(host: string = '127.0.0.1', port?: number): Promise { + if (this.isLaunched()) { + console.warn('[server]: Visualization tool is already running at http://${this.host}:${this.port}'); + return; + } + const app = express(); const server = http.createServer(app); const wss = new WebSocketServer({ server: server }); @@ -35,11 +41,11 @@ export default class VisualizationTool { wss.on('error', error => { switch ((error as any).code) { case 'EADDRINUSE': - console.error(`[server]: Port ${this.port} is already in use`); + console.error(`[server]: Port ${port} is already in use`); break; case 'EACCES': - console.error(`[server]: Permission denied to use port ${this.port}`); + console.error(`[server]: Permission denied to use port ${port}`); break; default: @@ -63,8 +69,9 @@ export default class VisualizationTool { }); return new Promise(res => { - server.listen(this.port, this.host, () => { + server.listen(port ?? 0, host, () => { const addressInfo = server.address() as AddressInfo; + this.host = addressInfo.address; this.port = addressInfo.port; console.log(`\nVisualization tool is running at http://${this.host}:${this.port}\n`); diff --git a/Lara-JS/src-code/WeaverLauncher.ts b/Lara-JS/src-code/WeaverLauncher.ts index 279646877..95627f837 100644 --- a/Lara-JS/src-code/WeaverLauncher.ts +++ b/Lara-JS/src-code/WeaverLauncher.ts @@ -13,7 +13,6 @@ import { } from "./ChildProcessHandling.js"; import WeaverConfiguration from "./WeaverConfiguration.js"; import WeaverMessageFromLauncher from "./WeaverMessageFromLauncher.js"; -import VisualizationTool from "../api/visualization/VisualizationTool.js"; listenForTerminationSignals(); @@ -39,7 +38,6 @@ export default class WeaverLauncher { protected main(args: Arguments): void { this.debug(`${this.config.weaverPrettyName} execution arguments: %O`, args); - new VisualizationTool().launch(); void this.executeWeaver(args); if (args.watch) { diff --git a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java index be1cb94d4..ce5f2a48a 100644 --- a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java +++ b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java @@ -106,6 +106,7 @@ public enum LaraApiJsResource implements LaraResourceProvider { TUPLEID_JS("lara/util/TupleId.js"), CYTOSCAPE_3_26_0_JS("libs/cytoscape-3.26.0.js"), VISUALIZATIONTOOL_JS("visualization/VisualizationTool.js"), + VISUALIZATIONTOOLUPDATE_JS("visualization/VisualizationToolUpdate.js"), AST_IMPORT_JS("visualization/public/js/ast-import.js"), UTILS_JS("visualization/public/js/utils.js"), VISUALIZATION_JS("visualization/public/js/visualization.js"), diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js index ec0cc48ca..6e6654abd 100644 --- a/LaraApi/src-lara/visualization/VisualizationTool.js +++ b/LaraApi/src-lara/visualization/VisualizationTool.js @@ -2,21 +2,25 @@ import express from 'express'; import http from 'http'; import path from 'path'; import { fileURLToPath } from 'url'; -import WebSocket, { WebSocketServer } from 'ws'; +import { WebSocketServer } from 'ws'; +import Query from '../weaver/Query.js'; export default class VisualizationTool { - host; - port; - constructor(host = '127.0.0.1', port) { - this.host = host; - this.port = port; + static host; + static port; + static isLaunched() { + return this.port !== undefined; } - getHost() { + static getHost() { return this.host; } - getPort() { + static getPort() { return this.port; } - async launch() { + static async launch(host = '127.0.0.1', port) { + if (this.isLaunched()) { + console.warn('[server]: Visualization tool is already running at http://${this.host}:${this.port}'); + return; + } const app = express(); const server = http.createServer(app); const wss = new WebSocketServer({ server: server }); @@ -26,10 +30,10 @@ export default class VisualizationTool { wss.on('error', error => { switch (error.code) { case 'EADDRINUSE': - console.error(`[server]: Port ${this.port} is already in use`); + console.error(`[server]: Port ${port} is already in use`); break; case 'EACCES': - console.error(`[server]: Permission denied to use port ${this.port}`); + console.error(`[server]: Permission denied to use port ${port}`); break; default: console.error(`[server]: Unknown error occurred: ${error.message}`); @@ -48,8 +52,9 @@ export default class VisualizationTool { }); }); return new Promise(res => { - server.listen(this.port, this.host, () => { + server.listen(port ?? 0, host, () => { const addressInfo = server.address(); + this.host = addressInfo.address; this.port = addressInfo.port; console.log(`\nVisualization tool is running at http://${this.host}:${this.port}\n`); // child.exec(`xdg-open http://${this.host}:${this.port}`); diff --git a/LaraApi/src-lara/visualization/VisualizationToolUpdate.js b/LaraApi/src-lara/visualization/VisualizationToolUpdate.js new file mode 100644 index 000000000..18ef9be4f --- /dev/null +++ b/LaraApi/src-lara/visualization/VisualizationToolUpdate.js @@ -0,0 +1,9 @@ +import JoinPoints from "../weaver/JoinPoints.js"; +export default class VisualizationToolUpdate { + static updateVisualizationTool() { + const socket = new WebSocket("ws://127.0.0.1:3000"); + console.log(JSON.stringify(JoinPoints.root().dump)); + } +} +; +//# sourceMappingURL=VisualizationToolUpdate.js.map \ No newline at end of file From 3bbf29dd9b91c8f82bc30396fa3745633bb1c886 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 4 Jul 2024 18:48:08 +0100 Subject: [PATCH 026/136] Create function to wait for tool --- Lara-JS/src-api/visualization/VisualizationTool.ts | 8 ++++++-- LaraApi/src-lara/visualization/VisualizationTool.js | 9 ++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts index b2bb0225a..70b5f093d 100644 --- a/Lara-JS/src-api/visualization/VisualizationTool.ts +++ b/Lara-JS/src-api/visualization/VisualizationTool.ts @@ -82,7 +82,11 @@ export default class VisualizationTool { }); } - public createWebSocketClient(): WebSocket { - return new WebSocket(`ws://${this.host}:${this.port}`); + public static async waitForTool(): Promise { + if (!this.isLaunched()) { + console.warn('Visualization tool is not running'); // TODO: Convert to error + return; + } + await new Promise(() => {}); // TODO: Effectively wait for web page to respond } } \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js index 6e6654abd..3fcb0f2de 100644 --- a/LaraApi/src-lara/visualization/VisualizationTool.js +++ b/LaraApi/src-lara/visualization/VisualizationTool.js @@ -3,7 +3,6 @@ import http from 'http'; import path from 'path'; import { fileURLToPath } from 'url'; import { WebSocketServer } from 'ws'; -import Query from '../weaver/Query.js'; export default class VisualizationTool { static host; static port; @@ -63,8 +62,12 @@ export default class VisualizationTool { }); }); } - createWebSocketClient() { - return new WebSocket(`ws://${this.host}:${this.port}`); + static async waitForTool() { + if (!this.isLaunched()) { + console.warn('Visualization tool is not running'); // TODO: Convert to error + return; + } + await new Promise(() => { }); } } //# sourceMappingURL=VisualizationTool.js.map \ No newline at end of file From a1db69a12717fee99f72af806729b66b522dffb8 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 4 Jul 2024 22:05:14 +0100 Subject: [PATCH 027/136] Create continue button --- Lara-JS/src-api/visualization/public/css/styles.css | 4 ++++ Lara-JS/src-api/visualization/public/index.html | 1 + LaraApi/src-lara/visualization/VisualizationTool.js | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index c02dbd761..0ad19dacf 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -26,4 +26,8 @@ pre * { .ast-node::before { content: ' - '; +} + +#continue-button { + display: block; } \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 3d7def0ce..bcec6a0f5 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -93,6 +93,7 @@

Code

test_matrix_mul(); }
+ \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js index 3fcb0f2de..6ec786cf5 100644 --- a/LaraApi/src-lara/visualization/VisualizationTool.js +++ b/LaraApi/src-lara/visualization/VisualizationTool.js @@ -67,7 +67,7 @@ export default class VisualizationTool { console.warn('Visualization tool is not running'); // TODO: Convert to error return; } - await new Promise(() => { }); + await new Promise(() => { }); // TODO: Effectively wait for web page to respond } } //# sourceMappingURL=VisualizationTool.js.map \ No newline at end of file From dee8140c8572b8fcc7f988b70ea45be9d84411c5 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 5 Jul 2024 10:28:33 +0100 Subject: [PATCH 028/136] Establish communication between tool server and page --- .../visualization/VisualizationTool.ts | 46 ++++++++++++++++--- .../src-api/visualization/public/index.html | 1 + .../visualization/public/js/communication.ts | 30 ++++++++++++ .../up/fe/specs/lara/LaraApiJsResource.java | 1 + .../visualization/VisualizationTool.js | 36 +++++++++++++-- .../visualization/public/js/communication.js | 30 ++++++++++++ 6 files changed, 132 insertions(+), 12 deletions(-) create mode 100644 Lara-JS/src-api/visualization/public/js/communication.ts create mode 100644 LaraApi/src-lara/visualization/public/js/communication.js diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts index 70b5f093d..245ea7ffe 100644 --- a/Lara-JS/src-api/visualization/VisualizationTool.ts +++ b/Lara-JS/src-api/visualization/VisualizationTool.ts @@ -2,13 +2,14 @@ import express from 'express'; import http from 'http'; import path from 'path'; import { fileURLToPath } from 'url'; -import WebSocket, { WebSocketServer } from 'ws'; +import WebSocket, { WebSocketServer, MessageEvent } from 'ws'; import Query from '../weaver/Query.js'; import { AddressInfo } from 'net'; export default class VisualizationTool { private static host: string | undefined; private static port: number | undefined; + private static wss: WebSocketServer | undefined; public static isLaunched(): boolean { return this.port !== undefined; @@ -22,7 +23,6 @@ export default class VisualizationTool { return this.port; } - public static async launch(host: string = '127.0.0.1', port?: number): Promise { if (this.isLaunched()) { console.warn('[server]: Visualization tool is already running at http://${this.host}:${this.port}'); @@ -31,14 +31,14 @@ export default class VisualizationTool { const app = express(); const server = http.createServer(app); - const wss = new WebSocketServer({ server: server }); + this.wss = new WebSocketServer({ server: server }); const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); app.use(express.static(path.join(dirname, 'public'))); - wss.on('error', error => { + this.wss.on('error', error => { switch ((error as any).code) { case 'EADDRINUSE': console.error(`[server]: Port ${port} is already in use`); @@ -56,14 +56,14 @@ export default class VisualizationTool { server.close(); }); - wss.on('connection', (ws: WebSocket) => { + this.wss.on('connection', (ws: WebSocket) => { console.log('[server]: Client connected'); ws.on('message', (message: string) => { console.log(`[server]: Received message => ${message}`); }); - ws.on('close', () => { + ws.addEventListener('close', () => { console.log('[server]: Client disconnected'); }); }); @@ -82,11 +82,43 @@ export default class VisualizationTool { }); } + private static sendToClient(ws: WebSocket, data: any): void { + ws.send(JSON.stringify(data)); + } + + private static sendToAllClients(data: any): void { + this.wss!.clients.forEach(ws => this.sendToClient(ws, data)); + } + public static async waitForTool(): Promise { if (!this.isLaunched()) { console.warn('Visualization tool is not running'); // TODO: Convert to error return; } - await new Promise(() => {}); // TODO: Effectively wait for web page to respond + + return new Promise(res => { + let placeClientOnWait: (ws: WebSocket) => void; + + const waitOnMessage = (message: string) => { + const data = JSON.parse(message); + if (data.message === 'continue') { + this.wss!.clients.forEach(ws => { + this.wss!.off('connection', placeClientOnWait); + ws.off('message', waitOnMessage); + }); + + this.sendToAllClients({ message: 'continue' }); + res(); + } + } + + placeClientOnWait = (ws: WebSocket) => { + ws.on('message', waitOnMessage); + this.sendToClient(ws, { message: 'wait' }); + } + + this.wss!.clients.forEach(placeClientOnWait); + this.wss!.on('connection', placeClientOnWait); + }); // TODO: Effectively wait for web page to respond } } \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index bcec6a0f5..a510de3ca 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -8,6 +8,7 @@ + AST Visualization diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts new file mode 100644 index 000000000..b11867fe9 --- /dev/null +++ b/Lara-JS/src-api/visualization/public/js/communication.ts @@ -0,0 +1,30 @@ +const getWebSocket = (): WebSocket => { + const url = `ẁs://${window.location.host}`; + return new WebSocket(url); +}; + +(() => { + const ws = getWebSocket(); + const continueButton = document.querySelector('#continue-button'); + if (!continueButton) + return; + + continueButton.addEventListener('click', () => { + continueButton.disabled = true; + ws.send(JSON.stringify({ message: 'continue' })); + }); + + ws.addEventListener('message', (message: MessageEvent) => { + const data = JSON.parse(message.data); + + switch (data.message) { + case 'continue': + continueButton.disabled = true; + break; + + case 'wait': + continueButton.disabled = false; + break; + } + }); +})(); diff --git a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java index ce5f2a48a..44d16ae23 100644 --- a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java +++ b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java @@ -108,6 +108,7 @@ public enum LaraApiJsResource implements LaraResourceProvider { VISUALIZATIONTOOL_JS("visualization/VisualizationTool.js"), VISUALIZATIONTOOLUPDATE_JS("visualization/VisualizationToolUpdate.js"), AST_IMPORT_JS("visualization/public/js/ast-import.js"), + COMMUNICATION_JS("visualization/public/js/communication.js"), UTILS_JS("visualization/public/js/utils.js"), VISUALIZATION_JS("visualization/public/js/visualization.js"), AST_JS("weaver/Ast.js"), diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js index 6ec786cf5..8ebd4ae23 100644 --- a/LaraApi/src-lara/visualization/VisualizationTool.js +++ b/LaraApi/src-lara/visualization/VisualizationTool.js @@ -6,6 +6,7 @@ import { WebSocketServer } from 'ws'; export default class VisualizationTool { static host; static port; + static wss; static isLaunched() { return this.port !== undefined; } @@ -22,11 +23,11 @@ export default class VisualizationTool { } const app = express(); const server = http.createServer(app); - const wss = new WebSocketServer({ server: server }); + this.wss = new WebSocketServer({ server: server }); const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); app.use(express.static(path.join(dirname, 'public'))); - wss.on('error', error => { + this.wss.on('error', error => { switch (error.code) { case 'EADDRINUSE': console.error(`[server]: Port ${port} is already in use`); @@ -41,12 +42,12 @@ export default class VisualizationTool { ; server.close(); }); - wss.on('connection', (ws) => { + this.wss.on('connection', (ws) => { console.log('[server]: Client connected'); ws.on('message', (message) => { console.log(`[server]: Received message => ${message}`); }); - ws.on('close', () => { + ws.addEventListener('close', () => { console.log('[server]: Client disconnected'); }); }); @@ -62,12 +63,37 @@ export default class VisualizationTool { }); }); } + static sendToClient(ws, data) { + ws.send(JSON.stringify(data)); + } + static sendToAllClients(data) { + this.wss.clients.forEach(ws => this.sendToClient(ws, data)); + } static async waitForTool() { if (!this.isLaunched()) { console.warn('Visualization tool is not running'); // TODO: Convert to error return; } - await new Promise(() => { }); // TODO: Effectively wait for web page to respond + return new Promise(res => { + let placeClientOnWait; + const waitOnMessage = (message) => { + const data = JSON.parse(message); + if (data.message === 'continue') { + this.wss.clients.forEach(ws => { + this.wss.off('connection', placeClientOnWait); + ws.off('message', waitOnMessage); + }); + this.sendToAllClients({ message: 'continue' }); + res(); + } + }; + placeClientOnWait = (ws) => { + ws.on('message', waitOnMessage); + this.sendToClient(ws, { message: 'wait' }); + }; + this.wss.clients.forEach(placeClientOnWait); + this.wss.on('connection', placeClientOnWait); + }); // TODO: Effectively wait for web page to respond } } //# sourceMappingURL=VisualizationTool.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js new file mode 100644 index 000000000..3e7e60cbe --- /dev/null +++ b/LaraApi/src-lara/visualization/public/js/communication.js @@ -0,0 +1,30 @@ +const getWebSocket = () => { + const url = `ẁs://${window.location.host}`; + return new WebSocket(url); +}; +(() => { + const ws = getWebSocket(); + ws.addEventListener('message', (message) => { + console.log(`[client]: Received message => ${message.data}`); + }); + const continueButton = document.querySelector('#continue-button'); + if (!continueButton) + return; + continueButton.addEventListener('click', () => { + continueButton.disabled = true; + ws.send(JSON.stringify({ message: 'continue' })); + }); + ws.addEventListener('message', (message) => { + const data = JSON.parse(message.data); + switch (data.message) { + case 'continue': + continueButton.disabled = true; + break; + case 'wait': + continueButton.disabled = false; + break; + } + }); +})(); +export {}; +//# sourceMappingURL=communication.js.map \ No newline at end of file From d7900eda01cead721b6c3dba0304b3420ca4a421 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 5 Jul 2024 10:49:19 +0100 Subject: [PATCH 029/136] Small refactor on VisualizationTool.ts --- .../visualization/VisualizationTool.ts | 62 +++++++++++-------- .../visualization/VisualizationTool.js | 54 +++++++++------- .../visualization/public/js/communication.js | 3 - 3 files changed, 65 insertions(+), 54 deletions(-) diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts index 245ea7ffe..f242598cc 100644 --- a/Lara-JS/src-api/visualization/VisualizationTool.ts +++ b/Lara-JS/src-api/visualization/VisualizationTool.ts @@ -7,25 +7,44 @@ import Query from '../weaver/Query.js'; import { AddressInfo } from 'net'; export default class VisualizationTool { - private static host: string | undefined; + private static hostname: string | undefined; private static port: number | undefined; private static wss: WebSocketServer | undefined; + private static serverClosed: boolean = false; public static isLaunched(): boolean { - return this.port !== undefined; + return this.wss !== undefined && this.serverClosed === false; } - public static getHost(): string | undefined { - return this.host; + public static getHostname(): string | undefined { + return this.hostname; } public static getPort(): number | undefined { return this.port; } - public static async launch(host: string = '127.0.0.1', port?: number): Promise { + private static onWssError(error: NodeJS.ErrnoException): void { + switch (error.code) { + case 'EADDRINUSE': + console.error(`[server]: Port ${this.port} is already in use`); + break; + + case 'EACCES': + console.error(`[server]: Permission denied to use port ${this.port}`); + break; + + default: + console.error(`[server]: Unknown error occurred: ${error.message}`); + break; + }; + + this.wss!.close(); + } + + public static async launch(hostname: string = '127.0.0.1', port?: number): Promise { if (this.isLaunched()) { - console.warn('[server]: Visualization tool is already running at http://${this.host}:${this.port}'); + console.warn(`Visualization tool is already running at http://${this.hostname}:${this.port}`); return; } @@ -38,24 +57,6 @@ export default class VisualizationTool { app.use(express.static(path.join(dirname, 'public'))); - this.wss.on('error', error => { - switch ((error as any).code) { - case 'EADDRINUSE': - console.error(`[server]: Port ${port} is already in use`); - break; - - case 'EACCES': - console.error(`[server]: Permission denied to use port ${port}`); - break; - - default: - console.error(`[server]: Unknown error occurred: ${error.message}`); - break; - }; - - server.close(); - }); - this.wss.on('connection', (ws: WebSocket) => { console.log('[server]: Client connected'); @@ -66,15 +67,22 @@ export default class VisualizationTool { ws.addEventListener('close', () => { console.log('[server]: Client disconnected'); }); + }); // TODO: Remove this + + this.wss.on('close', () => { + this.serverClosed = true; }); + this.wss.on('error', this.onWssError); + return new Promise(res => { - server.listen(port ?? 0, host, () => { + server.listen(port ?? 0, hostname, () => { const addressInfo = server.address() as AddressInfo; - this.host = addressInfo.address; + this.hostname = addressInfo.address; this.port = addressInfo.port; + this.serverClosed = false; - console.log(`\nVisualization tool is running at http://${this.host}:${this.port}\n`); + console.log(`\nVisualization tool is running at http://${this.hostname}:${this.port}\n`); // child.exec(`xdg-open http://${this.host}:${this.port}`); // TODO: See if opening automatically is a good idea res(); diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js index 8ebd4ae23..bf3142c93 100644 --- a/LaraApi/src-lara/visualization/VisualizationTool.js +++ b/LaraApi/src-lara/visualization/VisualizationTool.js @@ -4,21 +4,37 @@ import path from 'path'; import { fileURLToPath } from 'url'; import { WebSocketServer } from 'ws'; export default class VisualizationTool { - static host; + static hostname; static port; static wss; + static serverClosed = false; static isLaunched() { - return this.port !== undefined; + return this.wss !== undefined && this.serverClosed === false; } - static getHost() { - return this.host; + static getHostname() { + return this.hostname; } static getPort() { return this.port; } - static async launch(host = '127.0.0.1', port) { + static onWssError(error) { + switch (error.code) { + case 'EADDRINUSE': + console.error(`[server]: Port ${this.port} is already in use`); + break; + case 'EACCES': + console.error(`[server]: Permission denied to use port ${this.port}`); + break; + default: + console.error(`[server]: Unknown error occurred: ${error.message}`); + break; + } + ; + this.wss.close(); + } + static async launch(hostname = '127.0.0.1', port) { if (this.isLaunched()) { - console.warn('[server]: Visualization tool is already running at http://${this.host}:${this.port}'); + console.warn(`Visualization tool is already running at http://${this.hostname}:${this.port}`); return; } const app = express(); @@ -27,21 +43,6 @@ export default class VisualizationTool { const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); app.use(express.static(path.join(dirname, 'public'))); - this.wss.on('error', error => { - switch (error.code) { - case 'EADDRINUSE': - console.error(`[server]: Port ${port} is already in use`); - break; - case 'EACCES': - console.error(`[server]: Permission denied to use port ${port}`); - break; - default: - console.error(`[server]: Unknown error occurred: ${error.message}`); - break; - } - ; - server.close(); - }); this.wss.on('connection', (ws) => { console.log('[server]: Client connected'); ws.on('message', (message) => { @@ -50,13 +51,18 @@ export default class VisualizationTool { ws.addEventListener('close', () => { console.log('[server]: Client disconnected'); }); + }); // TODO: Remove this + this.wss.on('close', () => { + this.serverClosed = true; }); + this.wss.on('error', this.onWssError); return new Promise(res => { - server.listen(port ?? 0, host, () => { + server.listen(port ?? 0, hostname, () => { const addressInfo = server.address(); - this.host = addressInfo.address; + this.hostname = addressInfo.address; this.port = addressInfo.port; - console.log(`\nVisualization tool is running at http://${this.host}:${this.port}\n`); + this.serverClosed = false; + console.log(`\nVisualization tool is running at http://${this.hostname}:${this.port}\n`); // child.exec(`xdg-open http://${this.host}:${this.port}`); // TODO: See if opening automatically is a good idea res(); diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js index 3e7e60cbe..8575475b0 100644 --- a/LaraApi/src-lara/visualization/public/js/communication.js +++ b/LaraApi/src-lara/visualization/public/js/communication.js @@ -4,9 +4,6 @@ const getWebSocket = () => { }; (() => { const ws = getWebSocket(); - ws.addEventListener('message', (message) => { - console.log(`[client]: Received message => ${message.data}`); - }); const continueButton = document.querySelector('#continue-button'); if (!continueButton) return; From 027e14a132c33be83a130fdf8ba23b247ba16e4f Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 5 Jul 2024 10:53:20 +0100 Subject: [PATCH 030/136] Convert tool not running warning into an error --- Lara-JS/src-api/visualization/VisualizationTool.ts | 3 +-- LaraApi/src-lara/visualization/VisualizationTool.js | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts index f242598cc..6fa74a040 100644 --- a/Lara-JS/src-api/visualization/VisualizationTool.ts +++ b/Lara-JS/src-api/visualization/VisualizationTool.ts @@ -100,8 +100,7 @@ export default class VisualizationTool { public static async waitForTool(): Promise { if (!this.isLaunched()) { - console.warn('Visualization tool is not running'); // TODO: Convert to error - return; + throw Error('Visualization tool is not running'); } return new Promise(res => { diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js index bf3142c93..940d87da5 100644 --- a/LaraApi/src-lara/visualization/VisualizationTool.js +++ b/LaraApi/src-lara/visualization/VisualizationTool.js @@ -77,8 +77,7 @@ export default class VisualizationTool { } static async waitForTool() { if (!this.isLaunched()) { - console.warn('Visualization tool is not running'); // TODO: Convert to error - return; + throw Error('Visualization tool is not running'); } return new Promise(res => { let placeClientOnWait; From ea791bbf275702926fd090ea070c3eb3826bc9fb Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 5 Jul 2024 10:54:53 +0100 Subject: [PATCH 031/136] Remove outdated TODO --- Lara-JS/src-api/visualization/VisualizationTool.ts | 2 +- LaraApi/src-lara/visualization/VisualizationTool.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts index 6fa74a040..593465307 100644 --- a/Lara-JS/src-api/visualization/VisualizationTool.ts +++ b/Lara-JS/src-api/visualization/VisualizationTool.ts @@ -126,6 +126,6 @@ export default class VisualizationTool { this.wss!.clients.forEach(placeClientOnWait); this.wss!.on('connection', placeClientOnWait); - }); // TODO: Effectively wait for web page to respond + }); } } \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js index 940d87da5..fb2b151dc 100644 --- a/LaraApi/src-lara/visualization/VisualizationTool.js +++ b/LaraApi/src-lara/visualization/VisualizationTool.js @@ -98,7 +98,7 @@ export default class VisualizationTool { }; this.wss.clients.forEach(placeClientOnWait); this.wss.on('connection', placeClientOnWait); - }); // TODO: Effectively wait for web page to respond + }); } } //# sourceMappingURL=VisualizationTool.js.map \ No newline at end of file From df1f96df6420a8b816dc8633850473adb8138cd9 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 5 Jul 2024 11:39:25 +0100 Subject: [PATCH 032/136] Create function to update tool AST --- .../visualization/VisualizationTool.ts | 21 +- .../visualization/public/js/ast-import.ts | 329 +----------------- .../visualization/public/js/communication.ts | 17 +- .../visualization/VisualizationTool.js | 16 +- .../visualization/public/js/ast-import.js | 327 +---------------- .../visualization/public/js/communication.js | 13 +- 6 files changed, 66 insertions(+), 657 deletions(-) diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts index 593465307..7ee96be5c 100644 --- a/Lara-JS/src-api/visualization/VisualizationTool.ts +++ b/Lara-JS/src-api/visualization/VisualizationTool.ts @@ -3,7 +3,7 @@ import http from 'http'; import path from 'path'; import { fileURLToPath } from 'url'; import WebSocket, { WebSocketServer, MessageEvent } from 'ws'; -import Query from '../weaver/Query.js'; +import JoinPoints from '../weaver/JoinPoints.js'; import { AddressInfo } from 'net'; export default class VisualizationTool { @@ -68,12 +68,14 @@ export default class VisualizationTool { console.log('[server]: Client disconnected'); }); }); // TODO: Remove this + + this.wss.on('connection', ws => this.updateClient(ws)); this.wss.on('close', () => { this.serverClosed = true; }); - this.wss.on('error', this.onWssError); + this.wss.on('error', error => this.onWssError(error)); return new Promise(res => { server.listen(port ?? 0, hostname, () => { @@ -98,10 +100,14 @@ export default class VisualizationTool { this.wss!.clients.forEach(ws => this.sendToClient(ws, data)); } - public static async waitForTool(): Promise { + private static verifyToolIsRunning(): void { if (!this.isLaunched()) { throw Error('Visualization tool is not running'); } + } + + public static async waitForTool(): Promise { + this.verifyToolIsRunning(); return new Promise(res => { let placeClientOnWait: (ws: WebSocket) => void; @@ -128,4 +134,13 @@ export default class VisualizationTool { this.wss!.on('connection', placeClientOnWait); }); } + + private static updateClient(ws: WebSocket): void { + this.sendToClient(ws, { message: 'update', ast: JoinPoints.root().dump.trim() }); + } + + public static update(): void { + this.verifyToolIsRunning(); + this.wss!.clients.forEach(this.updateClient); + } } \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index 64b1c1de2..81c71db9f 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -67,327 +67,10 @@ const linkCodeToAstNodes = (nodeElements: HTMLElement[], codeContainer: HTMLElem } } +const importAst = (ast: any, astContainer: HTMLElement, codeContainer: HTMLElement): void => { + const nodeElements = createAstNodeElements(ast); + fillAstContainer(nodeElements, astContainer); + linkCodeToAstNodes(nodeElements, codeContainer); +} -(() => { - const astContainer = document.querySelector('#ast code'); - const codeContainer = document.querySelector('#code code'); - - const sampleAst = `Joinpoint 'program' - Joinpoint 'file' - Joinpoint 'include' - Joinpoint 'include' - Joinpoint 'include' - Joinpoint 'comment' - Joinpoint 'comment' - Joinpoint 'function' - Joinpoint 'param' - Joinpoint 'param' - Joinpoint 'param' - Joinpoint 'param' - Joinpoint 'param' - Joinpoint 'param' - Joinpoint 'body' - Joinpoint 'loop' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'unaryOp' - Joinpoint 'varref' - Joinpoint 'body' - Joinpoint 'loop' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'unaryOp' - Joinpoint 'varref' - Joinpoint 'body' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'arrayAccess' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'intLiteral' - Joinpoint 'loop' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'unaryOp' - Joinpoint 'varref' - Joinpoint 'body' - Joinpoint 'loop' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'unaryOp' - Joinpoint 'varref' - Joinpoint 'body' - Joinpoint 'loop' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'unaryOp' - Joinpoint 'varref' - Joinpoint 'body' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'arrayAccess' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'arrayAccess' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'arrayAccess' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'comment' - Joinpoint 'function' - Joinpoint 'param' - Joinpoint 'param' - Joinpoint 'param' - Joinpoint 'body' - Joinpoint 'loop' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'unaryOp' - Joinpoint 'varref' - Joinpoint 'body' - Joinpoint 'loop' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'unaryOp' - Joinpoint 'varref' - Joinpoint 'body' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'arrayAccess' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'parenExpr' - Joinpoint 'cast' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'cast' - Joinpoint 'intLiteral' - Joinpoint 'function' - Joinpoint 'param' - Joinpoint 'param' - Joinpoint 'param' - Joinpoint 'body' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'floatLiteral' - Joinpoint 'if' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'intLiteral' - Joinpoint 'body' - Joinpoint 'exprStmt' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'intLiteral' - Joinpoint 'varref' - Joinpoint 'loop' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'unaryOp' - Joinpoint 'varref' - Joinpoint 'body' - Joinpoint 'loop' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'unaryOp' - Joinpoint 'varref' - Joinpoint 'body' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'arrayAccess' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'literal' - Joinpoint 'varref' - Joinpoint 'function' - Joinpoint 'body' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'cast' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'unaryExprOrType' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'cast' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'unaryExprOrType' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'cast' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'unaryExprOrType' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'exprStmt' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'exprStmt' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'function' - Joinpoint 'body' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'exprStmt' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'call' - Joinpoint 'varref'`; // TODO: Import from Lara - - if (astContainer != null && codeContainer != null) { - const nodeElements = createAstNodeElements(sampleAst); - fillAstContainer(nodeElements, astContainer); - linkCodeToAstNodes(nodeElements, codeContainer); - } -})(); +export { importAst }; diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts index b11867fe9..98a3ab112 100644 --- a/Lara-JS/src-api/visualization/public/js/communication.ts +++ b/Lara-JS/src-api/visualization/public/js/communication.ts @@ -1,3 +1,5 @@ +import { importAst } from "./ast-import.js"; + const getWebSocket = (): WebSocket => { const url = `ẁs://${window.location.host}`; return new WebSocket(url); @@ -6,7 +8,10 @@ const getWebSocket = (): WebSocket => { (() => { const ws = getWebSocket(); const continueButton = document.querySelector('#continue-button'); - if (!continueButton) + const astContainer = document.querySelector('#ast code'); + const codeContainer = document.querySelector('#code code'); + + if (!continueButton || !astContainer || !codeContainer) return; continueButton.addEventListener('click', () => { @@ -18,13 +23,17 @@ const getWebSocket = (): WebSocket => { const data = JSON.parse(message.data); switch (data.message) { - case 'continue': - continueButton.disabled = true; + case 'update': + importAst(data.ast, astContainer, codeContainer); break; - + case 'wait': continueButton.disabled = false; break; + + case 'continue': + continueButton.disabled = true; + break; } }); })(); diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js index fb2b151dc..61e6257cf 100644 --- a/LaraApi/src-lara/visualization/VisualizationTool.js +++ b/LaraApi/src-lara/visualization/VisualizationTool.js @@ -3,6 +3,7 @@ import http from 'http'; import path from 'path'; import { fileURLToPath } from 'url'; import { WebSocketServer } from 'ws'; +import JoinPoints from '../weaver/JoinPoints.js'; export default class VisualizationTool { static hostname; static port; @@ -52,10 +53,11 @@ export default class VisualizationTool { console.log('[server]: Client disconnected'); }); }); // TODO: Remove this + this.wss.on('connection', ws => this.updateClient(ws)); this.wss.on('close', () => { this.serverClosed = true; }); - this.wss.on('error', this.onWssError); + this.wss.on('error', error => this.onWssError(error)); return new Promise(res => { server.listen(port ?? 0, hostname, () => { const addressInfo = server.address(); @@ -75,10 +77,13 @@ export default class VisualizationTool { static sendToAllClients(data) { this.wss.clients.forEach(ws => this.sendToClient(ws, data)); } - static async waitForTool() { + static verifyToolIsRunning() { if (!this.isLaunched()) { throw Error('Visualization tool is not running'); } + } + static async waitForTool() { + this.verifyToolIsRunning(); return new Promise(res => { let placeClientOnWait; const waitOnMessage = (message) => { @@ -100,5 +105,12 @@ export default class VisualizationTool { this.wss.on('connection', placeClientOnWait); }); } + static updateClient(ws) { + this.sendToClient(ws, { message: 'update', ast: JoinPoints.root().dump.trim() }); + } + static update() { + this.verifyToolIsRunning(); + this.wss.clients.forEach(this.updateClient); + } } //# sourceMappingURL=VisualizationTool.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index e04bdad28..f99ef6779 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -56,325 +56,10 @@ const linkCodeToAstNodes = (nodeElements, codeContainer) => { // TODO: Associate only the real match (this associates all code fragments that are identical to the node code) } }; -(() => { - const astContainer = document.querySelector('#ast code'); - const codeContainer = document.querySelector('#code code'); - const sampleAst = `Joinpoint 'program' - Joinpoint 'file' - Joinpoint 'include' - Joinpoint 'include' - Joinpoint 'include' - Joinpoint 'comment' - Joinpoint 'comment' - Joinpoint 'function' - Joinpoint 'param' - Joinpoint 'param' - Joinpoint 'param' - Joinpoint 'param' - Joinpoint 'param' - Joinpoint 'param' - Joinpoint 'body' - Joinpoint 'loop' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'unaryOp' - Joinpoint 'varref' - Joinpoint 'body' - Joinpoint 'loop' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'unaryOp' - Joinpoint 'varref' - Joinpoint 'body' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'arrayAccess' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'intLiteral' - Joinpoint 'loop' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'unaryOp' - Joinpoint 'varref' - Joinpoint 'body' - Joinpoint 'loop' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'unaryOp' - Joinpoint 'varref' - Joinpoint 'body' - Joinpoint 'loop' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'unaryOp' - Joinpoint 'varref' - Joinpoint 'body' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'arrayAccess' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'arrayAccess' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'arrayAccess' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'comment' - Joinpoint 'function' - Joinpoint 'param' - Joinpoint 'param' - Joinpoint 'param' - Joinpoint 'body' - Joinpoint 'loop' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'unaryOp' - Joinpoint 'varref' - Joinpoint 'body' - Joinpoint 'loop' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'unaryOp' - Joinpoint 'varref' - Joinpoint 'body' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'arrayAccess' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'parenExpr' - Joinpoint 'cast' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'cast' - Joinpoint 'intLiteral' - Joinpoint 'function' - Joinpoint 'param' - Joinpoint 'param' - Joinpoint 'param' - Joinpoint 'body' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'floatLiteral' - Joinpoint 'if' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'intLiteral' - Joinpoint 'body' - Joinpoint 'exprStmt' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'intLiteral' - Joinpoint 'varref' - Joinpoint 'loop' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'unaryOp' - Joinpoint 'varref' - Joinpoint 'body' - Joinpoint 'loop' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'unaryOp' - Joinpoint 'varref' - Joinpoint 'body' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'exprStmt' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'arrayAccess' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'literal' - Joinpoint 'varref' - Joinpoint 'function' - Joinpoint 'body' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'intLiteral' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'cast' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'unaryExprOrType' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'cast' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'unaryExprOrType' - Joinpoint 'declStmt' - Joinpoint 'vardecl' - Joinpoint 'cast' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'binaryOp' - Joinpoint 'binaryOp' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'unaryExprOrType' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'exprStmt' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'exprStmt' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'exprStmt' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'varref' - Joinpoint 'function' - Joinpoint 'body' - Joinpoint 'wrapperStmt' - Joinpoint 'comment' - Joinpoint 'exprStmt' - Joinpoint 'call' - Joinpoint 'varref' - Joinpoint 'intLiteral' - Joinpoint 'exprStmt' - Joinpoint 'call' - Joinpoint 'varref'`; // TODO: Import from Lara - if (astContainer != null && codeContainer != null) { - const nodeElements = createAstNodeElements(sampleAst); - fillAstContainer(nodeElements, astContainer); - linkCodeToAstNodes(nodeElements, codeContainer); - } -})(); +const importAst = (ast, astContainer, codeContainer) => { + const nodeElements = createAstNodeElements(ast); + fillAstContainer(nodeElements, astContainer); + linkCodeToAstNodes(nodeElements, codeContainer); +}; +export { importAst }; //# sourceMappingURL=ast-import.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js index 8575475b0..bebd53ca5 100644 --- a/LaraApi/src-lara/visualization/public/js/communication.js +++ b/LaraApi/src-lara/visualization/public/js/communication.js @@ -1,3 +1,4 @@ +import { importAst } from "./ast-import.js"; const getWebSocket = () => { const url = `ẁs://${window.location.host}`; return new WebSocket(url); @@ -5,7 +6,9 @@ const getWebSocket = () => { (() => { const ws = getWebSocket(); const continueButton = document.querySelector('#continue-button'); - if (!continueButton) + const astContainer = document.querySelector('#ast code'); + const codeContainer = document.querySelector('#code code'); + if (!continueButton || !astContainer || !codeContainer) return; continueButton.addEventListener('click', () => { continueButton.disabled = true; @@ -14,14 +17,16 @@ const getWebSocket = () => { ws.addEventListener('message', (message) => { const data = JSON.parse(message.data); switch (data.message) { - case 'continue': - continueButton.disabled = true; + case 'update': + importAst(data.ast, astContainer, codeContainer); break; case 'wait': continueButton.disabled = false; break; + case 'continue': + continueButton.disabled = true; + break; } }); })(); -export {}; //# sourceMappingURL=communication.js.map \ No newline at end of file From 369807a8ff69609b4a33e576fbcd1f832b1403eb Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 5 Jul 2024 12:28:35 +0100 Subject: [PATCH 033/136] Add main script and remove default execution from the remaining modules --- .../src-api/visualization/public/index.html | 9 ++-- .../visualization/public/js/communication.ts | 38 ++++++++------ .../src-api/visualization/public/js/main.ts | 18 +++++++ .../visualization/public/js/visualization.ts | 5 +- .../up/fe/specs/lara/LaraApiJsResource.java | 1 + .../visualization/public/js/communication.js | 51 +++++++++---------- .../src-lara/visualization/public/js/main.js | 15 ++++++ .../visualization/public/js/visualization.js | 6 +-- 8 files changed, 86 insertions(+), 57 deletions(-) create mode 100644 Lara-JS/src-api/visualization/public/js/main.ts create mode 100644 LaraApi/src-lara/visualization/public/js/main.js diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index a510de3ca..3cce07b2e 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -5,10 +5,7 @@ - - - - + AST Visualization @@ -16,11 +13,11 @@

AST Visualization

AST

-
+

Code

-
#include <stdio.h>
+            
#include <stdio.h>
 #include <math.h>
 #include <stdlib.h>
 /*
diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts
index 98a3ab112..3f85fd295 100644
--- a/Lara-JS/src-api/visualization/public/js/communication.ts
+++ b/Lara-JS/src-api/visualization/public/js/communication.ts
@@ -5,22 +5,16 @@ const getWebSocket = (): WebSocket => {
   return new WebSocket(url);
 };
 
-(() => {
-  const ws = getWebSocket();
-  const continueButton = document.querySelector('#continue-button');
-  const astContainer = document.querySelector('#ast code');
-	const codeContainer = document.querySelector('#code code');
-
-  if (!continueButton || !astContainer || !codeContainer)
-    return;
+const sendData = (ws: WebSocket, data: any): void => {
+  ws.send(JSON.stringify(data));
+};
 
-  continueButton.addEventListener('click', () => {
-    continueButton.disabled = true;
-    ws.send(JSON.stringify({ message: 'continue' }));
-  });
+const parseMessage = (message: MessageEvent): any => {
+  return JSON.parse(message.data);
+};
 
-  ws.addEventListener('message', (message: MessageEvent) => {
-    const data = JSON.parse(message.data);
+const webSocketOnMessage = (message: MessageEvent, continueButton: HTMLButtonElement, astContainer: HTMLElement, codeContainer: HTMLElement): void => {
+  const data = parseMessage(message);
 
     switch (data.message) {
       case 'update':
@@ -35,5 +29,17 @@ const getWebSocket = (): WebSocket => {
         continueButton.disabled = true;
         break;
     }
-  });
-})();
+};
+
+const continueButtonOnClick = (continueButton: HTMLButtonElement, ws: WebSocket): void => {
+  continueButton.disabled = true;
+  sendData(ws, { message: 'continue' });
+};
+
+export {
+  getWebSocket,
+  sendData,
+  parseMessage,
+  webSocketOnMessage,
+  continueButtonOnClick,
+}
diff --git a/Lara-JS/src-api/visualization/public/js/main.ts b/Lara-JS/src-api/visualization/public/js/main.ts
new file mode 100644
index 000000000..39f6e7f46
--- /dev/null
+++ b/Lara-JS/src-api/visualization/public/js/main.ts
@@ -0,0 +1,18 @@
+import { continueButtonOnClick, getWebSocket, webSocketOnMessage } from "./communication.js";
+
+(() => {
+  const ws = getWebSocket();
+  console.log(ws);
+
+  const astContainer = document.querySelector('#ast-container');
+  const codeContainer = document.querySelector('#code-container');
+  const continueButton = document.querySelector('#continue-button');
+
+  if (!astContainer || !codeContainer || !continueButton) {
+    console.error('Required elements not found');
+    return;
+  }
+
+  ws.addEventListener('message', event => webSocketOnMessage(event, continueButton, astContainer, codeContainer));
+  continueButton.addEventListener('click', () => continueButtonOnClick(continueButton, ws));
+})();
\ No newline at end of file
diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts
index a13284f0b..0d142348d 100644
--- a/Lara-JS/src-api/visualization/public/js/visualization.ts
+++ b/Lara-JS/src-api/visualization/public/js/visualization.ts
@@ -26,7 +26,4 @@ const addEventListenersToAstNodes = (nodes: NodeListOf): void => {
   }
 };
 
-(() => {
-  const astNodes = document.querySelectorAll('.ast-node');
-  addEventListenersToAstNodes(astNodes);
-})();
+export { addEventListenersToAstNodes };
diff --git a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java
index 44d16ae23..5c3823e80 100644
--- a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java
+++ b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java
@@ -109,6 +109,7 @@ public enum LaraApiJsResource implements LaraResourceProvider {
     VISUALIZATIONTOOLUPDATE_JS("visualization/VisualizationToolUpdate.js"),
     AST_IMPORT_JS("visualization/public/js/ast-import.js"),
     COMMUNICATION_JS("visualization/public/js/communication.js"),
+    MAIN_JS("visualization/public/js/main.js"),
     UTILS_JS("visualization/public/js/utils.js"),
     VISUALIZATION_JS("visualization/public/js/visualization.js"),
     AST_JS("weaver/Ast.js"),
diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js
index bebd53ca5..8946c3f95 100644
--- a/LaraApi/src-lara/visualization/public/js/communication.js
+++ b/LaraApi/src-lara/visualization/public/js/communication.js
@@ -3,30 +3,29 @@ const getWebSocket = () => {
     const url = `ẁs://${window.location.host}`;
     return new WebSocket(url);
 };
-(() => {
-    const ws = getWebSocket();
-    const continueButton = document.querySelector('#continue-button');
-    const astContainer = document.querySelector('#ast code');
-    const codeContainer = document.querySelector('#code code');
-    if (!continueButton || !astContainer || !codeContainer)
-        return;
-    continueButton.addEventListener('click', () => {
-        continueButton.disabled = true;
-        ws.send(JSON.stringify({ message: 'continue' }));
-    });
-    ws.addEventListener('message', (message) => {
-        const data = JSON.parse(message.data);
-        switch (data.message) {
-            case 'update':
-                importAst(data.ast, astContainer, codeContainer);
-                break;
-            case 'wait':
-                continueButton.disabled = false;
-                break;
-            case 'continue':
-                continueButton.disabled = true;
-                break;
-        }
-    });
-})();
+const sendData = (ws, data) => {
+    ws.send(JSON.stringify(data));
+};
+const parseMessage = (message) => {
+    return JSON.parse(message.data);
+};
+const webSocketOnMessage = (message, continueButton, astContainer, codeContainer) => {
+    const data = parseMessage(message);
+    switch (data.message) {
+        case 'update':
+            importAst(data.ast, astContainer, codeContainer);
+            break;
+        case 'wait':
+            continueButton.disabled = false;
+            break;
+        case 'continue':
+            continueButton.disabled = true;
+            break;
+    }
+};
+const continueButtonOnClick = (continueButton, ws) => {
+    continueButton.disabled = true;
+    sendData(ws, { message: 'continue' });
+};
+export { getWebSocket, sendData, parseMessage, webSocketOnMessage, continueButtonOnClick, };
 //# sourceMappingURL=communication.js.map
\ No newline at end of file
diff --git a/LaraApi/src-lara/visualization/public/js/main.js b/LaraApi/src-lara/visualization/public/js/main.js
new file mode 100644
index 000000000..a68417c43
--- /dev/null
+++ b/LaraApi/src-lara/visualization/public/js/main.js
@@ -0,0 +1,15 @@
+import { continueButtonOnClick, getWebSocket, webSocketOnMessage } from "./communication.js";
+(() => {
+    const ws = getWebSocket();
+    console.log(ws);
+    const astContainer = document.querySelector('#ast-container');
+    const codeContainer = document.querySelector('#code-container');
+    const continueButton = document.querySelector('#continue-button');
+    if (!astContainer || !codeContainer || !continueButton) {
+        console.error('Required elements not found');
+        return;
+    }
+    ws.addEventListener('message', event => webSocketOnMessage(event, continueButton, astContainer, codeContainer));
+    continueButton.addEventListener('click', () => continueButtonOnClick(continueButton, ws));
+})();
+//# sourceMappingURL=main.js.map
\ No newline at end of file
diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js
index 7b6c141f5..ab7e9c17b 100644
--- a/LaraApi/src-lara/visualization/public/js/visualization.js
+++ b/LaraApi/src-lara/visualization/public/js/visualization.js
@@ -20,9 +20,5 @@ const addEventListenersToAstNodes = (nodes) => {
         }
     }
 };
-(() => {
-    const astNodes = document.querySelectorAll('.ast-node');
-    addEventListenersToAstNodes(astNodes);
-})();
-export {};
+export { addEventListenersToAstNodes };
 //# sourceMappingURL=visualization.js.map
\ No newline at end of file

From 1d2935c583a871449d8f67fb2f309ae8ff061227 Mon Sep 17 00:00:00 2001
From: Process-ing 
Date: Fri, 5 Jul 2024 12:33:31 +0100
Subject: [PATCH 034/136] Fix node highlighting

---
 Lara-JS/src-api/visualization/public/js/ast-import.ts    | 2 ++
 Lara-JS/src-api/visualization/public/js/visualization.ts | 2 +-
 LaraApi/src-lara/visualization/public/js/ast-import.js   | 2 ++
 3 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts
index 81c71db9f..3ae8c06f2 100644
--- a/Lara-JS/src-api/visualization/public/js/ast-import.ts
+++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts
@@ -1,4 +1,5 @@
 import { escapeHtml } from './utils.js';
+import { addEventListenersToAstNodes } from './visualization.js';
 
 const createAstNodeElements = (ast: any): HTMLElement[] => {
 	let id = 0;  // TODO: Refactor identification (e.g. using astId)
@@ -71,6 +72,7 @@ const importAst = (ast: any, astContainer: HTMLElement, codeContainer: HTMLEleme
   const nodeElements = createAstNodeElements(ast);
   fillAstContainer(nodeElements, astContainer);
   linkCodeToAstNodes(nodeElements, codeContainer);
+  addEventListenersToAstNodes(nodeElements);
 }
 
 export { importAst };
diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts
index 0d142348d..9d326af5a 100644
--- a/Lara-JS/src-api/visualization/public/js/visualization.ts
+++ b/Lara-JS/src-api/visualization/public/js/visualization.ts
@@ -10,7 +10,7 @@ const unhighlightElements = (elements: NodeListOf): void => {
   elements.forEach(element => element.classList.remove('highlighted'));
 }
 
-const addEventListenersToAstNodes = (nodes: NodeListOf): void => {
+const addEventListenersToAstNodes = (nodes: HTMLElement[]): void => {
   for (const nodeElement of nodes) {
     if (!nodeElement.dataset.nodeId) {
       continue;
diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js
index f99ef6779..ad14be3d3 100644
--- a/LaraApi/src-lara/visualization/public/js/ast-import.js
+++ b/LaraApi/src-lara/visualization/public/js/ast-import.js
@@ -1,4 +1,5 @@
 import { escapeHtml } from './utils.js';
+import { addEventListenersToAstNodes } from './visualization.js';
 const createAstNodeElements = (ast) => {
     let id = 0; // TODO: Refactor identification (e.g. using astId)
     const nodeElements = [];
@@ -60,6 +61,7 @@ const importAst = (ast, astContainer, codeContainer) => {
     const nodeElements = createAstNodeElements(ast);
     fillAstContainer(nodeElements, astContainer);
     linkCodeToAstNodes(nodeElements, codeContainer);
+    addEventListenersToAstNodes(nodeElements);
 };
 export { importAst };
 //# sourceMappingURL=ast-import.js.map
\ No newline at end of file

From 00be2945df0361e43eb37147f6bce86c0b86815f Mon Sep 17 00:00:00 2001
From: Process-ing 
Date: Fri, 5 Jul 2024 15:04:25 +0100
Subject: [PATCH 035/136] Try to make code generation and linking more dynamic

---
 .../visualization/VisualizationTool.ts        |  2 +-
 .../visualization/public/js/ast-import.ts     | 31 +++++++------------
 .../visualization/public/js/communication.ts  |  6 ++--
 .../src-api/visualization/public/js/main.ts   |  1 -
 .../visualization/VisualizationTool.js        |  3 +-
 .../visualization/public/js/ast-import.js     | 28 ++++++-----------
 .../visualization/public/js/communication.js  |  4 ++-
 .../src-lara/visualization/public/js/main.js  |  1 -
 8 files changed, 32 insertions(+), 44 deletions(-)

diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts
index 7ee96be5c..d3fc56522 100644
--- a/Lara-JS/src-api/visualization/VisualizationTool.ts
+++ b/Lara-JS/src-api/visualization/VisualizationTool.ts
@@ -136,7 +136,7 @@ export default class VisualizationTool {
   }
 
   private static updateClient(ws: WebSocket): void {
-    this.sendToClient(ws, { message: 'update', ast: JoinPoints.root().dump.trim() });
+    this.sendToClient(ws, { message: 'update', ast: JoinPoints.root().dump.trim() });  // TODO: Use real AST
   }
 
   public static update(): void {
diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts
index 3ae8c06f2..993556a1e 100644
--- a/Lara-JS/src-api/visualization/public/js/ast-import.ts
+++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts
@@ -6,7 +6,7 @@ const createAstNodeElements = (ast: any): HTMLElement[] => {
 	const nodeElements = [];
 
 	for (const node of ast.split('\n')) {
-		const matches = node.match(/(\s*)Joinpoint '(.+)'/, '');
+		const matches = node.match(/^(\s*)Joinpoint '(.+)'$/);
 		if (matches == null) {
 			console.warn(`Invalid node: "${node}"`);
 			continue;
@@ -34,24 +34,13 @@ const fillAstContainer = (nodeElements: HTMLElement[], astContainer: HTMLElement
 
 const linkCodeToAstNodes = (nodeElements: HTMLElement[], codeContainer: HTMLElement): void => {
 	for (const nodeElement of nodeElements) {
-		const nodeCode = `void matrix_mult(double const *A, double const *B, double *C, int const N, int const M, int const K) {
-   for(int ii = 0; ii < N; ii++) {
-      for(int jj = 0; jj < K; jj++) {
-         //C[i][j] = 0;
-         C[K * ii + jj] = 0;
-      }
-   }
-   for(int i = 0; i < N; i++) {
-      for(int l = 0; l < M; l++) {
-         for(int j = 0; j < K; j++) {
-            //C[i][j] += A[i][l]*B[l][j];
-            C[K * i + j] += A[M * i + l] * B[K * l + j];
-         }
-      }
-   }
-}`;  // TODO: Use real node code
-    const nodeCodeHtml = escapeHtml(nodeCode);
+		const nodeCode = nodeElement.textContent;  // TODO: Use real node code
+		if (nodeCode == null) {
+			console.warn(`Node with null code: "${nodeCode}"`);
+			continue;
+		}
 
+    const nodeCodeHtml = escapeHtml(nodeCode);
 		const nodeCodeStart = codeContainer.innerHTML.indexOf(nodeCodeHtml);
 		if (nodeCodeStart === -1) {
 			console.warn(`Node code not found in code container: "${nodeCode}"`);
@@ -68,6 +57,10 @@ const linkCodeToAstNodes = (nodeElements: HTMLElement[], codeContainer: HTMLElem
 	}
 }
 
+const importCode = (ast: any, codeContainer: HTMLElement): void => {
+  codeContainer.textContent = ast;
+}
+
 const importAst = (ast: any, astContainer: HTMLElement, codeContainer: HTMLElement): void => {
   const nodeElements = createAstNodeElements(ast);
   fillAstContainer(nodeElements, astContainer);
@@ -75,4 +68,4 @@ const importAst = (ast: any, astContainer: HTMLElement, codeContainer: HTMLEleme
   addEventListenersToAstNodes(nodeElements);
 }
 
-export { importAst };
+export { importCode, importAst };
diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts
index 3f85fd295..67b00bfdd 100644
--- a/Lara-JS/src-api/visualization/public/js/communication.ts
+++ b/Lara-JS/src-api/visualization/public/js/communication.ts
@@ -1,4 +1,5 @@
-import { importAst } from "./ast-import.js";
+import { LaraJoinPoint } from "../../../LaraJoinPoint.js";
+import { importAst, importCode } from "./ast-import.js";
 
 const getWebSocket = (): WebSocket => {
   const url = `ẁs://${window.location.host}`;
@@ -18,7 +19,8 @@ const webSocketOnMessage = (message: MessageEvent, continueButton: HTMLButtonEle
 
     switch (data.message) {
       case 'update':
-        importAst(data.ast, astContainer, codeContainer);
+        importCode(data.ast as LaraJoinPoint, codeContainer);
+        importAst(data.ast as LaraJoinPoint, astContainer, codeContainer);
         break;
 
       case 'wait':
diff --git a/Lara-JS/src-api/visualization/public/js/main.ts b/Lara-JS/src-api/visualization/public/js/main.ts
index 39f6e7f46..28f155f39 100644
--- a/Lara-JS/src-api/visualization/public/js/main.ts
+++ b/Lara-JS/src-api/visualization/public/js/main.ts
@@ -2,7 +2,6 @@ import { continueButtonOnClick, getWebSocket, webSocketOnMessage } from "./commu
 
 (() => {
   const ws = getWebSocket();
-  console.log(ws);
 
   const astContainer = document.querySelector('#ast-container');
   const codeContainer = document.querySelector('#code-container');
diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js
index 61e6257cf..688fb40eb 100644
--- a/LaraApi/src-lara/visualization/VisualizationTool.js
+++ b/LaraApi/src-lara/visualization/VisualizationTool.js
@@ -106,7 +106,8 @@ export default class VisualizationTool {
         });
     }
     static updateClient(ws) {
-        this.sendToClient(ws, { message: 'update', ast: JoinPoints.root().dump.trim() });
+        console.log(JoinPoints.root().self);
+        this.sendToClient(ws, { message: 'update', ast: JoinPoints.root().dump.trim() }); // TODO: Use real AST
     }
     static update() {
         this.verifyToolIsRunning();
diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js
index ad14be3d3..3bc485d8f 100644
--- a/LaraApi/src-lara/visualization/public/js/ast-import.js
+++ b/LaraApi/src-lara/visualization/public/js/ast-import.js
@@ -4,7 +4,7 @@ const createAstNodeElements = (ast) => {
     let id = 0; // TODO: Refactor identification (e.g. using astId)
     const nodeElements = [];
     for (const node of ast.split('\n')) {
-        const matches = node.match(/(\s*)Joinpoint '(.+)'/, '');
+        const matches = node.match(/^(\s*)Joinpoint '(.+)'$/);
         if (matches == null) {
             console.warn(`Invalid node: "${node}"`);
             continue;
@@ -27,22 +27,11 @@ const fillAstContainer = (nodeElements, astContainer) => {
 };
 const linkCodeToAstNodes = (nodeElements, codeContainer) => {
     for (const nodeElement of nodeElements) {
-        const nodeCode = `void matrix_mult(double const *A, double const *B, double *C, int const N, int const M, int const K) {
-   for(int ii = 0; ii < N; ii++) {
-      for(int jj = 0; jj < K; jj++) {
-         //C[i][j] = 0;
-         C[K * ii + jj] = 0;
-      }
-   }
-   for(int i = 0; i < N; i++) {
-      for(int l = 0; l < M; l++) {
-         for(int j = 0; j < K; j++) {
-            //C[i][j] += A[i][l]*B[l][j];
-            C[K * i + j] += A[M * i + l] * B[K * l + j];
-         }
-      }
-   }
-}`; // TODO: Use real node code
+        const nodeCode = nodeElement.textContent; // TODO: Use real node code
+        if (nodeCode == null) {
+            console.warn(`Node with null code: "${nodeCode}"`);
+            continue;
+        }
         const nodeCodeHtml = escapeHtml(nodeCode);
         const nodeCodeStart = codeContainer.innerHTML.indexOf(nodeCodeHtml);
         if (nodeCodeStart === -1) {
@@ -57,11 +46,14 @@ const linkCodeToAstNodes = (nodeElements, codeContainer) => {
         // TODO: Associate only the real match (this associates all code fragments that are identical to the node code)
     }
 };
+const importCode = (ast, codeContainer) => {
+    codeContainer.textContent = ast;
+};
 const importAst = (ast, astContainer, codeContainer) => {
     const nodeElements = createAstNodeElements(ast);
     fillAstContainer(nodeElements, astContainer);
     linkCodeToAstNodes(nodeElements, codeContainer);
     addEventListenersToAstNodes(nodeElements);
 };
-export { importAst };
+export { importCode, importAst };
 //# sourceMappingURL=ast-import.js.map
\ No newline at end of file
diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js
index 8946c3f95..43119f3ed 100644
--- a/LaraApi/src-lara/visualization/public/js/communication.js
+++ b/LaraApi/src-lara/visualization/public/js/communication.js
@@ -1,4 +1,4 @@
-import { importAst } from "./ast-import.js";
+import { importAst, importCode } from "./ast-import.js";
 const getWebSocket = () => {
     const url = `ẁs://${window.location.host}`;
     return new WebSocket(url);
@@ -13,6 +13,8 @@ const webSocketOnMessage = (message, continueButton, astContainer, codeContainer
     const data = parseMessage(message);
     switch (data.message) {
         case 'update':
+            console.log(data.ast);
+            importCode(data.ast, codeContainer);
             importAst(data.ast, astContainer, codeContainer);
             break;
         case 'wait':
diff --git a/LaraApi/src-lara/visualization/public/js/main.js b/LaraApi/src-lara/visualization/public/js/main.js
index a68417c43..9b1a459d2 100644
--- a/LaraApi/src-lara/visualization/public/js/main.js
+++ b/LaraApi/src-lara/visualization/public/js/main.js
@@ -1,7 +1,6 @@
 import { continueButtonOnClick, getWebSocket, webSocketOnMessage } from "./communication.js";
 (() => {
     const ws = getWebSocket();
-    console.log(ws);
     const astContainer = document.querySelector('#ast-container');
     const codeContainer = document.querySelector('#code-container');
     const continueButton = document.querySelector('#continue-button');

From ee9b3c1f84ebf6d09cd325d21616fc4e1553b65d Mon Sep 17 00:00:00 2001
From: Process-ing 
Date: Fri, 5 Jul 2024 15:16:26 +0100
Subject: [PATCH 036/136] Fix AST updates

---
 Lara-JS/src-api/visualization/VisualizationTool.ts        | 2 +-
 Lara-JS/src-api/visualization/public/js/ast-import.ts     | 2 ++
 LaraApi/src-lara/visualization/VisualizationTool.js       | 3 +--
 LaraApi/src-lara/visualization/public/js/ast-import.js    | 1 +
 LaraApi/src-lara/visualization/public/js/communication.js | 1 -
 5 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts
index d3fc56522..d38046ba5 100644
--- a/Lara-JS/src-api/visualization/VisualizationTool.ts
+++ b/Lara-JS/src-api/visualization/VisualizationTool.ts
@@ -141,6 +141,6 @@ export default class VisualizationTool {
 
   public static update(): void {
     this.verifyToolIsRunning();
-    this.wss!.clients.forEach(this.updateClient);
+    this.wss!.clients.forEach(ws => this.updateClient(ws));
   }
 }
\ No newline at end of file
diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts
index 993556a1e..db41a3a4e 100644
--- a/Lara-JS/src-api/visualization/public/js/ast-import.ts
+++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts
@@ -26,6 +26,8 @@ const createAstNodeElements = (ast: any): HTMLElement[] => {
 };
 
 const fillAstContainer = (nodeElements: HTMLElement[], astContainer: HTMLElement): void => {
+	astContainer.innerHTML = '';
+
 	for (const nodeElement of nodeElements) {
 		astContainer.appendChild(nodeElement);
 		astContainer.appendChild(document.createElement('br'));
diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js
index 688fb40eb..15b6cd016 100644
--- a/LaraApi/src-lara/visualization/VisualizationTool.js
+++ b/LaraApi/src-lara/visualization/VisualizationTool.js
@@ -106,12 +106,11 @@ export default class VisualizationTool {
         });
     }
     static updateClient(ws) {
-        console.log(JoinPoints.root().self);
         this.sendToClient(ws, { message: 'update', ast: JoinPoints.root().dump.trim() }); // TODO: Use real AST
     }
     static update() {
         this.verifyToolIsRunning();
-        this.wss.clients.forEach(this.updateClient);
+        this.wss.clients.forEach(ws => this.updateClient(ws));
     }
 }
 //# sourceMappingURL=VisualizationTool.js.map
\ No newline at end of file
diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js
index 3bc485d8f..b6e255e61 100644
--- a/LaraApi/src-lara/visualization/public/js/ast-import.js
+++ b/LaraApi/src-lara/visualization/public/js/ast-import.js
@@ -20,6 +20,7 @@ const createAstNodeElements = (ast) => {
     return nodeElements;
 };
 const fillAstContainer = (nodeElements, astContainer) => {
+    astContainer.innerHTML = '';
     for (const nodeElement of nodeElements) {
         astContainer.appendChild(nodeElement);
         astContainer.appendChild(document.createElement('br'));
diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js
index 43119f3ed..eb14f4d14 100644
--- a/LaraApi/src-lara/visualization/public/js/communication.js
+++ b/LaraApi/src-lara/visualization/public/js/communication.js
@@ -13,7 +13,6 @@ const webSocketOnMessage = (message, continueButton, astContainer, codeContainer
     const data = parseMessage(message);
     switch (data.message) {
         case 'update':
-            console.log(data.ast);
             importCode(data.ast, codeContainer);
             importAst(data.ast, astContainer, codeContainer);
             break;

From 9b120e95cac3f369cdc8d2d7f0c09106e4d20d99 Mon Sep 17 00:00:00 2001
From: Process-ing 
Date: Mon, 8 Jul 2024 08:46:44 +0100
Subject: [PATCH 037/136] Send tree data to web page

---
 .../visualization/VisualizationTool.ts        | 11 +++++--
 .../visualization/public/js/communication.ts  | 30 +++++++++----------
 .../visualization/VisualizationTool.js        |  9 +++++-
 .../visualization/public/js/communication.js  |  1 +
 4 files changed, 33 insertions(+), 18 deletions(-)

diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts
index d38046ba5..0a76fb825 100644
--- a/Lara-JS/src-api/visualization/VisualizationTool.ts
+++ b/Lara-JS/src-api/visualization/VisualizationTool.ts
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
 import WebSocket, { WebSocketServer, MessageEvent } from 'ws';
 import JoinPoints from '../weaver/JoinPoints.js';
 import { AddressInfo } from 'net';
-
+import { LaraJoinPoint, wrapJoinPoint } from '../LaraJoinPoint.js';
 export default class VisualizationTool {
   private static hostname: string | undefined;
   private static port: number | undefined;
@@ -135,8 +135,15 @@ export default class VisualizationTool {
     });
   }
 
+  private static parseTree(jp: LaraJoinPoint): Object {
+    return {
+      children: jp.children.map(child => this.parseTree(child)),
+      code: wrapJoinPoint(jp._javaObject.getCode()),
+    };
+  }
+
   private static updateClient(ws: WebSocket): void {
-    this.sendToClient(ws, { message: 'update', ast: JoinPoints.root().dump.trim() });  // TODO: Use real AST
+    this.sendToClient(ws, { message: 'update', ast: this.parseTree(JoinPoints.root()) });  // TODO: Use real AST
   }
 
   public static update(): void {
diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts
index 67b00bfdd..c62a566dc 100644
--- a/Lara-JS/src-api/visualization/public/js/communication.ts
+++ b/Lara-JS/src-api/visualization/public/js/communication.ts
@@ -1,4 +1,3 @@
-import { LaraJoinPoint } from "../../../LaraJoinPoint.js";
 import { importAst, importCode } from "./ast-import.js";
 
 const getWebSocket = (): WebSocket => {
@@ -17,20 +16,21 @@ const parseMessage = (message: MessageEvent): any => {
 const webSocketOnMessage = (message: MessageEvent, continueButton: HTMLButtonElement, astContainer: HTMLElement, codeContainer: HTMLElement): void => {
   const data = parseMessage(message);
 
-    switch (data.message) {
-      case 'update':
-        importCode(data.ast as LaraJoinPoint, codeContainer);
-        importAst(data.ast as LaraJoinPoint, astContainer, codeContainer);
-        break;
-
-      case 'wait':
-        continueButton.disabled = false;
-        break;
-
-      case 'continue':
-        continueButton.disabled = true;
-        break;
-    }
+  switch (data.message) {
+    case 'update':
+      console.log(data.ast);
+      importCode(data.ast, codeContainer);
+      importAst(data.ast, astContainer, codeContainer);
+      break;
+
+    case 'wait':
+      continueButton.disabled = false;
+      break;
+
+    case 'continue':
+      continueButton.disabled = true;
+      break;
+  }
 };
 
 const continueButtonOnClick = (continueButton: HTMLButtonElement, ws: WebSocket): void => {
diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js
index 15b6cd016..53473430c 100644
--- a/LaraApi/src-lara/visualization/VisualizationTool.js
+++ b/LaraApi/src-lara/visualization/VisualizationTool.js
@@ -4,6 +4,7 @@ import path from 'path';
 import { fileURLToPath } from 'url';
 import { WebSocketServer } from 'ws';
 import JoinPoints from '../weaver/JoinPoints.js';
+import { wrapJoinPoint } from '../LaraJoinPoint.js';
 export default class VisualizationTool {
     static hostname;
     static port;
@@ -105,8 +106,14 @@ export default class VisualizationTool {
             this.wss.on('connection', placeClientOnWait);
         });
     }
+    static parseTree(jp) {
+        return {
+            children: jp.children.map(child => this.parseTree(child)),
+            code: wrapJoinPoint(jp._javaObject.getCode()),
+        };
+    }
     static updateClient(ws) {
-        this.sendToClient(ws, { message: 'update', ast: JoinPoints.root().dump.trim() }); // TODO: Use real AST
+        this.sendToClient(ws, { message: 'update', ast: this.parseTree(JoinPoints.root()) }); // TODO: Use real AST
     }
     static update() {
         this.verifyToolIsRunning();
diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js
index eb14f4d14..43119f3ed 100644
--- a/LaraApi/src-lara/visualization/public/js/communication.js
+++ b/LaraApi/src-lara/visualization/public/js/communication.js
@@ -13,6 +13,7 @@ const webSocketOnMessage = (message, continueButton, astContainer, codeContainer
     const data = parseMessage(message);
     switch (data.message) {
         case 'update':
+            console.log(data.ast);
             importCode(data.ast, codeContainer);
             importAst(data.ast, astContainer, codeContainer);
             break;

From 2a539362527da1750fcb4ca0042872cd95cce413 Mon Sep 17 00:00:00 2001
From: Process-ing 
Date: Mon, 8 Jul 2024 15:03:04 +0100
Subject: [PATCH 038/136] Create ToolJoinPoint class

---
 .../visualization/VisualizationTool.ts        | 13 ++++++----
 .../visualization/public/js/ToolJoinPoint.ts  | 24 +++++++++++++++++++
 .../visualization/VisualizationTool.js        | 11 +++++----
 .../visualization/public/js/ToolJoinPoint.js  | 18 ++++++++++++++
 4 files changed, 58 insertions(+), 8 deletions(-)
 create mode 100644 Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts
 create mode 100644 LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js

diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts
index 0a76fb825..f48342682 100644
--- a/Lara-JS/src-api/visualization/VisualizationTool.ts
+++ b/Lara-JS/src-api/visualization/VisualizationTool.ts
@@ -3,9 +3,12 @@ import http from 'http';
 import path from 'path';
 import { fileURLToPath } from 'url';
 import WebSocket, { WebSocketServer, MessageEvent } from 'ws';
-import JoinPoints from '../weaver/JoinPoints.js';
 import { AddressInfo } from 'net';
+
 import { LaraJoinPoint, wrapJoinPoint } from '../LaraJoinPoint.js';
+import JoinPoints from '../weaver/JoinPoints.js';
+
+
 export default class VisualizationTool {
   private static hostname: string | undefined;
   private static port: number | undefined;
@@ -135,15 +138,17 @@ export default class VisualizationTool {
     });
   }
 
-  private static parseTree(jp: LaraJoinPoint): Object {
+  private static toToolJpJson(jp: LaraJoinPoint): any {
     return {
-      children: jp.children.map(child => this.parseTree(child)),
+      id: wrapJoinPoint(jp._javaObject.getAstId()),
+      type: wrapJoinPoint(jp._javaObject.getAstName()),
       code: wrapJoinPoint(jp._javaObject.getCode()),
+      children: jp.children.map(child => this.toToolJpJson(child))
     };
   }
 
   private static updateClient(ws: WebSocket): void {
-    this.sendToClient(ws, { message: 'update', ast: this.parseTree(JoinPoints.root()) });  // TODO: Use real AST
+    this.sendToClient(ws, { message: 'update', ast: this.toToolJpJson(JoinPoints.root()) });
   }
 
   public static update(): void {
diff --git a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts
new file mode 100644
index 000000000..5102490ff
--- /dev/null
+++ b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts
@@ -0,0 +1,24 @@
+class ToolJoinPoint {
+  id: string;
+  type: string;
+  code: string;
+  children: ToolJoinPoint[];
+
+  constructor(id: string, type: string, code: string, children: ToolJoinPoint[]) {
+    this.id = id;
+    this.type = type;
+    this.code = code;
+    this.children = children;
+  }
+
+  public static fromJSON(json: any): ToolJoinPoint {
+    return new ToolJoinPoint(
+      json.id,
+      json.type,
+      json.code,
+      json.children.map((child: any) => ToolJoinPoint.fromJSON(child)),
+    );
+  }
+};
+
+export default ToolJoinPoint;
\ No newline at end of file
diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js
index 53473430c..adf795232 100644
--- a/LaraApi/src-lara/visualization/VisualizationTool.js
+++ b/LaraApi/src-lara/visualization/VisualizationTool.js
@@ -3,8 +3,8 @@ import http from 'http';
 import path from 'path';
 import { fileURLToPath } from 'url';
 import { WebSocketServer } from 'ws';
-import JoinPoints from '../weaver/JoinPoints.js';
 import { wrapJoinPoint } from '../LaraJoinPoint.js';
+import JoinPoints from '../weaver/JoinPoints.js';
 export default class VisualizationTool {
     static hostname;
     static port;
@@ -106,14 +106,17 @@ export default class VisualizationTool {
             this.wss.on('connection', placeClientOnWait);
         });
     }
-    static parseTree(jp) {
+    static toToolJpJson(jp) {
+        console.log(jp.toString(), wrapJoinPoint(jp._javaObject.getLocation()));
         return {
-            children: jp.children.map(child => this.parseTree(child)),
+            id: wrapJoinPoint(jp._javaObject.getAstId()),
+            type: wrapJoinPoint(jp._javaObject.getAstName()),
             code: wrapJoinPoint(jp._javaObject.getCode()),
+            children: jp.children.map(child => this.toToolJpJson(child))
         };
     }
     static updateClient(ws) {
-        this.sendToClient(ws, { message: 'update', ast: this.parseTree(JoinPoints.root()) }); // TODO: Use real AST
+        this.sendToClient(ws, { message: 'update', ast: this.toToolJpJson(JoinPoints.root()) });
     }
     static update() {
         this.verifyToolIsRunning();
diff --git a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js
new file mode 100644
index 000000000..526c77865
--- /dev/null
+++ b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js
@@ -0,0 +1,18 @@
+class ToolJoinPoint {
+    id;
+    type;
+    code;
+    children;
+    constructor(id, type, code, children) {
+        this.id = id;
+        this.type = type;
+        this.code = code;
+        this.children = children;
+    }
+    static fromJSON(json) {
+        return new ToolJoinPoint(json.id, json.type, json.code, json.children.map((child) => ToolJoinPoint.fromJSON(child)));
+    }
+}
+;
+export default ToolJoinPoint;
+//# sourceMappingURL=ToolJoinPoint.js.map
\ No newline at end of file

From bc0788d5032c332a7396fdfbd751c500162a342f Mon Sep 17 00:00:00 2001
From: Process-ing 
Date: Mon, 8 Jul 2024 15:10:19 +0100
Subject: [PATCH 039/136] Use real AST on visualization tool

---
 .../visualization/public/js/ast-import.ts     | 85 +++++++++---------
 .../visualization/public/js/communication.ts  |  7 +-
 .../src-api/visualization/public/js/utils.ts  |  4 +-
 .../visualization/public/js/visualization.ts  |  4 +-
 .../up/fe/specs/lara/LaraApiJsResource.java   |  1 +
 .../visualization/public/js/ast-import.js     | 90 ++++++++++---------
 .../visualization/public/js/communication.js  |  7 +-
 .../src-lara/visualization/public/js/utils.js |  4 +-
 .../visualization/public/js/visualization.js  |  2 +-
 9 files changed, 106 insertions(+), 98 deletions(-)

diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts
index db41a3a4e..c48f98733 100644
--- a/Lara-JS/src-api/visualization/public/js/ast-import.ts
+++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts
@@ -1,25 +1,17 @@
 import { escapeHtml } from './utils.js';
 import { addEventListenersToAstNodes } from './visualization.js';
+import JoinPoint from './ToolJoinPoint.js';
 
-const createAstNodeElements = (ast: any): HTMLElement[] => {
-	let id = 0;  // TODO: Refactor identification (e.g. using astId)
-	const nodeElements = [];
+const convertAstNodesToElements = (root: JoinPoint, depth: number = 0): HTMLElement[] => {
+	const rootElement = document.createElement('span');
+	rootElement.classList.add('ast-node');
+	rootElement.dataset.nodeId = root.id;
+	rootElement.style.marginLeft = (depth * 2) + "em";
+	rootElement.textContent = root.type;
 
-	for (const node of ast.split('\n')) {
-		const matches = node.match(/^(\s*)Joinpoint '(.+)'$/);
-		if (matches == null) {
-			console.warn(`Invalid node: "${node}"`);
-			continue;
-		}
-		const [, indentation, nodeName] = matches;
-		
-		const nodeElement = document.createElement('span');
-		nodeElement.classList.add('ast-node');  // TODO: Add joinpoint info
-		nodeElement.dataset.nodeId = (id++).toString();
-		nodeElement.style.marginLeft = (indentation.length / 2) + "em";
-		nodeElement.textContent = nodeName;
-
-		nodeElements.push(nodeElement);
+	const nodeElements = [rootElement];
+	for (const node of root.children) {
+		nodeElements.push(...convertAstNodesToElements(node, depth + 1));
 	}
 
 	return nodeElements;
@@ -34,39 +26,46 @@ const fillAstContainer = (nodeElements: HTMLElement[], astContainer: HTMLElement
 	}
 }
 
-const linkCodeToAstNodes = (nodeElements: HTMLElement[], codeContainer: HTMLElement): void => {
-	for (const nodeElement of nodeElements) {
-		const nodeCode = nodeElement.textContent;  // TODO: Use real node code
-		if (nodeCode == null) {
-			console.warn(`Node with null code: "${nodeCode}"`);
-			continue;
-		}
+const linkCodeToAstNodes = (root: JoinPoint, codeContainer: HTMLElement, codeStart: number = 0): number => {
+	const nodeElement = document.querySelector(`span.ast-node[data-node-id="${root.id}"]`);
+	if (nodeElement == null) {
+		console.warn(`Node element not found: "${root.id}"`);
+		return 0;
+	}
 
-    const nodeCodeHtml = escapeHtml(nodeCode);
-		const nodeCodeStart = codeContainer.innerHTML.indexOf(nodeCodeHtml);
-		if (nodeCodeStart === -1) {
-			console.warn(`Node code not found in code container: "${nodeCode}"`);
-			continue;
-		}
+	const nodeCode = root.code.trim();
+	const nodeCodeHtml = escapeHtml(nodeCode);
+	const nodeCodeStart = codeContainer.innerHTML.indexOf(nodeCodeHtml, codeStart);
+	if (nodeCodeStart === -1) {
+		console.warn(`Node code not found in code container: "${nodeCodeHtml}"`);
+		return 0;
+	}
+
+	const nodeCodeWrapper = document.createElement('span');
+	nodeCodeWrapper.classList.add('node-code');
+	nodeCodeWrapper.dataset.nodeId = root.id.toString();
+	nodeCodeWrapper.innerHTML = nodeCodeHtml;
+	codeContainer.innerHTML = codeContainer.innerHTML.replace(nodeCodeHtml, nodeCodeWrapper.outerHTML);
+  // TODO: Associate only the real match (this associates all code fragments that are identical to the node code)
 
-		const nodeCodeWrapper = document.createElement('span');
-		nodeCodeWrapper.classList.add('node-code');
-		nodeCodeWrapper.dataset.nodeId = nodeElement.dataset.nodeId;
-    nodeCodeWrapper.textContent = nodeCode;
-		
-		codeContainer.innerHTML = codeContainer.innerHTML.replaceAll(nodeCodeHtml, nodeCodeWrapper.outerHTML);
-    // TODO: Associate only the real match (this associates all code fragments that are identical to the node code)
+	const nodeCodeContainer = codeContainer.querySelector(`span.node-code[data-node-id="${root.id}"]`);
+	let nodeCodeLowerBound = 0;
+	for (const child of root.children) {
+		nodeCodeLowerBound = linkCodeToAstNodes(child, nodeCodeContainer!, nodeCodeLowerBound);
 	}
+
+	const codeEnd = nodeCodeStart + nodeCodeWrapper.outerHTML.length;
+	return codeEnd;
 }
 
-const importCode = (ast: any, codeContainer: HTMLElement): void => {
-  codeContainer.textContent = ast;
+const importCode = (astRoot: JoinPoint, codeContainer: HTMLElement): void => {
+  codeContainer.innerHTML = escapeHtml(astRoot.code);
 }
 
-const importAst = (ast: any, astContainer: HTMLElement, codeContainer: HTMLElement): void => {
-  const nodeElements = createAstNodeElements(ast);
+const importAst = (astRoot: JoinPoint, astContainer: HTMLElement, codeContainer: HTMLElement): void => {
+  const nodeElements = convertAstNodesToElements(astRoot);
   fillAstContainer(nodeElements, astContainer);
-  linkCodeToAstNodes(nodeElements, codeContainer);
+  linkCodeToAstNodes(astRoot, codeContainer);
   addEventListenersToAstNodes(nodeElements);
 }
 
diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts
index c62a566dc..8ced63ca2 100644
--- a/Lara-JS/src-api/visualization/public/js/communication.ts
+++ b/Lara-JS/src-api/visualization/public/js/communication.ts
@@ -1,4 +1,5 @@
 import { importAst, importCode } from "./ast-import.js";
+import JoinPoint from "./ToolJoinPoint.js";
 
 const getWebSocket = (): WebSocket => {
   const url = `ẁs://${window.location.host}`;
@@ -18,9 +19,9 @@ const webSocketOnMessage = (message: MessageEvent, continueButton: HTMLButtonEle
 
   switch (data.message) {
     case 'update':
-      console.log(data.ast);
-      importCode(data.ast, codeContainer);
-      importAst(data.ast, astContainer, codeContainer);
+      const ast = JoinPoint.fromJSON(data.ast);
+      importCode(ast, codeContainer);
+      importAst(ast, astContainer, codeContainer);
       break;
 
     case 'wait':
diff --git a/Lara-JS/src-api/visualization/public/js/utils.ts b/Lara-JS/src-api/visualization/public/js/utils.ts
index be6d4172b..1780a2b6c 100644
--- a/Lara-JS/src-api/visualization/public/js/utils.ts
+++ b/Lara-JS/src-api/visualization/public/js/utils.ts
@@ -3,11 +3,9 @@ const escapeHtml = (text: string): string => {
     '&': '&',
     '<': '<',
     '>': '>',
-    '"': '"',
-    "'": '''
   };
   
-  return text.replace(/[&<>"']/g, (match) => specialCharMap[match]);
+  return text.replace(/[&<>]/g, (match) => specialCharMap[match]);
 }
 
 export { escapeHtml };
\ No newline at end of file
diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts
index 9d326af5a..700fded66 100644
--- a/Lara-JS/src-api/visualization/public/js/visualization.ts
+++ b/Lara-JS/src-api/visualization/public/js/visualization.ts
@@ -1,4 +1,4 @@
-const getElementsWithNodeId = (id: number): NodeListOf => {
+const getElementsWithNodeId = (id: string): NodeListOf => {
   return document.querySelectorAll(`span[data-node-id="${id}"]`);
 }
 
@@ -16,7 +16,7 @@ const addEventListenersToAstNodes = (nodes: HTMLElement[]): void => {
       continue;
     }
 
-    const nodeId = parseInt(nodeElement.dataset.nodeId!);
+    const nodeId = nodeElement.dataset.nodeId!;
     const nodeRelatedElements = getElementsWithNodeId(nodeId);
 
     for (const nodeRelatedElement of nodeRelatedElements) {
diff --git a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java
index 5c3823e80..3fce72572 100644
--- a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java
+++ b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java
@@ -107,6 +107,7 @@ public enum LaraApiJsResource implements LaraResourceProvider {
     CYTOSCAPE_3_26_0_JS("libs/cytoscape-3.26.0.js"),
     VISUALIZATIONTOOL_JS("visualization/VisualizationTool.js"),
     VISUALIZATIONTOOLUPDATE_JS("visualization/VisualizationToolUpdate.js"),
+    TOOLJOINPOINT_JS("visualization/public/js/ToolJoinPoint.js"),
     AST_IMPORT_JS("visualization/public/js/ast-import.js"),
     COMMUNICATION_JS("visualization/public/js/communication.js"),
     MAIN_JS("visualization/public/js/main.js"),
diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js
index b6e255e61..be0a8c292 100644
--- a/LaraApi/src-lara/visualization/public/js/ast-import.js
+++ b/LaraApi/src-lara/visualization/public/js/ast-import.js
@@ -1,21 +1,14 @@
 import { escapeHtml } from './utils.js';
 import { addEventListenersToAstNodes } from './visualization.js';
-const createAstNodeElements = (ast) => {
-    let id = 0; // TODO: Refactor identification (e.g. using astId)
-    const nodeElements = [];
-    for (const node of ast.split('\n')) {
-        const matches = node.match(/^(\s*)Joinpoint '(.+)'$/);
-        if (matches == null) {
-            console.warn(`Invalid node: "${node}"`);
-            continue;
-        }
-        const [, indentation, nodeName] = matches;
-        const nodeElement = document.createElement('span');
-        nodeElement.classList.add('ast-node'); // TODO: Add joinpoint info
-        nodeElement.dataset.nodeId = (id++).toString();
-        nodeElement.style.marginLeft = (indentation.length / 2) + "em";
-        nodeElement.textContent = nodeName;
-        nodeElements.push(nodeElement);
+const convertAstNodesToElements = (root, depth = 0) => {
+    const rootElement = document.createElement('span');
+    rootElement.classList.add('ast-node');
+    rootElement.dataset.nodeId = root.id;
+    rootElement.style.marginLeft = (depth * 2) + "em";
+    rootElement.textContent = root.type;
+    const nodeElements = [rootElement];
+    for (const node of root.children) {
+        nodeElements.push(...convertAstNodesToElements(node, depth + 1));
     }
     return nodeElements;
 };
@@ -26,34 +19,51 @@ const fillAstContainer = (nodeElements, astContainer) => {
         astContainer.appendChild(document.createElement('br'));
     }
 };
-const linkCodeToAstNodes = (nodeElements, codeContainer) => {
-    for (const nodeElement of nodeElements) {
-        const nodeCode = nodeElement.textContent; // TODO: Use real node code
-        if (nodeCode == null) {
-            console.warn(`Node with null code: "${nodeCode}"`);
-            continue;
-        }
-        const nodeCodeHtml = escapeHtml(nodeCode);
-        const nodeCodeStart = codeContainer.innerHTML.indexOf(nodeCodeHtml);
-        if (nodeCodeStart === -1) {
-            console.warn(`Node code not found in code container: "${nodeCode}"`);
-            continue;
-        }
-        const nodeCodeWrapper = document.createElement('span');
-        nodeCodeWrapper.classList.add('node-code');
-        nodeCodeWrapper.dataset.nodeId = nodeElement.dataset.nodeId;
-        nodeCodeWrapper.textContent = nodeCode;
-        codeContainer.innerHTML = codeContainer.innerHTML.replaceAll(nodeCodeHtml, nodeCodeWrapper.outerHTML);
-        // TODO: Associate only the real match (this associates all code fragments that are identical to the node code)
+// const addIdentation = (code: string, indentation: number): string => {
+// 	return code.split('\n').map((line, i) => i > 0 ? '   '.repeat(indentation) + line : line).join('\n');
+// }
+// const refineAstNodeCode = (root: JoinPoint, indentation: number = 0): void => {
+// 	root.code = addIdentation(root.code.trim(), indentation);
+// 	if (root.type == "l" && root.children.length >= 3 && root.children[2].type == "ExprStmt")
+// 		root.children[2].code = root.children[2].code.trim().slice(0, -1);  // Remove semicolon from increment expression
+// 	for (const child of root.children) {
+// 		refineAstNodeCode(child, root.type === 'CompoundStmt' ? indentation + 1 : indentation);
+// 	}
+// }
+const linkCodeToAstNodes = (root, codeContainer, codeStart = 0) => {
+    const nodeElement = document.querySelector(`span.ast-node[data-node-id="${root.id}"]`);
+    if (nodeElement == null) {
+        console.warn(`Node element not found: "${root.id}"`);
+        return 0;
+    }
+    const nodeCode = root.code.trim();
+    const nodeCodeHtml = escapeHtml(nodeCode);
+    const nodeCodeStart = codeContainer.innerHTML.indexOf(nodeCodeHtml, codeStart);
+    if (nodeCodeStart === -1) {
+        console.warn(`Node code not found in code container: "${nodeCodeHtml}"`);
+        return 0;
+    }
+    const nodeCodeWrapper = document.createElement('span');
+    nodeCodeWrapper.classList.add('node-code');
+    nodeCodeWrapper.dataset.nodeId = root.id.toString();
+    nodeCodeWrapper.innerHTML = nodeCodeHtml;
+    codeContainer.innerHTML = codeContainer.innerHTML.replace(nodeCodeHtml, nodeCodeWrapper.outerHTML);
+    // TODO: Associate only the real match (this associates all code fragments that are identical to the node code)
+    const nodeCodeContainer = codeContainer.querySelector(`span.node-code[data-node-id="${root.id}"]`);
+    let nodeCodeLowerBound = 0;
+    for (const child of root.children) {
+        nodeCodeLowerBound = linkCodeToAstNodes(child, nodeCodeContainer, nodeCodeLowerBound);
     }
+    const codeEnd = nodeCodeStart + nodeCodeWrapper.outerHTML.length;
+    return codeEnd;
 };
-const importCode = (ast, codeContainer) => {
-    codeContainer.textContent = ast;
+const importCode = (astRoot, codeContainer) => {
+    codeContainer.innerHTML = escapeHtml(astRoot.code);
 };
-const importAst = (ast, astContainer, codeContainer) => {
-    const nodeElements = createAstNodeElements(ast);
+const importAst = (astRoot, astContainer, codeContainer) => {
+    const nodeElements = convertAstNodesToElements(astRoot);
     fillAstContainer(nodeElements, astContainer);
-    linkCodeToAstNodes(nodeElements, codeContainer);
+    linkCodeToAstNodes(astRoot, codeContainer);
     addEventListenersToAstNodes(nodeElements);
 };
 export { importCode, importAst };
diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js
index 43119f3ed..9505d3ec0 100644
--- a/LaraApi/src-lara/visualization/public/js/communication.js
+++ b/LaraApi/src-lara/visualization/public/js/communication.js
@@ -1,4 +1,5 @@
 import { importAst, importCode } from "./ast-import.js";
+import JoinPoint from "./ToolJoinPoint.js";
 const getWebSocket = () => {
     const url = `ẁs://${window.location.host}`;
     return new WebSocket(url);
@@ -13,9 +14,9 @@ const webSocketOnMessage = (message, continueButton, astContainer, codeContainer
     const data = parseMessage(message);
     switch (data.message) {
         case 'update':
-            console.log(data.ast);
-            importCode(data.ast, codeContainer);
-            importAst(data.ast, astContainer, codeContainer);
+            const ast = JoinPoint.fromJSON(data.ast);
+            importCode(ast, codeContainer);
+            importAst(ast, astContainer, codeContainer);
             break;
         case 'wait':
             continueButton.disabled = false;
diff --git a/LaraApi/src-lara/visualization/public/js/utils.js b/LaraApi/src-lara/visualization/public/js/utils.js
index 854cf0233..01e9f2366 100644
--- a/LaraApi/src-lara/visualization/public/js/utils.js
+++ b/LaraApi/src-lara/visualization/public/js/utils.js
@@ -3,10 +3,8 @@ const escapeHtml = (text) => {
         '&': '&',
         '<': '<',
         '>': '>',
-        '"': '"',
-        "'": '''
     };
-    return text.replace(/[&<>"']/g, (match) => specialCharMap[match]);
+    return text.replace(/[&<>]/g, (match) => specialCharMap[match]);
 };
 export { escapeHtml };
 //# sourceMappingURL=utils.js.map
\ No newline at end of file
diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js
index ab7e9c17b..894737789 100644
--- a/LaraApi/src-lara/visualization/public/js/visualization.js
+++ b/LaraApi/src-lara/visualization/public/js/visualization.js
@@ -12,7 +12,7 @@ const addEventListenersToAstNodes = (nodes) => {
         if (!nodeElement.dataset.nodeId) {
             continue;
         }
-        const nodeId = parseInt(nodeElement.dataset.nodeId);
+        const nodeId = nodeElement.dataset.nodeId;
         const nodeRelatedElements = getElementsWithNodeId(nodeId);
         for (const nodeRelatedElement of nodeRelatedElements) {
             nodeRelatedElement.addEventListener('mouseover', () => highlightElements(nodeRelatedElements));

From 9af14facdb06fa92d7f93e254bf22275a9828d2d Mon Sep 17 00:00:00 2001
From: Process-ing 
Date: Mon, 8 Jul 2024 15:48:51 +0100
Subject: [PATCH 040/136] Fix problems with indentation and incerment
 statements in for loops

---
 .../visualization/public/js/ToolJoinPoint.ts  |  4 +++
 .../visualization/public/js/ast-import.ts     | 21 ++++++++++++--
 .../visualization/public/js/visualization.ts  | 10 +++++--
 .../visualization/VisualizationTool.js        |  1 -
 .../visualization/public/js/ToolJoinPoint.js  |  3 ++
 .../visualization/public/js/ast-import.js     | 28 ++++++++++---------
 .../visualization/public/js/visualization.js  | 10 +++++--
 7 files changed, 57 insertions(+), 20 deletions(-)

diff --git a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts
index 5102490ff..1d7d85c1b 100644
--- a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts
+++ b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts
@@ -19,6 +19,10 @@ class ToolJoinPoint {
       json.children.map((child: any) => ToolJoinPoint.fromJSON(child)),
     );
   }
+
+  public clone(): ToolJoinPoint {
+    return new ToolJoinPoint(this.id, this.type, this.code, this.children.map(child => child.clone()));
+  }
 };
 
 export default ToolJoinPoint;
\ No newline at end of file
diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts
index c48f98733..81a30e73a 100644
--- a/Lara-JS/src-api/visualization/public/js/ast-import.ts
+++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts
@@ -26,6 +26,20 @@ const fillAstContainer = (nodeElements: HTMLElement[], astContainer: HTMLElement
 	}
 }
 
+const addIdentation = (code: string, indentation: number): string => {
+	return code.split('\n').map((line, i) => i > 0 ? '   '.repeat(indentation) + line : line).join('\n');
+}
+
+const refineAst = (root: JoinPoint, indentation: number = 0): void => {
+	root.code = addIdentation(root.code.trim(), indentation);
+	if (root.type == "ForStmt" && root.children.length >= 3 && root.children[2].type == "ExprStmt")
+		root.children[2].code = root.children[2].code.slice(0, -1);  // Remove semicolon from increment expression in for loop
+
+	for (const child of root.children) {
+		refineAst(child, root.type === 'CompoundStmt' ? indentation + 1 : indentation);
+	}
+}
+
 const linkCodeToAstNodes = (root: JoinPoint, codeContainer: HTMLElement, codeStart: number = 0): number => {
 	const nodeElement = document.querySelector(`span.ast-node[data-node-id="${root.id}"]`);
 	if (nodeElement == null) {
@@ -63,9 +77,12 @@ const importCode = (astRoot: JoinPoint, codeContainer: HTMLElement): void => {
 }
 
 const importAst = (astRoot: JoinPoint, astContainer: HTMLElement, codeContainer: HTMLElement): void => {
-  const nodeElements = convertAstNodesToElements(astRoot);
+	const refinedAstRoot = astRoot.clone();
+	refineAst(refinedAstRoot);
+
+  const nodeElements = convertAstNodesToElements(refinedAstRoot);
   fillAstContainer(nodeElements, astContainer);
-  linkCodeToAstNodes(astRoot, codeContainer);
+  linkCodeToAstNodes(refinedAstRoot, codeContainer);
   addEventListenersToAstNodes(nodeElements);
 }
 
diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts
index 700fded66..7adf63180 100644
--- a/Lara-JS/src-api/visualization/public/js/visualization.ts
+++ b/Lara-JS/src-api/visualization/public/js/visualization.ts
@@ -20,8 +20,14 @@ const addEventListenersToAstNodes = (nodes: HTMLElement[]): void => {
     const nodeRelatedElements = getElementsWithNodeId(nodeId);
 
     for (const nodeRelatedElement of nodeRelatedElements) {
-      nodeRelatedElement.addEventListener('mouseover', () => highlightElements(nodeRelatedElements));
-      nodeRelatedElement.addEventListener('mouseout', () => unhighlightElements(nodeRelatedElements));
+      nodeRelatedElement.addEventListener('mouseover', event => {
+        highlightElements(nodeRelatedElements);
+        event.stopPropagation();
+      });
+      nodeRelatedElement.addEventListener('mouseout', event => {
+        unhighlightElements(nodeRelatedElements);
+        event.stopPropagation();
+      });
     }
   }
 };
diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js
index adf795232..0dd009c68 100644
--- a/LaraApi/src-lara/visualization/VisualizationTool.js
+++ b/LaraApi/src-lara/visualization/VisualizationTool.js
@@ -107,7 +107,6 @@ export default class VisualizationTool {
         });
     }
     static toToolJpJson(jp) {
-        console.log(jp.toString(), wrapJoinPoint(jp._javaObject.getLocation()));
         return {
             id: wrapJoinPoint(jp._javaObject.getAstId()),
             type: wrapJoinPoint(jp._javaObject.getAstName()),
diff --git a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js
index 526c77865..45a32640b 100644
--- a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js
+++ b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js
@@ -12,6 +12,9 @@ class ToolJoinPoint {
     static fromJSON(json) {
         return new ToolJoinPoint(json.id, json.type, json.code, json.children.map((child) => ToolJoinPoint.fromJSON(child)));
     }
+    clone() {
+        return new ToolJoinPoint(this.id, this.type, this.code, this.children.map(child => child.clone()));
+    }
 }
 ;
 export default ToolJoinPoint;
diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js
index be0a8c292..f2cc0877a 100644
--- a/LaraApi/src-lara/visualization/public/js/ast-import.js
+++ b/LaraApi/src-lara/visualization/public/js/ast-import.js
@@ -19,17 +19,17 @@ const fillAstContainer = (nodeElements, astContainer) => {
         astContainer.appendChild(document.createElement('br'));
     }
 };
-// const addIdentation = (code: string, indentation: number): string => {
-// 	return code.split('\n').map((line, i) => i > 0 ? '   '.repeat(indentation) + line : line).join('\n');
-// }
-// const refineAstNodeCode = (root: JoinPoint, indentation: number = 0): void => {
-// 	root.code = addIdentation(root.code.trim(), indentation);
-// 	if (root.type == "l" && root.children.length >= 3 && root.children[2].type == "ExprStmt")
-// 		root.children[2].code = root.children[2].code.trim().slice(0, -1);  // Remove semicolon from increment expression
-// 	for (const child of root.children) {
-// 		refineAstNodeCode(child, root.type === 'CompoundStmt' ? indentation + 1 : indentation);
-// 	}
-// }
+const addIdentation = (code, indentation) => {
+    return code.split('\n').map((line, i) => i > 0 ? '   '.repeat(indentation) + line : line).join('\n');
+};
+const refineAst = (root, indentation = 0) => {
+    root.code = addIdentation(root.code.trim(), indentation);
+    if (root.type == "ForStmt" && root.children.length >= 3 && root.children[2].type == "ExprStmt")
+        root.children[2].code = root.children[2].code.slice(0, -1); // Remove semicolon from increment expression in for loop
+    for (const child of root.children) {
+        refineAst(child, root.type === 'CompoundStmt' ? indentation + 1 : indentation);
+    }
+};
 const linkCodeToAstNodes = (root, codeContainer, codeStart = 0) => {
     const nodeElement = document.querySelector(`span.ast-node[data-node-id="${root.id}"]`);
     if (nodeElement == null) {
@@ -61,9 +61,11 @@ const importCode = (astRoot, codeContainer) => {
     codeContainer.innerHTML = escapeHtml(astRoot.code);
 };
 const importAst = (astRoot, astContainer, codeContainer) => {
-    const nodeElements = convertAstNodesToElements(astRoot);
+    const refinedAstRoot = astRoot.clone();
+    refineAst(refinedAstRoot);
+    const nodeElements = convertAstNodesToElements(refinedAstRoot);
     fillAstContainer(nodeElements, astContainer);
-    linkCodeToAstNodes(astRoot, codeContainer);
+    linkCodeToAstNodes(refinedAstRoot, codeContainer);
     addEventListenersToAstNodes(nodeElements);
 };
 export { importCode, importAst };
diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js
index 894737789..ba6263940 100644
--- a/LaraApi/src-lara/visualization/public/js/visualization.js
+++ b/LaraApi/src-lara/visualization/public/js/visualization.js
@@ -15,8 +15,14 @@ const addEventListenersToAstNodes = (nodes) => {
         const nodeId = nodeElement.dataset.nodeId;
         const nodeRelatedElements = getElementsWithNodeId(nodeId);
         for (const nodeRelatedElement of nodeRelatedElements) {
-            nodeRelatedElement.addEventListener('mouseover', () => highlightElements(nodeRelatedElements));
-            nodeRelatedElement.addEventListener('mouseout', () => unhighlightElements(nodeRelatedElements));
+            nodeRelatedElement.addEventListener('mouseover', event => {
+                highlightElements(nodeRelatedElements);
+                event.stopPropagation();
+            });
+            nodeRelatedElement.addEventListener('mouseout', event => {
+                unhighlightElements(nodeRelatedElements);
+                event.stopPropagation();
+            });
         }
     }
 };

From b833ff1d493ad81d785be7839f6b804ed6ed05d8 Mon Sep 17 00:00:00 2001
From: Process-ing 
Date: Mon, 8 Jul 2024 16:39:36 +0100
Subject: [PATCH 041/136] Fix search and replace bounds

---
 Lara-JS/src-api/visualization/public/js/ast-import.ts  |  6 +++---
 Lara-JS/src-api/visualization/public/js/utils.ts       | 10 +++++++++-
 LaraApi/src-lara/visualization/public/js/ast-import.js | 10 +++++++---
 LaraApi/src-lara/visualization/public/js/utils.js      |  9 ++++++++-
 4 files changed, 27 insertions(+), 8 deletions(-)

diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts
index 81a30e73a..cda05c689 100644
--- a/Lara-JS/src-api/visualization/public/js/ast-import.ts
+++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts
@@ -1,4 +1,4 @@
-import { escapeHtml } from './utils.js';
+import { escapeHtml, replaceAfter } from './utils.js';
 import { addEventListenersToAstNodes } from './visualization.js';
 import JoinPoint from './ToolJoinPoint.js';
 
@@ -59,7 +59,7 @@ const linkCodeToAstNodes = (root: JoinPoint, codeContainer: HTMLElement, codeSta
 	nodeCodeWrapper.classList.add('node-code');
 	nodeCodeWrapper.dataset.nodeId = root.id.toString();
 	nodeCodeWrapper.innerHTML = nodeCodeHtml;
-	codeContainer.innerHTML = codeContainer.innerHTML.replace(nodeCodeHtml, nodeCodeWrapper.outerHTML);
+	codeContainer.innerHTML = replaceAfter(codeContainer.innerHTML, nodeCodeHtml, nodeCodeWrapper.outerHTML, codeStart);
   // TODO: Associate only the real match (this associates all code fragments that are identical to the node code)
 
 	const nodeCodeContainer = codeContainer.querySelector(`span.node-code[data-node-id="${root.id}"]`);
@@ -68,7 +68,7 @@ const linkCodeToAstNodes = (root: JoinPoint, codeContainer: HTMLElement, codeSta
 		nodeCodeLowerBound = linkCodeToAstNodes(child, nodeCodeContainer!, nodeCodeLowerBound);
 	}
 
-	const codeEnd = nodeCodeStart + nodeCodeWrapper.outerHTML.length;
+	const codeEnd = nodeCodeStart + nodeCodeContainer!.outerHTML.length;
 	return codeEnd;
 }
 
diff --git a/Lara-JS/src-api/visualization/public/js/utils.ts b/Lara-JS/src-api/visualization/public/js/utils.ts
index 1780a2b6c..33ab949e2 100644
--- a/Lara-JS/src-api/visualization/public/js/utils.ts
+++ b/Lara-JS/src-api/visualization/public/js/utils.ts
@@ -8,4 +8,12 @@ const escapeHtml = (text: string): string => {
   return text.replace(/[&<>]/g, (match) => specialCharMap[match]);
 }
 
-export { escapeHtml };
\ No newline at end of file
+const replaceAfter = (text: string, search: string, replace: string, start: number): string => {
+  const index = text.indexOf(search, start);
+  if (index === -1) {
+    return text;
+  }
+  return text.slice(0, index) + replace + text.slice(index + search.length);
+}
+
+export { escapeHtml, replaceAfter };
\ No newline at end of file
diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js
index f2cc0877a..3077a697d 100644
--- a/LaraApi/src-lara/visualization/public/js/ast-import.js
+++ b/LaraApi/src-lara/visualization/public/js/ast-import.js
@@ -1,4 +1,4 @@
-import { escapeHtml } from './utils.js';
+import { escapeHtml, replaceAfter } from './utils.js';
 import { addEventListenersToAstNodes } from './visualization.js';
 const convertAstNodesToElements = (root, depth = 0) => {
     const rootElement = document.createElement('span');
@@ -43,18 +43,22 @@ const linkCodeToAstNodes = (root, codeContainer, codeStart = 0) => {
         console.warn(`Node code not found in code container: "${nodeCodeHtml}"`);
         return 0;
     }
+    if (root.type == "DeclRefExpr") {
+        console.log(`DeclRefExpr: ${root.code}`);
+        console.log(`DeclRefExpr: ${codeContainer.innerHTML}`);
+    }
     const nodeCodeWrapper = document.createElement('span');
     nodeCodeWrapper.classList.add('node-code');
     nodeCodeWrapper.dataset.nodeId = root.id.toString();
     nodeCodeWrapper.innerHTML = nodeCodeHtml;
-    codeContainer.innerHTML = codeContainer.innerHTML.replace(nodeCodeHtml, nodeCodeWrapper.outerHTML);
+    codeContainer.innerHTML = replaceAfter(codeContainer.innerHTML, nodeCodeHtml, nodeCodeWrapper.outerHTML, codeStart);
     // TODO: Associate only the real match (this associates all code fragments that are identical to the node code)
     const nodeCodeContainer = codeContainer.querySelector(`span.node-code[data-node-id="${root.id}"]`);
     let nodeCodeLowerBound = 0;
     for (const child of root.children) {
         nodeCodeLowerBound = linkCodeToAstNodes(child, nodeCodeContainer, nodeCodeLowerBound);
     }
-    const codeEnd = nodeCodeStart + nodeCodeWrapper.outerHTML.length;
+    const codeEnd = nodeCodeStart + nodeCodeContainer.outerHTML.length;
     return codeEnd;
 };
 const importCode = (astRoot, codeContainer) => {
diff --git a/LaraApi/src-lara/visualization/public/js/utils.js b/LaraApi/src-lara/visualization/public/js/utils.js
index 01e9f2366..1912041ba 100644
--- a/LaraApi/src-lara/visualization/public/js/utils.js
+++ b/LaraApi/src-lara/visualization/public/js/utils.js
@@ -6,5 +6,12 @@ const escapeHtml = (text) => {
     };
     return text.replace(/[&<>]/g, (match) => specialCharMap[match]);
 };
-export { escapeHtml };
+const replaceAfter = (text, search, replace, start) => {
+    const index = text.indexOf(search, start);
+    if (index === -1) {
+        return text;
+    }
+    return text.slice(0, index) + replace + text.slice(index + search.length);
+};
+export { escapeHtml, replaceAfter };
 //# sourceMappingURL=utils.js.map
\ No newline at end of file

From ed4a7377c8cbdcdfa285cd55d5a5849a82132e1e Mon Sep 17 00:00:00 2001
From: Process-ing 
Date: Tue, 9 Jul 2024 08:42:58 +0100
Subject: [PATCH 042/136] Fix condition search for while and do-while loops

---
 Lara-JS/src-api/visualization/public/js/ast-import.ts  | 5 +++++
 LaraApi/src-lara/visualization/public/js/ast-import.js | 8 ++++----
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts
index cda05c689..29bf1644b 100644
--- a/Lara-JS/src-api/visualization/public/js/ast-import.ts
+++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts
@@ -32,6 +32,11 @@ const addIdentation = (code: string, indentation: number): string => {
 
 const refineAst = (root: JoinPoint, indentation: number = 0): void => {
 	root.code = addIdentation(root.code.trim(), indentation);
+
+	if (root.type == "WhileStmt")
+		root.children[0].code = root.children[0].code.slice(0, -1);  // Remove semicolon from condition in while loop
+	if (root.type == "DoStmt")
+		root.children[1].code = root.children[1].code.slice(0, -1);  // Remove semicolon from condition in do-while loop
 	if (root.type == "ForStmt" && root.children.length >= 3 && root.children[2].type == "ExprStmt")
 		root.children[2].code = root.children[2].code.slice(0, -1);  // Remove semicolon from increment expression in for loop
 
diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js
index 3077a697d..92b8d02b6 100644
--- a/LaraApi/src-lara/visualization/public/js/ast-import.js
+++ b/LaraApi/src-lara/visualization/public/js/ast-import.js
@@ -24,6 +24,10 @@ const addIdentation = (code, indentation) => {
 };
 const refineAst = (root, indentation = 0) => {
     root.code = addIdentation(root.code.trim(), indentation);
+    if (root.type == "WhileStmt")
+        root.children[0].code = root.children[0].code.slice(0, -1); // Remove semicolon from condition in while loop
+    if (root.type == "DoStmt")
+        root.children[1].code = root.children[1].code.slice(0, -1); // Remove semicolon from condition in do
     if (root.type == "ForStmt" && root.children.length >= 3 && root.children[2].type == "ExprStmt")
         root.children[2].code = root.children[2].code.slice(0, -1); // Remove semicolon from increment expression in for loop
     for (const child of root.children) {
@@ -43,10 +47,6 @@ const linkCodeToAstNodes = (root, codeContainer, codeStart = 0) => {
         console.warn(`Node code not found in code container: "${nodeCodeHtml}"`);
         return 0;
     }
-    if (root.type == "DeclRefExpr") {
-        console.log(`DeclRefExpr: ${root.code}`);
-        console.log(`DeclRefExpr: ${codeContainer.innerHTML}`);
-    }
     const nodeCodeWrapper = document.createElement('span');
     nodeCodeWrapper.classList.add('node-code');
     nodeCodeWrapper.dataset.nodeId = root.id.toString();

From 7d36c450b4c4f1b255519ef2744337e644396f50 Mon Sep 17 00:00:00 2001
From: Process-ing 
Date: Tue, 9 Jul 2024 09:38:06 +0100
Subject: [PATCH 043/136] Fix multiple variable declaration statements search

---
 .../visualization/public/js/ast-import.ts       | 17 +++++++++++++----
 .../visualization/public/js/ast-import.js       | 17 ++++++++++++-----
 2 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts
index 29bf1644b..8dd730868 100644
--- a/Lara-JS/src-api/visualization/public/js/ast-import.ts
+++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts
@@ -6,7 +6,7 @@ const convertAstNodesToElements = (root: JoinPoint, depth: number = 0): HTMLElem
 	const rootElement = document.createElement('span');
 	rootElement.classList.add('ast-node');
 	rootElement.dataset.nodeId = root.id;
-	rootElement.style.marginLeft = (depth * 2) + "em";
+	rootElement.style.marginLeft = (depth * 2) + 'em';
 	rootElement.textContent = root.type;
 
 	const nodeElements = [rootElement];
@@ -33,13 +33,22 @@ const addIdentation = (code: string, indentation: number): string => {
 const refineAst = (root: JoinPoint, indentation: number = 0): void => {
 	root.code = addIdentation(root.code.trim(), indentation);
 
-	if (root.type == "WhileStmt")
+	if (root.type == 'WhileStmt')
 		root.children[0].code = root.children[0].code.slice(0, -1);  // Remove semicolon from condition in while loop
-	if (root.type == "DoStmt")
+	if (root.type == 'DoStmt')
 		root.children[1].code = root.children[1].code.slice(0, -1);  // Remove semicolon from condition in do-while loop
-	if (root.type == "ForStmt" && root.children.length >= 3 && root.children[2].type == "ExprStmt")
+	if (root.type == 'ForStmt' && root.children.length >= 3 && root.children[2].type == 'ExprStmt')
 		root.children[2].code = root.children[2].code.slice(0, -1);  // Remove semicolon from increment expression in for loop
 
+	if (root.type == 'DeclStmt') {
+		root.children
+			.slice(1)
+			.forEach(child => {
+				child.code = child.code.match(/(?:\S+\s+)(\S.*)/)![1];
+			});  // Remove type from variable declarations
+	}
+
+
 	for (const child of root.children) {
 		refineAst(child, root.type === 'CompoundStmt' ? indentation + 1 : indentation);
 	}
diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js
index 92b8d02b6..07e3a28ba 100644
--- a/LaraApi/src-lara/visualization/public/js/ast-import.js
+++ b/LaraApi/src-lara/visualization/public/js/ast-import.js
@@ -4,7 +4,7 @@ const convertAstNodesToElements = (root, depth = 0) => {
     const rootElement = document.createElement('span');
     rootElement.classList.add('ast-node');
     rootElement.dataset.nodeId = root.id;
-    rootElement.style.marginLeft = (depth * 2) + "em";
+    rootElement.style.marginLeft = (depth * 2) + 'em';
     rootElement.textContent = root.type;
     const nodeElements = [rootElement];
     for (const node of root.children) {
@@ -24,12 +24,19 @@ const addIdentation = (code, indentation) => {
 };
 const refineAst = (root, indentation = 0) => {
     root.code = addIdentation(root.code.trim(), indentation);
-    if (root.type == "WhileStmt")
+    if (root.type == 'WhileStmt')
         root.children[0].code = root.children[0].code.slice(0, -1); // Remove semicolon from condition in while loop
-    if (root.type == "DoStmt")
-        root.children[1].code = root.children[1].code.slice(0, -1); // Remove semicolon from condition in do
-    if (root.type == "ForStmt" && root.children.length >= 3 && root.children[2].type == "ExprStmt")
+    if (root.type == 'DoStmt')
+        root.children[1].code = root.children[1].code.slice(0, -1); // Remove semicolon from condition in do-while loop
+    if (root.type == 'ForStmt' && root.children.length >= 3 && root.children[2].type == 'ExprStmt')
         root.children[2].code = root.children[2].code.slice(0, -1); // Remove semicolon from increment expression in for loop
+    if (root.type == 'DeclStmt') {
+        root.children
+            .slice(1)
+            .forEach(child => {
+            child.code = child.code.match(/(?:\S+\s+)(\S.*)/)[1];
+        }); // Remove type from variable declarations
+    }
     for (const child of root.children) {
         refineAst(child, root.type === 'CompoundStmt' ? indentation + 1 : indentation);
     }

From c5399c8ad645f3bc24d5c8333509c1eda82c1d51 Mon Sep 17 00:00:00 2001
From: Process-ing 
Date: Tue, 9 Jul 2024 10:02:57 +0100
Subject: [PATCH 044/136] Fix more bugs with search of expressions inside loop
 parentheses

---
 .../src-api/visualization/public/js/ast-import.ts    | 12 ++++++------
 .../src-lara/visualization/public/js/ast-import.js   | 12 ++++++------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts
index 8dd730868..831142657 100644
--- a/Lara-JS/src-api/visualization/public/js/ast-import.ts
+++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts
@@ -33,12 +33,12 @@ const addIdentation = (code: string, indentation: number): string => {
 const refineAst = (root: JoinPoint, indentation: number = 0): void => {
 	root.code = addIdentation(root.code.trim(), indentation);
 
-	if (root.type == 'WhileStmt')
-		root.children[0].code = root.children[0].code.slice(0, -1);  // Remove semicolon from condition in while loop
-	if (root.type == 'DoStmt')
-		root.children[1].code = root.children[1].code.slice(0, -1);  // Remove semicolon from condition in do-while loop
-	if (root.type == 'ForStmt' && root.children.length >= 3 && root.children[2].type == 'ExprStmt')
-		root.children[2].code = root.children[2].code.slice(0, -1);  // Remove semicolon from increment expression in for loop
+	const children = root.children;
+	if (['WhileStmt', 'DoStmt', 'ForStmt'].includes(root.type)) {
+		children
+			.filter(child => child.type === 'ExprStmt')
+			.forEach(child => child.code = child.code.slice(0, -1));	// Remove semicolon from expression statements inside loop parentheses
+	}
 
 	if (root.type == 'DeclStmt') {
 		root.children
diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js
index 07e3a28ba..12af80e55 100644
--- a/LaraApi/src-lara/visualization/public/js/ast-import.js
+++ b/LaraApi/src-lara/visualization/public/js/ast-import.js
@@ -24,12 +24,12 @@ const addIdentation = (code, indentation) => {
 };
 const refineAst = (root, indentation = 0) => {
     root.code = addIdentation(root.code.trim(), indentation);
-    if (root.type == 'WhileStmt')
-        root.children[0].code = root.children[0].code.slice(0, -1); // Remove semicolon from condition in while loop
-    if (root.type == 'DoStmt')
-        root.children[1].code = root.children[1].code.slice(0, -1); // Remove semicolon from condition in do-while loop
-    if (root.type == 'ForStmt' && root.children.length >= 3 && root.children[2].type == 'ExprStmt')
-        root.children[2].code = root.children[2].code.slice(0, -1); // Remove semicolon from increment expression in for loop
+    const children = root.children;
+    if (['WhileStmt', 'DoStmt', 'ForStmt'].includes(root.type)) {
+        children
+            .filter(child => child.type === 'ExprStmt')
+            .forEach(child => child.code = child.code.slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses
+    }
     if (root.type == 'DeclStmt') {
         root.children
             .slice(1)

From 14e0d93419b9c5f4305a51799941d84431493b9a Mon Sep 17 00:00:00 2001
From: Process-ing 
Date: Tue, 9 Jul 2024 10:25:42 +0100
Subject: [PATCH 045/136] Refactor highlighting

---
 .../visualization/public/css/styles.css       |  6 +-
 .../src-api/visualization/public/index.html   | 74 +------------------
 .../visualization/public/js/visualization.ts  |  5 +-
 .../visualization/public/js/visualization.js  |  5 +-
 4 files changed, 8 insertions(+), 82 deletions(-)

diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css
index 0ad19dacf..775e5c8c5 100644
--- a/Lara-JS/src-api/visualization/public/css/styles.css
+++ b/Lara-JS/src-api/visualization/public/css/styles.css
@@ -17,11 +17,7 @@ pre {
 }
 
 pre * {
-  border-radius: 4px;
-}
-
-.highlighted {
-  background-color: yellow;
+  transition: background-color 0.05s linear;
 }
 
 .ast-node::before {
diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html
index 3cce07b2e..adad551db 100644
--- a/Lara-JS/src-api/visualization/public/index.html
+++ b/Lara-JS/src-api/visualization/public/index.html
@@ -17,79 +17,7 @@ 

AST

Code

-
#include <stdio.h>
-#include <math.h>
-#include <stdlib.h>
-/*
-Simple matrix multiplication example.
-*/
-/*
-matrix multiplication
-*/
-void matrix_mult(double const *A, double const *B, double *C, int const N, int const M, int const K) {
-   for(int ii = 0; ii < N; ii++) {
-      for(int jj = 0; jj < K; jj++) {
-         //C[i][j] = 0;
-         C[K * ii + jj] = 0;
-      }
-   }
-   for(int i = 0; i < N; i++) {
-      for(int l = 0; l < M; l++) {
-         for(int j = 0; j < K; j++) {
-            //C[i][j] += A[i][l]*B[l][j];
-            C[K * i + j] += A[M * i + l] * B[K * l + j];
-         }
-      }
-   }
-}
-
-/*
-* Set an N by M matrix A to random values
-*/
-void init_matrix(double *A, int const N, int const M) {
-   for(int i = 0; i < N; ++i) {
-      for(int j = 0; j < M; ++j) {
-         //A[i][j] = ((double) rand()) / (double) RAND_MAX; 
-         A[M * i + j] = ((double) rand()) / (double) 2147483647;
-      }
-   }
-}
-
-void print_matrix_result(double *A, int const N, int const K) {
-   double acc = 0.0;
-   if(N == 1) print_matrix_result(A, N - 1, K);
-   for(int i = 0; i < N; ++i) {
-      for(int j = 0; j < K; ++j) {
-         //acc += A[i][j];
-         acc += A[K * i + j];
-      }
-   }
-   printf("Result acc: %f\n", acc);
-}
-
-void test_matrix_mul() {
-   int N = 512;
-   int M = 256;
-   int K = 512;
-   //double A[N][M];
-   //double B[M][K];
-   //double C[N][K];
-   double *A = (double *) malloc(N * M * sizeof(double));
-   double *B = (double *) malloc(M * K * sizeof(double));
-   double *C = (double *) malloc(N * K * sizeof(double));
-   // initialize matrices
-   init_matrix(A, N, M);
-   init_matrix(B, M, K);
-   // do: C = A*B
-   matrix_mult(A, B, C, N, M, K);
-   print_matrix_result(C, N, K);
-}
-
-int main() {
-   // To make results repeatable
-   srand(0);
-   test_matrix_mul();
-}
+
diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index 7adf63180..43fe663ee 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -3,16 +3,17 @@ const getElementsWithNodeId = (id: string): NodeListOf => { } const highlightElements = (elements: NodeListOf): void => { - elements.forEach(element => element.classList.add('highlighted')); + elements.forEach(element => element.style.backgroundColor = 'yellow'); }; const unhighlightElements = (elements: NodeListOf): void => { - elements.forEach(element => element.classList.remove('highlighted')); + elements.forEach(element => element.style.backgroundColor = ''); } const addEventListenersToAstNodes = (nodes: HTMLElement[]): void => { for (const nodeElement of nodes) { if (!nodeElement.dataset.nodeId) { + console.warn('Node element does not have a data-node-id attribute'); continue; } diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index ba6263940..f671cb218 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -2,14 +2,15 @@ const getElementsWithNodeId = (id) => { return document.querySelectorAll(`span[data-node-id="${id}"]`); }; const highlightElements = (elements) => { - elements.forEach(element => element.classList.add('highlighted')); + elements.forEach(element => element.style.backgroundColor = 'yellow'); }; const unhighlightElements = (elements) => { - elements.forEach(element => element.classList.remove('highlighted')); + elements.forEach(element => element.style.backgroundColor = ''); }; const addEventListenersToAstNodes = (nodes) => { for (const nodeElement of nodes) { if (!nodeElement.dataset.nodeId) { + console.warn('Node element does not have a data-node-id attribute'); continue; } const nodeId = nodeElement.dataset.nodeId; From b5a2422409281771bae0eddca74fa9cbe0375305 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 11 Jul 2024 21:12:46 +0100 Subject: [PATCH 046/136] Add favicon --- .../src-api/visualization/public/favicon.ico | Bin 0 -> 15406 bytes Lara-JS/src-api/visualization/public/index.html | 1 - 2 files changed, 1 deletion(-) create mode 100644 Lara-JS/src-api/visualization/public/favicon.ico diff --git a/Lara-JS/src-api/visualization/public/favicon.ico b/Lara-JS/src-api/visualization/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..23007c7f312006be99414f5d8bbbf72a57d57f06 GIT binary patch literal 15406 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 IpN9qh10n5$cmMzZ literal 0 HcmV?d00001 diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index adad551db..1daef5c79 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -3,7 +3,6 @@ - AST Visualization From d8ff8f3222d0582c49d965607d20b5b6d47c43ea Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 12 Jul 2024 16:27:13 +0100 Subject: [PATCH 047/136] Make WebSocket reconnect on close --- Lara-JS/src-api/visualization/public/js/main.ts | 11 ++++++++--- LaraApi/src-lara/visualization/public/js/main.js | 9 +++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/main.ts b/Lara-JS/src-api/visualization/public/js/main.ts index 28f155f39..e0377115c 100644 --- a/Lara-JS/src-api/visualization/public/js/main.ts +++ b/Lara-JS/src-api/visualization/public/js/main.ts @@ -1,8 +1,6 @@ import { continueButtonOnClick, getWebSocket, webSocketOnMessage } from "./communication.js"; (() => { - const ws = getWebSocket(); - const astContainer = document.querySelector('#ast-container'); const codeContainer = document.querySelector('#code-container'); const continueButton = document.querySelector('#continue-button'); @@ -12,6 +10,13 @@ import { continueButtonOnClick, getWebSocket, webSocketOnMessage } from "./commu return; } - ws.addEventListener('message', event => webSocketOnMessage(event, continueButton, astContainer, codeContainer)); + let ws: WebSocket; + const setupWebSocket = () => { + ws = getWebSocket(); + ws.addEventListener('message', event => webSocketOnMessage(event, continueButton, astContainer, codeContainer)); + ws.addEventListener('close', () => setupWebSocket()); + }; + setupWebSocket(); + continueButton.addEventListener('click', () => continueButtonOnClick(continueButton, ws)); })(); \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/main.js b/LaraApi/src-lara/visualization/public/js/main.js index 9b1a459d2..186e2682d 100644 --- a/LaraApi/src-lara/visualization/public/js/main.js +++ b/LaraApi/src-lara/visualization/public/js/main.js @@ -1,6 +1,5 @@ import { continueButtonOnClick, getWebSocket, webSocketOnMessage } from "./communication.js"; (() => { - const ws = getWebSocket(); const astContainer = document.querySelector('#ast-container'); const codeContainer = document.querySelector('#code-container'); const continueButton = document.querySelector('#continue-button'); @@ -8,7 +7,13 @@ import { continueButtonOnClick, getWebSocket, webSocketOnMessage } from "./commu console.error('Required elements not found'); return; } - ws.addEventListener('message', event => webSocketOnMessage(event, continueButton, astContainer, codeContainer)); + let ws; + const setupWebSocket = () => { + ws = getWebSocket(); + ws.addEventListener('message', event => webSocketOnMessage(event, continueButton, astContainer, codeContainer)); + ws.addEventListener('close', () => setupWebSocket()); + }; + setupWebSocket(); continueButton.addEventListener('click', () => continueButtonOnClick(continueButton, ws)); })(); //# sourceMappingURL=main.js.map \ No newline at end of file From 8ee1c5cd0846bd82083eba0d5391e4afae8234a0 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 12 Jul 2024 18:07:25 +0100 Subject: [PATCH 048/136] Create color scheme --- .../visualization/public/css/color-scheme.css | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Lara-JS/src-api/visualization/public/css/color-scheme.css diff --git a/Lara-JS/src-api/visualization/public/css/color-scheme.css b/Lara-JS/src-api/visualization/public/css/color-scheme.css new file mode 100644 index 000000000..6e3c8f335 --- /dev/null +++ b/Lara-JS/src-api/visualization/public/css/color-scheme.css @@ -0,0 +1,25 @@ +/* Generic colors */ + +:root { + --white: #fff; + --lighter-gray: #e4e4e4; + --light-gray: #d4d4d4; + --gray: #a2a2a2; + --dark-gray: #313131; + --black: #000; + --light-blue: #19d8ff; + --transparent-light-blue: #19d8ff66; +} + +:root { + --bg-color: var(--white); + --text-color: var(--dark-gray); + --header-color: var(--lighter-gray); + --divider-color: var(--light-gray); + --icon-color: var(--dark-gray); + --tab-bg-color: var(--white); + --tab-hover-bg-color: var(--lighter-gray); + --tab-active-bg-color: var(--light-gray); + --highlight-color: var(--light-blue); + --secondary-highlight-color: var(--transparent-light-blue); +} \ No newline at end of file From 432b5704cf5219db3c99cb32b94d78db35f49340 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 12 Jul 2024 18:10:20 +0100 Subject: [PATCH 049/136] Change default font --- Lara-JS/src-api/visualization/public/css/styles.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 775e5c8c5..543454deb 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -1,3 +1,9 @@ +@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap'); + +body { + font-family: "Roboto", sans-serif; +} + #ast-code-visualization > article { display: inline-block; } From 51f51615120ab633afc7a4e4baf4e31c3ef910f2 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 12 Jul 2024 18:14:33 +0100 Subject: [PATCH 050/136] Fix child node ordering --- Lara-JS/src-api/visualization/VisualizationTool.ts | 6 +++++- LaraApi/src-lara/visualization/VisualizationTool.js | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts index f48342682..9a7980d5d 100644 --- a/Lara-JS/src-api/visualization/VisualizationTool.ts +++ b/Lara-JS/src-api/visualization/VisualizationTool.ts @@ -139,11 +139,15 @@ export default class VisualizationTool { } private static toToolJpJson(jp: LaraJoinPoint): any { + console.log(wrapJoinPoint(jp._javaObject.getAstName()), '=>', wrapJoinPoint(jp._javaObject.getLocation())); return { id: wrapJoinPoint(jp._javaObject.getAstId()), type: wrapJoinPoint(jp._javaObject.getAstName()), code: wrapJoinPoint(jp._javaObject.getCode()), - children: jp.children.map(child => this.toToolJpJson(child)) + children: jp.children + .slice() + .sort((a, b) => wrapJoinPoint(a._javaObject.getLocation()).localeCompare(wrapJoinPoint(b._javaObject.getLocation()), 'en', { numeric: true })) + .map(child => this.toToolJpJson(child)) }; } diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js index 0dd009c68..efae7599a 100644 --- a/LaraApi/src-lara/visualization/VisualizationTool.js +++ b/LaraApi/src-lara/visualization/VisualizationTool.js @@ -107,11 +107,15 @@ export default class VisualizationTool { }); } static toToolJpJson(jp) { + console.log(wrapJoinPoint(jp._javaObject.getAstName()), '=>', wrapJoinPoint(jp._javaObject.getLocation())); return { id: wrapJoinPoint(jp._javaObject.getAstId()), type: wrapJoinPoint(jp._javaObject.getAstName()), code: wrapJoinPoint(jp._javaObject.getCode()), - children: jp.children.map(child => this.toToolJpJson(child)) + children: jp.children + .slice() + .sort((a, b) => wrapJoinPoint(a._javaObject.getLocation()).localeCompare(wrapJoinPoint(b._javaObject.getLocation()), 'en', { numeric: true })) + .map(child => this.toToolJpJson(child)) }; } static updateClient(ws) { From f946bfe1944cbd463f9dd21a2c54fd9fd0fa0a7f Mon Sep 17 00:00:00 2001 From: Process-ing Date: Sun, 14 Jul 2024 21:11:18 +0100 Subject: [PATCH 051/136] Upload SPeCS logo --- Lara-JS/src-api/visualization/img/specs-logo.png | Bin 0 -> 4759 bytes .../visualization/public/css/color-scheme.css | 2 ++ .../src-api/visualization/public/css/styles.css | 5 ++++- 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 Lara-JS/src-api/visualization/img/specs-logo.png diff --git a/Lara-JS/src-api/visualization/img/specs-logo.png b/Lara-JS/src-api/visualization/img/specs-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..23d45e3c421b11f6eda852aa6c8b80bcab71ab38 GIT binary patch literal 4759 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 k_rd Date: Sun, 14 Jul 2024 21:52:34 +0100 Subject: [PATCH 052/136] Create tool header --- .../visualization/VisualizationTool.ts | 1 - .../visualization/public/css/styles.css | 34 ++++++++++++++---- .../{ => public}/img/specs-logo.png | Bin .../src-api/visualization/public/index.html | 6 +++- .../visualization/VisualizationTool.js | 1 - 5 files changed, 33 insertions(+), 9 deletions(-) rename Lara-JS/src-api/visualization/{ => public}/img/specs-logo.png (100%) diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts index 9a7980d5d..bcd06073a 100644 --- a/Lara-JS/src-api/visualization/VisualizationTool.ts +++ b/Lara-JS/src-api/visualization/VisualizationTool.ts @@ -139,7 +139,6 @@ export default class VisualizationTool { } private static toToolJpJson(jp: LaraJoinPoint): any { - console.log(wrapJoinPoint(jp._javaObject.getAstName()), '=>', wrapJoinPoint(jp._javaObject.getLocation())); return { id: wrapJoinPoint(jp._javaObject.getAstId()), type: wrapJoinPoint(jp._javaObject.getAstName()), diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index bcb6b3729..4defbddb3 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -1,9 +1,3 @@ -@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap'); - -body { - font-family: "Roboto", sans-serif; -} - #ast-code-visualization > article { display: inline-block; } @@ -36,3 +30,31 @@ pre * { /* TODO: Refactor previous code */ +@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap'); + +body { + margin: 0; + + font-family: "Roboto", sans-serif; + color: var(--text-color); +} + + +header { + height: 3em; + padding: 0.5em; + background-color: var(--header-color); + + display: flex; + align-items: center; + justify-content: left; + gap: 1em; +} + +header .specs-logo { + height: 100%; +} + +header h1 { + font-size: 1.5em; +} diff --git a/Lara-JS/src-api/visualization/img/specs-logo.png b/Lara-JS/src-api/visualization/public/img/specs-logo.png similarity index 100% rename from Lara-JS/src-api/visualization/img/specs-logo.png rename to Lara-JS/src-api/visualization/public/img/specs-logo.png diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 1daef5c79..59099eba1 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -3,13 +3,17 @@ + AST Visualization +
+ +

LARA Visualization Tool

+
-

AST Visualization

AST

diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js index efae7599a..4853b0e07 100644 --- a/LaraApi/src-lara/visualization/VisualizationTool.js +++ b/LaraApi/src-lara/visualization/VisualizationTool.js @@ -107,7 +107,6 @@ export default class VisualizationTool { }); } static toToolJpJson(jp) { - console.log(wrapJoinPoint(jp._javaObject.getAstName()), '=>', wrapJoinPoint(jp._javaObject.getLocation())); return { id: wrapJoinPoint(jp._javaObject.getAstId()), type: wrapJoinPoint(jp._javaObject.getAstName()), From bd4a3bbb87e3f45343ecb446d209217fdea5fcb1 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 15 Jul 2024 08:39:25 +0100 Subject: [PATCH 053/136] Add Lucide icon to resume button --- Lara-JS/src-api/visualization/public/css/styles.css | 2 +- Lara-JS/src-api/visualization/public/index.html | 5 ++++- .../visualization/public/svg/lucide-icons/step-forward.svg | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 Lara-JS/src-api/visualization/public/svg/lucide-icons/step-forward.svg diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 4defbddb3..4d39bbc06 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -35,7 +35,7 @@ pre * { body { margin: 0; - font-family: "Roboto", sans-serif; + font-family: 'Roboto', sans-serif; color: var(--text-color); } diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 59099eba1..316e39eac 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -14,6 +14,10 @@

LARA Visualization Tool

+

AST

@@ -22,7 +26,6 @@

AST

Code

-
\ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/svg/lucide-icons/step-forward.svg b/Lara-JS/src-api/visualization/public/svg/lucide-icons/step-forward.svg new file mode 100644 index 000000000..983ba7447 --- /dev/null +++ b/Lara-JS/src-api/visualization/public/svg/lucide-icons/step-forward.svg @@ -0,0 +1 @@ + \ No newline at end of file From b8d44c5711dc10cd0cc72fee0927843425066f00 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 15 Jul 2024 10:12:23 +0100 Subject: [PATCH 054/136] Refactor page layout and style continue button --- .../visualization/public/css/color-scheme.css | 3 +- .../visualization/public/css/styles.css | 62 +++++++++++++++---- .../src-api/visualization/public/index.html | 12 ++-- 3 files changed, 56 insertions(+), 21 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/color-scheme.css b/Lara-JS/src-api/visualization/public/css/color-scheme.css index 81ba2e39a..4e8f73bca 100644 --- a/Lara-JS/src-api/visualization/public/css/color-scheme.css +++ b/Lara-JS/src-api/visualization/public/css/color-scheme.css @@ -17,8 +17,9 @@ --bg-color: var(--white); --text-color: var(--dark-gray); --header-color: var(--lighter-gray); - --divider-color: var(--light-gray); + --divider-color: var(--gray); --icon-color: var(--dark-gray); + --button-bg-color: var(--white); --tab-bg-color: var(--white); --tab-hover-bg-color: var(--lighter-gray); --tab-active-bg-color: var(--light-gray); diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 4d39bbc06..92c5fb26a 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -1,13 +1,4 @@ -#ast-code-visualization > article { - display: inline-block; -} - -#ast-code-visualization > article:not(:first-of-type) { - margin-left: 5em; -} - pre { - width: 50em; height: 80em; overflow: auto; border: 1px solid black; @@ -24,10 +15,6 @@ pre * { content: ' - '; } -#continue-button { - display: block; -} - /* TODO: Refactor previous code */ @import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap'); @@ -39,6 +26,18 @@ body { color: var(--text-color); } +button { + height: 2.5em; + padding: 0 1.25em; + background-color: var(--button-bg-color); + border: 1px solid var(--divider-color); + border-radius: 2px; + + font-size: 1em; + + cursor: pointer; +} + header { height: 3em; @@ -58,3 +57,40 @@ header .specs-logo { header h1 { font-size: 1.5em; } + + +main { + margin: 1em 2em 2em; + background-color: var(--bg-color); + + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-areas: "continue-button code" + "ast code"; + column-gap: 5em; +} + +#continue-button { + width: fit-content; + + display: flex; + justify-content: center; + align-items: center; + gap: 0.5em; + + grid-area: continue-button; +} + +#continue-button img { + width: 1.25em; + height: 1.25em; + color: var(--icon-color); +} + +#ast { + grid-area: ast; +} + +#code { + grid-area: code; +} diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 316e39eac..7e54b3164 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -6,26 +6,24 @@ - AST Visualization + LARA Visualization Tool

LARA Visualization Tool

-
+
-

AST

-

Code

-
+ \ No newline at end of file From bfbcf6e59c75f1ac584a75e3c86a264310dfad7c Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 15 Jul 2024 10:41:35 +0100 Subject: [PATCH 055/136] Fix container sizes --- .../visualization/public/css/styles.css | 36 ++++++++++++------- .../src-api/visualization/public/index.html | 4 +-- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 92c5fb26a..7d006b347 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -1,5 +1,4 @@ pre { - height: 80em; overflow: auto; border: 1px solid black; border-radius: 2px; @@ -20,10 +19,18 @@ pre * { @import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap'); body { + height: 100vh; margin: 0; font-family: 'Roboto', sans-serif; color: var(--text-color); + + display: grid; + grid-template-rows: 4em 1fr; +} + +.icon { + color: var(--icon-color); } button { @@ -38,9 +45,18 @@ button { cursor: pointer; } +button .icon { + width: 1.25em; + height: 1.25em; +} + +pre { + height: 100%; + margin: 0; +} + header { - height: 3em; padding: 0.5em; background-color: var(--header-color); @@ -60,31 +76,27 @@ header h1 { main { - margin: 1em 2em 2em; + padding: 1em 2em 2em; background-color: var(--bg-color); display: grid; grid-template-columns: 1fr 1fr; + grid-template-rows: min-content 1fr; grid-template-areas: "continue-button code" "ast code"; - column-gap: 5em; + gap: 1em; } + #continue-button { width: fit-content; + grid-area: continue-button; + display: flex; justify-content: center; align-items: center; gap: 0.5em; - - grid-area: continue-button; -} - -#continue-button img { - width: 1.25em; - height: 1.25em; - color: var(--icon-color); } #ast { diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 7e54b3164..e116c6bbe 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -10,12 +10,12 @@
- +

LARA Visualization Tool

From 609af91af4c3d1d6af2f19ec8fcbe6395e55f316 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 15 Jul 2024 10:51:55 +0100 Subject: [PATCH 056/136] Refactor container style --- .../visualization/public/css/color-scheme.css | 2 +- .../src-api/visualization/public/css/styles.css | 15 ++++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/color-scheme.css b/Lara-JS/src-api/visualization/public/css/color-scheme.css index 4e8f73bca..7b1713c51 100644 --- a/Lara-JS/src-api/visualization/public/css/color-scheme.css +++ b/Lara-JS/src-api/visualization/public/css/color-scheme.css @@ -17,7 +17,7 @@ --bg-color: var(--white); --text-color: var(--dark-gray); --header-color: var(--lighter-gray); - --divider-color: var(--gray); + --border-color: var(--gray); --icon-color: var(--dark-gray); --button-bg-color: var(--white); --tab-bg-color: var(--white); diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 7d006b347..0f24edf64 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -1,11 +1,3 @@ -pre { - overflow: auto; - border: 1px solid black; - border-radius: 2px; - cursor: default; - user-select: none; -} - pre * { transition: background-color 0.05s linear; } @@ -37,7 +29,7 @@ button { height: 2.5em; padding: 0 1.25em; background-color: var(--button-bg-color); - border: 1px solid var(--divider-color); + border: 1px solid var(--border-color); border-radius: 2px; font-size: 1em; @@ -53,6 +45,11 @@ button .icon { pre { height: 100%; margin: 0; + border: 1px solid var(--border-color); + border-radius: 2px; + + overflow: auto; + user-select: none; } From a52165c6979ebb7c15aa28a4161bb4cbded2aadf Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 15 Jul 2024 11:01:27 +0100 Subject: [PATCH 057/136] Refactor containers --- .../visualization/public/css/styles.css | 21 ++++++++++--------- .../src-api/visualization/public/index.html | 4 ++-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 0f24edf64..2c2f72393 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -42,16 +42,6 @@ button .icon { height: 1.25em; } -pre { - height: 100%; - margin: 0; - border: 1px solid var(--border-color); - border-radius: 2px; - - overflow: auto; - user-select: none; -} - header { padding: 0.5em; @@ -103,3 +93,14 @@ main { #code { grid-area: code; } + + +.container { + height: 100%; + margin: 0; + border: 1px solid var(--border-color); + border-radius: 2px; + + overflow: auto; + user-select: none; +} diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index e116c6bbe..dff2d9a9d 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -19,10 +19,10 @@

LARA Visualization Tool

Continue
-
+
-
+
From f0652fad3342c8763b06bcba48855adbd1b5351e Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 15 Jul 2024 13:48:58 +0100 Subject: [PATCH 058/136] Refactor AST container style --- .../visualization/VisualizationTool.ts | 2 +- .../visualization/public/css/imports.css | 1 + .../visualization/public/css/styles.css | 31 ++++++++++++++++--- .../src-api/visualization/public/index.html | 1 + .../visualization/public/js/ast-import.ts | 28 +++++++++++++---- .../src-api/visualization/public/js/utils.ts | 9 +++++- .../visualization/public/js/visualization.ts | 26 ++++++++++------ .../public/svg/lucide-icons/chevron-down.svg | 1 + .../public/svg/lucide-icons/chevron-right.svg | 1 + .../visualization/VisualizationTool.js | 2 +- .../visualization/public/js/ast-import.js | 22 +++++++++---- .../src-lara/visualization/public/js/utils.js | 8 ++++- .../visualization/public/js/visualization.js | 24 ++++++++------ 13 files changed, 116 insertions(+), 40 deletions(-) create mode 100644 Lara-JS/src-api/visualization/public/css/imports.css create mode 100644 Lara-JS/src-api/visualization/public/svg/lucide-icons/chevron-down.svg create mode 100644 Lara-JS/src-api/visualization/public/svg/lucide-icons/chevron-right.svg diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts index bcd06073a..d7aa39229 100644 --- a/Lara-JS/src-api/visualization/VisualizationTool.ts +++ b/Lara-JS/src-api/visualization/VisualizationTool.ts @@ -145,7 +145,7 @@ export default class VisualizationTool { code: wrapJoinPoint(jp._javaObject.getCode()), children: jp.children .slice() - .sort((a, b) => wrapJoinPoint(a._javaObject.getLocation()).localeCompare(wrapJoinPoint(b._javaObject.getLocation()), 'en', { numeric: true })) + .sort((a, b) => wrapJoinPoint(a._javaObject.getLocation()).localeCompare(wrapJoinPoint(b._javaObject.getLocation()), 'en', { numeric: true })) // TODO: Perform sorting on frontend .map(child => this.toToolJpJson(child)) }; } diff --git a/Lara-JS/src-api/visualization/public/css/imports.css b/Lara-JS/src-api/visualization/public/css/imports.css new file mode 100644 index 000000000..8424c7f10 --- /dev/null +++ b/Lara-JS/src-api/visualization/public/css/imports.css @@ -0,0 +1 @@ +@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap'); diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 2c2f72393..da72d85d3 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -2,13 +2,9 @@ pre * { transition: background-color 0.05s linear; } -.ast-node::before { - content: ' - '; -} -/* TODO: Refactor previous code */ -@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap'); +/* TODO: Refactor previous code */ body { height: 100vh; @@ -33,6 +29,8 @@ button { border-radius: 2px; font-size: 1em; + font-weight: 500; + font-family: inherit; cursor: pointer; } @@ -96,6 +94,7 @@ main { .container { + box-sizing: border-box; height: 100%; margin: 0; border: 1px solid var(--border-color); @@ -103,4 +102,26 @@ main { overflow: auto; user-select: none; + -webkit-user-select: none; +} + +#ast-container { + padding: 0.25em 0.5em; + + font-weight: 500; + + display: flex; + flex-direction: column; + gap: 0; +} + +.ast-node { + width: min-content; + + font-weight: 500; + + display: flex; + justify-content: left; + align-items: center; + gap: 0.25em } diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index dff2d9a9d..294975e83 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -3,6 +3,7 @@ + diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index 831142657..40342adf3 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -1,13 +1,29 @@ -import { escapeHtml, replaceAfter } from './utils.js'; +import { createIcon, escapeHtml, replaceAfter } from './utils.js'; import { addEventListenersToAstNodes } from './visualization.js'; import JoinPoint from './ToolJoinPoint.js'; +const createAstNodeElement = (nodeId: string, indentation: number, text: string): HTMLSpanElement => { + const nodeElement = document.createElement('span'); + nodeElement.classList.add('ast-node'); + + nodeElement.dataset.nodeId = nodeId; + nodeElement.style.marginLeft = (indentation * 2) + 'em'; + + const chevronDownUrl = '/svg/lucide-icons/chevron-down.svg'; + const chevronIcon = createIcon(chevronDownUrl); + + const nodeText = document.createElement('span'); + nodeText.classList.add('ast-node-text'); + nodeText.textContent = text; + + nodeElement.appendChild(chevronIcon); + nodeElement.appendChild(nodeText); + + return nodeElement; +} + const convertAstNodesToElements = (root: JoinPoint, depth: number = 0): HTMLElement[] => { - const rootElement = document.createElement('span'); - rootElement.classList.add('ast-node'); - rootElement.dataset.nodeId = root.id; - rootElement.style.marginLeft = (depth * 2) + 'em'; - rootElement.textContent = root.type; + const rootElement = createAstNodeElement(root.id, depth, root.type); const nodeElements = [rootElement]; for (const node of root.children) { diff --git a/Lara-JS/src-api/visualization/public/js/utils.ts b/Lara-JS/src-api/visualization/public/js/utils.ts index 33ab949e2..2d571ec32 100644 --- a/Lara-JS/src-api/visualization/public/js/utils.ts +++ b/Lara-JS/src-api/visualization/public/js/utils.ts @@ -16,4 +16,11 @@ const replaceAfter = (text: string, search: string, replace: string, start: numb return text.slice(0, index) + replace + text.slice(index + search.length); } -export { escapeHtml, replaceAfter }; \ No newline at end of file +const createIcon = (src: string): HTMLImageElement => { + const icon = document.createElement('img'); + icon.classList.add('icon'); + icon.src = src; + return icon; +} + +export { escapeHtml, replaceAfter, createIcon }; \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index 43fe663ee..c7bd7989a 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -1,13 +1,19 @@ -const getElementsWithNodeId = (id: string): NodeListOf => { - return document.querySelectorAll(`span[data-node-id="${id}"]`); +const getNodeRelatedElements = (nodeId: string): HTMLElement[] => { + return Array.from(document.querySelectorAll(`[data-node-id="${nodeId}"]`)); } -const highlightElements = (elements: NodeListOf): void => { - elements.forEach(element => element.style.backgroundColor = 'yellow'); -}; +const highlightNode = (nodeId: string): void => { + const elements = document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"] .ast-node-text, .node-code[data-node-id="${nodeId}"]`); + for (const element of elements) { + element.style.backgroundColor = 'yellow'; + } +} -const unhighlightElements = (elements: NodeListOf): void => { - elements.forEach(element => element.style.backgroundColor = ''); +const unhighlightNode = (nodeId: string): void => { + const elements = document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"] .ast-node-text, .node-code[data-node-id="${nodeId}"]`); + for (const element of elements) { + element.style.backgroundColor = ''; + } } const addEventListenersToAstNodes = (nodes: HTMLElement[]): void => { @@ -18,15 +24,15 @@ const addEventListenersToAstNodes = (nodes: HTMLElement[]): void => { } const nodeId = nodeElement.dataset.nodeId!; - const nodeRelatedElements = getElementsWithNodeId(nodeId); + const nodeRelatedElements = getNodeRelatedElements(nodeId); for (const nodeRelatedElement of nodeRelatedElements) { nodeRelatedElement.addEventListener('mouseover', event => { - highlightElements(nodeRelatedElements); + highlightNode(nodeId); event.stopPropagation(); }); nodeRelatedElement.addEventListener('mouseout', event => { - unhighlightElements(nodeRelatedElements); + unhighlightNode(nodeId); event.stopPropagation(); }); } diff --git a/Lara-JS/src-api/visualization/public/svg/lucide-icons/chevron-down.svg b/Lara-JS/src-api/visualization/public/svg/lucide-icons/chevron-down.svg new file mode 100644 index 000000000..b26052518 --- /dev/null +++ b/Lara-JS/src-api/visualization/public/svg/lucide-icons/chevron-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/svg/lucide-icons/chevron-right.svg b/Lara-JS/src-api/visualization/public/svg/lucide-icons/chevron-right.svg new file mode 100644 index 000000000..387517288 --- /dev/null +++ b/Lara-JS/src-api/visualization/public/svg/lucide-icons/chevron-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js index 4853b0e07..3f203c384 100644 --- a/LaraApi/src-lara/visualization/VisualizationTool.js +++ b/LaraApi/src-lara/visualization/VisualizationTool.js @@ -113,7 +113,7 @@ export default class VisualizationTool { code: wrapJoinPoint(jp._javaObject.getCode()), children: jp.children .slice() - .sort((a, b) => wrapJoinPoint(a._javaObject.getLocation()).localeCompare(wrapJoinPoint(b._javaObject.getLocation()), 'en', { numeric: true })) + .sort((a, b) => wrapJoinPoint(a._javaObject.getLocation()).localeCompare(wrapJoinPoint(b._javaObject.getLocation()), 'en', { numeric: true })) // TODO: Perform sorting on frontend .map(child => this.toToolJpJson(child)) }; } diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index 12af80e55..706873f91 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -1,11 +1,21 @@ -import { escapeHtml, replaceAfter } from './utils.js'; +import { createIcon, escapeHtml, replaceAfter } from './utils.js'; import { addEventListenersToAstNodes } from './visualization.js'; +const createAstNodeElement = (nodeId, indentation, text) => { + const nodeElement = document.createElement('span'); + nodeElement.classList.add('ast-node'); + nodeElement.dataset.nodeId = nodeId; + nodeElement.style.marginLeft = (indentation * 2) + 'em'; + const chevronDownUrl = '/svg/lucide-icons/chevron-down.svg'; + const chevronIcon = createIcon(chevronDownUrl); + const nodeText = document.createElement('span'); + nodeText.classList.add('ast-node-text'); + nodeText.textContent = text; + nodeElement.appendChild(chevronIcon); + nodeElement.appendChild(nodeText); + return nodeElement; +}; const convertAstNodesToElements = (root, depth = 0) => { - const rootElement = document.createElement('span'); - rootElement.classList.add('ast-node'); - rootElement.dataset.nodeId = root.id; - rootElement.style.marginLeft = (depth * 2) + 'em'; - rootElement.textContent = root.type; + const rootElement = createAstNodeElement(root.id, depth, root.type); const nodeElements = [rootElement]; for (const node of root.children) { nodeElements.push(...convertAstNodesToElements(node, depth + 1)); diff --git a/LaraApi/src-lara/visualization/public/js/utils.js b/LaraApi/src-lara/visualization/public/js/utils.js index 1912041ba..c13edd781 100644 --- a/LaraApi/src-lara/visualization/public/js/utils.js +++ b/LaraApi/src-lara/visualization/public/js/utils.js @@ -13,5 +13,11 @@ const replaceAfter = (text, search, replace, start) => { } return text.slice(0, index) + replace + text.slice(index + search.length); }; -export { escapeHtml, replaceAfter }; +const createIcon = (src) => { + const icon = document.createElement('img'); + icon.classList.add('icon'); + icon.src = src; + return icon; +}; +export { escapeHtml, replaceAfter, createIcon }; //# sourceMappingURL=utils.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index f671cb218..ee1191be2 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -1,11 +1,17 @@ -const getElementsWithNodeId = (id) => { - return document.querySelectorAll(`span[data-node-id="${id}"]`); +const getNodeRelatedElements = (nodeId) => { + return Array.from(document.querySelectorAll(`[data-node-id="${nodeId}"]`)); }; -const highlightElements = (elements) => { - elements.forEach(element => element.style.backgroundColor = 'yellow'); +const highlightNode = (nodeId) => { + const elements = document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"] .ast-node-text, .node-code[data-node-id="${nodeId}"]`); + for (const element of elements) { + element.style.backgroundColor = 'yellow'; + } }; -const unhighlightElements = (elements) => { - elements.forEach(element => element.style.backgroundColor = ''); +const unhighlightNode = (nodeId) => { + const elements = document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"] .ast-node-text, .node-code[data-node-id="${nodeId}"]`); + for (const element of elements) { + element.style.backgroundColor = ''; + } }; const addEventListenersToAstNodes = (nodes) => { for (const nodeElement of nodes) { @@ -14,14 +20,14 @@ const addEventListenersToAstNodes = (nodes) => { continue; } const nodeId = nodeElement.dataset.nodeId; - const nodeRelatedElements = getElementsWithNodeId(nodeId); + const nodeRelatedElements = getNodeRelatedElements(nodeId); for (const nodeRelatedElement of nodeRelatedElements) { nodeRelatedElement.addEventListener('mouseover', event => { - highlightElements(nodeRelatedElements); + highlightNode(nodeId); event.stopPropagation(); }); nodeRelatedElement.addEventListener('mouseout', event => { - unhighlightElements(nodeRelatedElements); + unhighlightNode(nodeId); event.stopPropagation(); }); } From 20848ce4efc4ffac6d67c7850fd294b0dd1ec99f Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 15 Jul 2024 15:07:30 +0100 Subject: [PATCH 059/136] Group child node elements in dropdowns --- .../visualization/public/css/styles.css | 19 +++++--- .../visualization/public/js/ToolJoinPoint.ts | 6 +-- .../visualization/public/js/ast-import.ts | 47 ++++++++++--------- .../src-api/visualization/public/js/utils.ts | 7 +-- .../visualization/public/js/visualization.ts | 37 +++++++-------- .../visualization/public/js/ToolJoinPoint.js | 3 +- .../visualization/public/js/ast-import.js | 42 ++++++++--------- .../src-lara/visualization/public/js/utils.js | 7 +-- .../visualization/public/js/visualization.js | 33 ++++++------- 9 files changed, 99 insertions(+), 102 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index da72d85d3..1f402757b 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -2,8 +2,6 @@ pre * { transition: background-color 0.05s linear; } - - /* TODO: Refactor previous code */ body { @@ -48,7 +46,7 @@ header { display: flex; align-items: center; justify-content: left; - gap: 1em; + gap: 0.5em; } header .specs-logo { @@ -109,13 +107,11 @@ main { padding: 0.25em 0.5em; font-weight: 500; - - display: flex; - flex-direction: column; - gap: 0; + line-height: 1.25em; } .ast-node { + padding-top: 0.25em; width: min-content; font-weight: 500; @@ -125,3 +121,12 @@ main { align-items: center; gap: 0.25em } + +.ast-node .icon { + width: 1.25em; + height: 1.25em; +} + +.ast-node-dropdown { + padding-left: 2em; +} diff --git a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts index 1d7d85c1b..60a553268 100644 --- a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts +++ b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts @@ -1,4 +1,4 @@ -class ToolJoinPoint { +export default class ToolJoinPoint { id: string; type: string; code: string; @@ -23,6 +23,4 @@ class ToolJoinPoint { public clone(): ToolJoinPoint { return new ToolJoinPoint(this.id, this.type, this.code, this.children.map(child => child.clone())); } -}; - -export default ToolJoinPoint; \ No newline at end of file +}; \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index 40342adf3..e049bfeca 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -1,16 +1,14 @@ -import { createIcon, escapeHtml, replaceAfter } from './utils.js'; +import { createLucideIcon, escapeHtml, replaceAfter } from './utils.js'; import { addEventListenersToAstNodes } from './visualization.js'; import JoinPoint from './ToolJoinPoint.js'; -const createAstNodeElement = (nodeId: string, indentation: number, text: string): HTMLSpanElement => { +const createAstNodeElement = (nodeId: string, text: string): HTMLSpanElement => { const nodeElement = document.createElement('span'); nodeElement.classList.add('ast-node'); nodeElement.dataset.nodeId = nodeId; - nodeElement.style.marginLeft = (indentation * 2) + 'em'; - const chevronDownUrl = '/svg/lucide-icons/chevron-down.svg'; - const chevronIcon = createIcon(chevronDownUrl); + const chevronIcon = createLucideIcon('chevron-down'); const nodeText = document.createElement('span'); nodeText.classList.add('ast-node-text'); @@ -22,25 +20,29 @@ const createAstNodeElement = (nodeId: string, indentation: number, text: string) return nodeElement; } -const convertAstNodesToElements = (root: JoinPoint, depth: number = 0): HTMLElement[] => { - const rootElement = createAstNodeElement(root.id, depth, root.type); +const createAstNodeDropdown = (nodeId: string): HTMLDivElement => { + const dropdown = document.createElement('div'); + dropdown.classList.add('ast-node-dropdown'); + dropdown.dataset.nodeId = nodeId; + + return dropdown; +} + +const convertAstNodeToHtml = (root: JoinPoint): DocumentFragment => { + const rootElement = createAstNodeElement(root.id, root.type); + const rootDropdown = createAstNodeDropdown(root.id); - const nodeElements = [rootElement]; for (const node of root.children) { - nodeElements.push(...convertAstNodesToElements(node, depth + 1)); + const descendantNodeElements = convertAstNodeToHtml(node); + rootDropdown.appendChild(descendantNodeElements); } - return nodeElements; -}; - -const fillAstContainer = (nodeElements: HTMLElement[], astContainer: HTMLElement): void => { - astContainer.innerHTML = ''; + const fragment = new DocumentFragment(); + fragment.appendChild(rootElement); + fragment.appendChild(rootDropdown); - for (const nodeElement of nodeElements) { - astContainer.appendChild(nodeElement); - astContainer.appendChild(document.createElement('br')); - } -} + return fragment; +}; const addIdentation = (code: string, indentation: number): string => { return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); @@ -90,7 +92,6 @@ const linkCodeToAstNodes = (root: JoinPoint, codeContainer: HTMLElement, codeSta nodeCodeWrapper.dataset.nodeId = root.id.toString(); nodeCodeWrapper.innerHTML = nodeCodeHtml; codeContainer.innerHTML = replaceAfter(codeContainer.innerHTML, nodeCodeHtml, nodeCodeWrapper.outerHTML, codeStart); - // TODO: Associate only the real match (this associates all code fragments that are identical to the node code) const nodeCodeContainer = codeContainer.querySelector(`span.node-code[data-node-id="${root.id}"]`); let nodeCodeLowerBound = 0; @@ -110,10 +111,10 @@ const importAst = (astRoot: JoinPoint, astContainer: HTMLElement, codeContainer: const refinedAstRoot = astRoot.clone(); refineAst(refinedAstRoot); - const nodeElements = convertAstNodesToElements(refinedAstRoot); - fillAstContainer(nodeElements, astContainer); + const astFragment = convertAstNodeToHtml(refinedAstRoot); + astContainer.appendChild(astFragment); linkCodeToAstNodes(refinedAstRoot, codeContainer); - addEventListenersToAstNodes(nodeElements); + addEventListenersToAstNodes(refinedAstRoot); } export { importCode, importAst }; diff --git a/Lara-JS/src-api/visualization/public/js/utils.ts b/Lara-JS/src-api/visualization/public/js/utils.ts index 2d571ec32..8a8149c64 100644 --- a/Lara-JS/src-api/visualization/public/js/utils.ts +++ b/Lara-JS/src-api/visualization/public/js/utils.ts @@ -16,11 +16,12 @@ const replaceAfter = (text: string, search: string, replace: string, start: numb return text.slice(0, index) + replace + text.slice(index + search.length); } -const createIcon = (src: string): HTMLImageElement => { +const createLucideIcon = (name: string): HTMLImageElement => { const icon = document.createElement('img'); icon.classList.add('icon'); - icon.src = src; + icon.src = `/svg/lucide-icons/${name}.svg`; + icon.alt = `${name}-icon`; return icon; } -export { escapeHtml, replaceAfter, createIcon }; \ No newline at end of file +export { escapeHtml, replaceAfter, createLucideIcon }; \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index c7bd7989a..a3802f807 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -1,5 +1,7 @@ +import JoinPoint from "./ToolJoinPoint.js"; + const getNodeRelatedElements = (nodeId: string): HTMLElement[] => { - return Array.from(document.querySelectorAll(`[data-node-id="${nodeId}"]`)); + return Array.from(document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"], .node-code[data-node-id="${nodeId}"]`)); } const highlightNode = (nodeId: string): void => { @@ -16,27 +18,22 @@ const unhighlightNode = (nodeId: string): void => { } } -const addEventListenersToAstNodes = (nodes: HTMLElement[]): void => { - for (const nodeElement of nodes) { - if (!nodeElement.dataset.nodeId) { - console.warn('Node element does not have a data-node-id attribute'); - continue; - } - - const nodeId = nodeElement.dataset.nodeId!; - const nodeRelatedElements = getNodeRelatedElements(nodeId); +const addEventListenersToAstNodes = (root: JoinPoint): void => { + const nodeId = root.id; + const nodeRelatedElements = getNodeRelatedElements(nodeId); - for (const nodeRelatedElement of nodeRelatedElements) { - nodeRelatedElement.addEventListener('mouseover', event => { - highlightNode(nodeId); - event.stopPropagation(); - }); - nodeRelatedElement.addEventListener('mouseout', event => { - unhighlightNode(nodeId); - event.stopPropagation(); - }); - } + for (const nodeRelatedElement of nodeRelatedElements) { + nodeRelatedElement.addEventListener('mouseover', event => { + highlightNode(nodeId); + event.stopPropagation(); + }); + nodeRelatedElement.addEventListener('mouseout', event => { + unhighlightNode(nodeId); + event.stopPropagation(); + }); } + + root.children.forEach(child => addEventListenersToAstNodes(child)); }; export { addEventListenersToAstNodes }; diff --git a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js index 45a32640b..b606dfca5 100644 --- a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js +++ b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js @@ -1,4 +1,4 @@ -class ToolJoinPoint { +export default class ToolJoinPoint { id; type; code; @@ -17,5 +17,4 @@ class ToolJoinPoint { } } ; -export default ToolJoinPoint; //# sourceMappingURL=ToolJoinPoint.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index 706873f91..7fb6df140 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -1,12 +1,10 @@ -import { createIcon, escapeHtml, replaceAfter } from './utils.js'; +import { createLucideIcon, escapeHtml, replaceAfter } from './utils.js'; import { addEventListenersToAstNodes } from './visualization.js'; -const createAstNodeElement = (nodeId, indentation, text) => { +const createAstNodeElement = (nodeId, text) => { const nodeElement = document.createElement('span'); nodeElement.classList.add('ast-node'); nodeElement.dataset.nodeId = nodeId; - nodeElement.style.marginLeft = (indentation * 2) + 'em'; - const chevronDownUrl = '/svg/lucide-icons/chevron-down.svg'; - const chevronIcon = createIcon(chevronDownUrl); + const chevronIcon = createLucideIcon('chevron-down'); const nodeText = document.createElement('span'); nodeText.classList.add('ast-node-text'); nodeText.textContent = text; @@ -14,20 +12,23 @@ const createAstNodeElement = (nodeId, indentation, text) => { nodeElement.appendChild(nodeText); return nodeElement; }; -const convertAstNodesToElements = (root, depth = 0) => { - const rootElement = createAstNodeElement(root.id, depth, root.type); - const nodeElements = [rootElement]; - for (const node of root.children) { - nodeElements.push(...convertAstNodesToElements(node, depth + 1)); - } - return nodeElements; +const createAstNodeDropdown = (nodeId) => { + const dropdown = document.createElement('div'); + dropdown.classList.add('ast-node-dropdown'); + dropdown.dataset.nodeId = nodeId; + return dropdown; }; -const fillAstContainer = (nodeElements, astContainer) => { - astContainer.innerHTML = ''; - for (const nodeElement of nodeElements) { - astContainer.appendChild(nodeElement); - astContainer.appendChild(document.createElement('br')); +const convertAstNodeToHtml = (root) => { + const rootElement = createAstNodeElement(root.id, root.type); + const rootDropdown = createAstNodeDropdown(root.id); + for (const node of root.children) { + const descendantNodeElements = convertAstNodeToHtml(node); + rootDropdown.appendChild(descendantNodeElements); } + const fragment = new DocumentFragment(); + fragment.appendChild(rootElement); + fragment.appendChild(rootDropdown); + return fragment; }; const addIdentation = (code, indentation) => { return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); @@ -69,7 +70,6 @@ const linkCodeToAstNodes = (root, codeContainer, codeStart = 0) => { nodeCodeWrapper.dataset.nodeId = root.id.toString(); nodeCodeWrapper.innerHTML = nodeCodeHtml; codeContainer.innerHTML = replaceAfter(codeContainer.innerHTML, nodeCodeHtml, nodeCodeWrapper.outerHTML, codeStart); - // TODO: Associate only the real match (this associates all code fragments that are identical to the node code) const nodeCodeContainer = codeContainer.querySelector(`span.node-code[data-node-id="${root.id}"]`); let nodeCodeLowerBound = 0; for (const child of root.children) { @@ -84,10 +84,10 @@ const importCode = (astRoot, codeContainer) => { const importAst = (astRoot, astContainer, codeContainer) => { const refinedAstRoot = astRoot.clone(); refineAst(refinedAstRoot); - const nodeElements = convertAstNodesToElements(refinedAstRoot); - fillAstContainer(nodeElements, astContainer); + const astFragment = convertAstNodeToHtml(refinedAstRoot); + astContainer.appendChild(astFragment); linkCodeToAstNodes(refinedAstRoot, codeContainer); - addEventListenersToAstNodes(nodeElements); + addEventListenersToAstNodes(refinedAstRoot); }; export { importCode, importAst }; //# sourceMappingURL=ast-import.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/utils.js b/LaraApi/src-lara/visualization/public/js/utils.js index c13edd781..1c46bf6c1 100644 --- a/LaraApi/src-lara/visualization/public/js/utils.js +++ b/LaraApi/src-lara/visualization/public/js/utils.js @@ -13,11 +13,12 @@ const replaceAfter = (text, search, replace, start) => { } return text.slice(0, index) + replace + text.slice(index + search.length); }; -const createIcon = (src) => { +const createLucideIcon = (name) => { const icon = document.createElement('img'); icon.classList.add('icon'); - icon.src = src; + icon.src = `/svg/lucide-icons/${name}.svg`; + icon.alt = `${name}-icon`; return icon; }; -export { escapeHtml, replaceAfter, createIcon }; +export { escapeHtml, replaceAfter, createLucideIcon }; //# sourceMappingURL=utils.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index ee1191be2..0076bc4d0 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -1,5 +1,5 @@ const getNodeRelatedElements = (nodeId) => { - return Array.from(document.querySelectorAll(`[data-node-id="${nodeId}"]`)); + return Array.from(document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"], .node-code[data-node-id="${nodeId}"]`)); }; const highlightNode = (nodeId) => { const elements = document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"] .ast-node-text, .node-code[data-node-id="${nodeId}"]`); @@ -13,25 +13,20 @@ const unhighlightNode = (nodeId) => { element.style.backgroundColor = ''; } }; -const addEventListenersToAstNodes = (nodes) => { - for (const nodeElement of nodes) { - if (!nodeElement.dataset.nodeId) { - console.warn('Node element does not have a data-node-id attribute'); - continue; - } - const nodeId = nodeElement.dataset.nodeId; - const nodeRelatedElements = getNodeRelatedElements(nodeId); - for (const nodeRelatedElement of nodeRelatedElements) { - nodeRelatedElement.addEventListener('mouseover', event => { - highlightNode(nodeId); - event.stopPropagation(); - }); - nodeRelatedElement.addEventListener('mouseout', event => { - unhighlightNode(nodeId); - event.stopPropagation(); - }); - } +const addEventListenersToAstNodes = (root) => { + const nodeId = root.id; + const nodeRelatedElements = getNodeRelatedElements(nodeId); + for (const nodeRelatedElement of nodeRelatedElements) { + nodeRelatedElement.addEventListener('mouseover', event => { + highlightNode(nodeId); + event.stopPropagation(); + }); + nodeRelatedElement.addEventListener('mouseout', event => { + unhighlightNode(nodeId); + event.stopPropagation(); + }); } + root.children.forEach(child => addEventListenersToAstNodes(child)); }; export { addEventListenersToAstNodes }; //# sourceMappingURL=visualization.js.map \ No newline at end of file From f3df0086f9e0f9aa2c8f40f3f3e687a92bae57a1 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 15 Jul 2024 15:24:10 +0100 Subject: [PATCH 060/136] Make dropdown collapsable --- .../visualization/public/js/visualization.ts | 20 ++++++++++++++++++- .../visualization/public/js/visualization.js | 15 ++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index a3802f807..797195c47 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -1,5 +1,13 @@ import JoinPoint from "./ToolJoinPoint.js"; +const getNodeElement = (nodeId: string): HTMLSpanElement | null => { + return document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); +} + +const getNodeDropdown = (nodeId: string): HTMLDivElement | null => { + return document.querySelector(`.ast-node-dropdown[data-node-id="${nodeId}"]`); +} + const getNodeRelatedElements = (nodeId: string): HTMLElement[] => { return Array.from(document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"], .node-code[data-node-id="${nodeId}"]`)); } @@ -20,8 +28,18 @@ const unhighlightNode = (nodeId: string): void => { const addEventListenersToAstNodes = (root: JoinPoint): void => { const nodeId = root.id; - const nodeRelatedElements = getNodeRelatedElements(nodeId); + const nodeElement = getNodeElement(nodeId)!; + const [nodeChevron, nodeText] = nodeElement.children; + + const nodeDropdown = getNodeDropdown(nodeId)!; + let nodeCollapsed = false; + nodeChevron.addEventListener('click', event => { + nodeCollapsed = !nodeCollapsed; + nodeDropdown.style.display = nodeCollapsed ? 'none' : 'block'; + event.stopPropagation(); + }); + const nodeRelatedElements = getNodeRelatedElements(nodeId); for (const nodeRelatedElement of nodeRelatedElements) { nodeRelatedElement.addEventListener('mouseover', event => { highlightNode(nodeId); diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index 0076bc4d0..edb11e942 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -1,3 +1,9 @@ +const getNodeElement = (nodeId) => { + return document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); +}; +const getNodeDropdown = (nodeId) => { + return document.querySelector(`.ast-node-dropdown[data-node-id="${nodeId}"]`); +}; const getNodeRelatedElements = (nodeId) => { return Array.from(document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"], .node-code[data-node-id="${nodeId}"]`)); }; @@ -15,6 +21,15 @@ const unhighlightNode = (nodeId) => { }; const addEventListenersToAstNodes = (root) => { const nodeId = root.id; + const nodeElement = getNodeElement(nodeId); + const [nodeChevron, nodeText] = nodeElement.children; + const nodeDropdown = getNodeDropdown(nodeId); + let nodeCollapsed = false; + nodeChevron.addEventListener('click', event => { + nodeCollapsed = !nodeCollapsed; + nodeDropdown.style.display = nodeCollapsed ? 'none' : 'block'; + event.stopPropagation(); + }); const nodeRelatedElements = getNodeRelatedElements(nodeId); for (const nodeRelatedElement of nodeRelatedElements) { nodeRelatedElement.addEventListener('mouseover', event => { From 92566fb9f374957b51108b3abc77b3cec2dab29b Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 15 Jul 2024 15:57:31 +0100 Subject: [PATCH 061/136] Stylish node dropdown buttons --- Lara-JS/src-api/visualization/public/css/styles.css | 11 +++++++++-- Lara-JS/src-api/visualization/public/js/ast-import.ts | 7 +++++-- .../src-api/visualization/public/js/visualization.ts | 9 ++++++--- .../src-lara/visualization/public/js/ast-import.js | 6 ++++-- .../src-lara/visualization/public/js/visualization.js | 7 ++++--- 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 1f402757b..1a940b520 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -122,9 +122,16 @@ main { gap: 0.25em } -.ast-node .icon { - width: 1.25em; +.ast-node button { height: 1.25em; + padding: 0; + background-color: var(--bg-color); + border: none; + border-radius: 50%; +} + +.ast-node button > .icon { + height: 100%; } .ast-node-dropdown { diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index e049bfeca..27095d5cb 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -8,13 +8,14 @@ const createAstNodeElement = (nodeId: string, text: string): HTMLSpanElement => nodeElement.dataset.nodeId = nodeId; - const chevronIcon = createLucideIcon('chevron-down'); + const chevronDropdownButton = document.createElement('button'); + chevronDropdownButton.appendChild(createLucideIcon('chevron-down')); const nodeText = document.createElement('span'); nodeText.classList.add('ast-node-text'); nodeText.textContent = text; - nodeElement.appendChild(chevronIcon); + nodeElement.appendChild(chevronDropdownButton); nodeElement.appendChild(nodeText); return nodeElement; @@ -112,7 +113,9 @@ const importAst = (astRoot: JoinPoint, astContainer: HTMLElement, codeContainer: refineAst(refinedAstRoot); const astFragment = convertAstNodeToHtml(refinedAstRoot); + astContainer.innerHTML = ''; astContainer.appendChild(astFragment); + linkCodeToAstNodes(refinedAstRoot, codeContainer); addEventListenersToAstNodes(refinedAstRoot); } diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index 797195c47..32c9631d8 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -29,14 +29,17 @@ const unhighlightNode = (nodeId: string): void => { const addEventListenersToAstNodes = (root: JoinPoint): void => { const nodeId = root.id; const nodeElement = getNodeElement(nodeId)!; - const [nodeChevron, nodeText] = nodeElement.children; + const nodeDropdownButton = nodeElement.children[0] as HTMLButtonElement; const nodeDropdown = getNodeDropdown(nodeId)!; let nodeCollapsed = false; - nodeChevron.addEventListener('click', event => { + + nodeDropdownButton.addEventListener('click', () => { nodeCollapsed = !nodeCollapsed; nodeDropdown.style.display = nodeCollapsed ? 'none' : 'block'; - event.stopPropagation(); + + const chevron = nodeDropdownButton.children[0] as HTMLImageElement; + chevron.src = `/svg/lucide-icons/chevron-${nodeCollapsed ? 'right' : 'down'}.svg`; }); const nodeRelatedElements = getNodeRelatedElements(nodeId); diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index 7fb6df140..47c349f8c 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -4,11 +4,12 @@ const createAstNodeElement = (nodeId, text) => { const nodeElement = document.createElement('span'); nodeElement.classList.add('ast-node'); nodeElement.dataset.nodeId = nodeId; - const chevronIcon = createLucideIcon('chevron-down'); + const chevronDropdownButton = document.createElement('button'); + chevronDropdownButton.appendChild(createLucideIcon('chevron-down')); const nodeText = document.createElement('span'); nodeText.classList.add('ast-node-text'); nodeText.textContent = text; - nodeElement.appendChild(chevronIcon); + nodeElement.appendChild(chevronDropdownButton); nodeElement.appendChild(nodeText); return nodeElement; }; @@ -85,6 +86,7 @@ const importAst = (astRoot, astContainer, codeContainer) => { const refinedAstRoot = astRoot.clone(); refineAst(refinedAstRoot); const astFragment = convertAstNodeToHtml(refinedAstRoot); + astContainer.innerHTML = ''; astContainer.appendChild(astFragment); linkCodeToAstNodes(refinedAstRoot, codeContainer); addEventListenersToAstNodes(refinedAstRoot); diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index edb11e942..756fc5a60 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -22,13 +22,14 @@ const unhighlightNode = (nodeId) => { const addEventListenersToAstNodes = (root) => { const nodeId = root.id; const nodeElement = getNodeElement(nodeId); - const [nodeChevron, nodeText] = nodeElement.children; + const nodeDropdownButton = nodeElement.children[0]; const nodeDropdown = getNodeDropdown(nodeId); let nodeCollapsed = false; - nodeChevron.addEventListener('click', event => { + nodeDropdownButton.addEventListener('click', () => { nodeCollapsed = !nodeCollapsed; nodeDropdown.style.display = nodeCollapsed ? 'none' : 'block'; - event.stopPropagation(); + const chevron = nodeDropdownButton.children[0]; + chevron.src = `/svg/lucide-icons/chevron-${nodeCollapsed ? 'right' : 'down'}.svg`; }); const nodeRelatedElements = getNodeRelatedElements(nodeId); for (const nodeRelatedElement of nodeRelatedElements) { From 11863d277c1b36044d8412c07759043d5d821838 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 15 Jul 2024 16:11:34 +0100 Subject: [PATCH 062/136] Fix invalid HTML --- Lara-JS/src-api/visualization/public/css/styles.css | 4 ++-- Lara-JS/src-api/visualization/public/index.html | 10 +++------- Lara-JS/src-api/visualization/public/js/main.ts | 2 +- LaraApi/src-lara/visualization/public/js/main.js | 2 +- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 1a940b520..e3a99cb5e 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -82,11 +82,11 @@ main { gap: 0.5em; } -#ast { +#ast-container { grid-area: ast; } -#code { +#code-container { grid-area: code; } diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 294975e83..d21705771 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -6,7 +6,7 @@ - + LARA Visualization Tool @@ -19,12 +19,8 @@

LARA Visualization Tool

Step Forward Icon Continue -
-
-
-
-
-
+
+
\ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/main.ts b/Lara-JS/src-api/visualization/public/js/main.ts index e0377115c..99b9c0501 100644 --- a/Lara-JS/src-api/visualization/public/js/main.ts +++ b/Lara-JS/src-api/visualization/public/js/main.ts @@ -2,7 +2,7 @@ import { continueButtonOnClick, getWebSocket, webSocketOnMessage } from "./commu (() => { const astContainer = document.querySelector('#ast-container'); - const codeContainer = document.querySelector('#code-container'); + const codeContainer = document.querySelector('#code-container code'); const continueButton = document.querySelector('#continue-button'); if (!astContainer || !codeContainer || !continueButton) { diff --git a/LaraApi/src-lara/visualization/public/js/main.js b/LaraApi/src-lara/visualization/public/js/main.js index 186e2682d..4e26ce697 100644 --- a/LaraApi/src-lara/visualization/public/js/main.js +++ b/LaraApi/src-lara/visualization/public/js/main.js @@ -1,7 +1,7 @@ import { continueButtonOnClick, getWebSocket, webSocketOnMessage } from "./communication.js"; (() => { const astContainer = document.querySelector('#ast-container'); - const codeContainer = document.querySelector('#code-container'); + const codeContainer = document.querySelector('#code-container code'); const continueButton = document.querySelector('#continue-button'); if (!astContainer || !codeContainer || !continueButton) { console.error('Required elements not found'); From 5a7a106bc3482c9b9b84b631bbee9c140aa57cc6 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 15 Jul 2024 16:16:49 +0100 Subject: [PATCH 063/136] Finish old CSS refactor --- Lara-JS/src-api/visualization/public/css/styles.css | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index e3a99cb5e..666ba3b04 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -1,9 +1,3 @@ -pre * { - transition: background-color 0.05s linear; -} - -/* TODO: Refactor previous code */ - body { height: 100vh; margin: 0; @@ -103,6 +97,11 @@ main { -webkit-user-select: none; } +.container * { + transition: background-color 0.05s linear; +} + + #ast-container { padding: 0.25em 0.5em; From d7e45eb2018ef9fbb88a8d722d42ed4364fe5970 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 15 Jul 2024 16:46:33 +0100 Subject: [PATCH 064/136] Fix container size --- Lara-JS/src-api/visualization/public/css/styles.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 666ba3b04..dee18bf7f 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -6,7 +6,7 @@ body { color: var(--text-color); display: grid; - grid-template-rows: 4em 1fr; + grid-template-rows: 4em calc(100vh - 4em); /* To force the containers to not span beyond the window size */ } .icon { @@ -92,6 +92,9 @@ main { border: 1px solid var(--border-color); border-radius: 2px; + display: flex; + flex-direction: column; + overflow: auto; user-select: none; -webkit-user-select: none; From 05ca470d8213731a8ad47257a93195b2d471e4cf Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 16 Jul 2024 09:16:52 +0100 Subject: [PATCH 065/136] Restylish code container --- Lara-JS/package.json | 2 ++ .../visualization/public/css/color-scheme.css | 3 +++ .../visualization/public/css/imports.css | 1 + .../visualization/public/css/styles.css | 21 +++++++++++++++++-- .../src-api/visualization/public/index.html | 6 +++++- .../visualization/public/js/ast-import.ts | 21 ++++++++++++------- .../src-api/visualization/public/js/main.ts | 2 +- .../src-api/visualization/public/js/utils.ts | 11 +++++++++- .../visualization/public/js/ast-import.js | 20 +++++++++++------- .../src-lara/visualization/public/js/main.js | 2 +- .../src-lara/visualization/public/js/utils.js | 10 ++++++++- 11 files changed, 76 insertions(+), 23 deletions(-) diff --git a/Lara-JS/package.json b/Lara-JS/package.json index dfa1cdf50..9747886d4 100644 --- a/Lara-JS/package.json +++ b/Lara-JS/package.json @@ -67,9 +67,11 @@ "devDependencies": { "@jest/globals": "^29.7.0", "@types/debug": "^4.1.12", + "@types/express": "^4.17.21", "@types/java": "^0.9.5", "@types/jest": "^29.5.12", "@types/node": "^20.14.10", + "@types/ws": "^8.5.11", "@types/yargs": "^17.0.32", "typescript-eslint": "^8.0.0-alpha.44", "cross-env": "^7.0.3", diff --git a/Lara-JS/src-api/visualization/public/css/color-scheme.css b/Lara-JS/src-api/visualization/public/css/color-scheme.css index 7b1713c51..89e22bef8 100644 --- a/Lara-JS/src-api/visualization/public/css/color-scheme.css +++ b/Lara-JS/src-api/visualization/public/css/color-scheme.css @@ -23,6 +23,9 @@ --tab-bg-color: var(--white); --tab-hover-bg-color: var(--lighter-gray); --tab-active-bg-color: var(--light-gray); + --highlight-color: var(--light-blue); --secondary-highlight-color: var(--transparent-light-blue); + + --line-num-color: var(--gray); } \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/css/imports.css b/Lara-JS/src-api/visualization/public/css/imports.css index 8424c7f10..06a908039 100644 --- a/Lara-JS/src-api/visualization/public/css/imports.css +++ b/Lara-JS/src-api/visualization/public/css/imports.css @@ -1 +1,2 @@ @import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap'); \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index dee18bf7f..8fca192c8 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -88,6 +88,7 @@ main { .container { box-sizing: border-box; height: 100%; + padding: 0.25em 0.5em; margin: 0; border: 1px solid var(--border-color); border-radius: 2px; @@ -106,8 +107,6 @@ main { #ast-container { - padding: 0.25em 0.5em; - font-weight: 500; line-height: 1.25em; } @@ -139,3 +138,21 @@ main { .ast-node-dropdown { padding-left: 2em; } + + +#code-container { + font-family: "JetBrains Mono", monospace; + font-optical-sizing: auto; + + display: grid; + grid-template-columns: auto 1fr; + gap: 1em; +} + +#code-container code { + font-family: inherit; +} + +#code-container .lines { + color: var(--line-num-color); +} diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index d21705771..ea7eba0aa 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -6,6 +6,7 @@ + LARA Visualization Tool @@ -20,7 +21,10 @@

LARA Visualization Tool

Continue
-
+
+            
+ +
\ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index 27095d5cb..37d2a666e 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -1,4 +1,4 @@ -import { createLucideIcon, escapeHtml, replaceAfter } from './utils.js'; +import { countChar, createLucideIcon, escapeHtml, replaceAfter } from './utils.js'; import { addEventListenersToAstNodes } from './visualization.js'; import JoinPoint from './ToolJoinPoint.js'; @@ -73,7 +73,7 @@ const refineAst = (root: JoinPoint, indentation: number = 0): void => { } } -const linkCodeToAstNodes = (root: JoinPoint, codeContainer: HTMLElement, codeStart: number = 0): number => { +const linkCodeToAstNodes = (root: JoinPoint, codeElement: HTMLElement, codeStart: number = 0): number => { const nodeElement = document.querySelector(`span.ast-node[data-node-id="${root.id}"]`); if (nodeElement == null) { console.warn(`Node element not found: "${root.id}"`); @@ -82,7 +82,7 @@ const linkCodeToAstNodes = (root: JoinPoint, codeContainer: HTMLElement, codeSta const nodeCode = root.code.trim(); const nodeCodeHtml = escapeHtml(nodeCode); - const nodeCodeStart = codeContainer.innerHTML.indexOf(nodeCodeHtml, codeStart); + const nodeCodeStart = codeElement.innerHTML.indexOf(nodeCodeHtml, codeStart); if (nodeCodeStart === -1) { console.warn(`Node code not found in code container: "${nodeCodeHtml}"`); return 0; @@ -90,11 +90,11 @@ const linkCodeToAstNodes = (root: JoinPoint, codeContainer: HTMLElement, codeSta const nodeCodeWrapper = document.createElement('span'); nodeCodeWrapper.classList.add('node-code'); - nodeCodeWrapper.dataset.nodeId = root.id.toString(); + nodeCodeWrapper.dataset.nodeId = root.id; nodeCodeWrapper.innerHTML = nodeCodeHtml; - codeContainer.innerHTML = replaceAfter(codeContainer.innerHTML, nodeCodeHtml, nodeCodeWrapper.outerHTML, codeStart); + codeElement.innerHTML = replaceAfter(codeElement.innerHTML, nodeCodeHtml, nodeCodeWrapper.outerHTML, codeStart); - const nodeCodeContainer = codeContainer.querySelector(`span.node-code[data-node-id="${root.id}"]`); + const nodeCodeContainer = codeElement.querySelector(`span.node-code[data-node-id="${root.id}"]`); let nodeCodeLowerBound = 0; for (const child of root.children) { nodeCodeLowerBound = linkCodeToAstNodes(child, nodeCodeContainer!, nodeCodeLowerBound); @@ -105,7 +105,12 @@ const linkCodeToAstNodes = (root: JoinPoint, codeContainer: HTMLElement, codeSta } const importCode = (astRoot: JoinPoint, codeContainer: HTMLElement): void => { - codeContainer.innerHTML = escapeHtml(astRoot.code); + const trimedCode = astRoot.code.trim(); + codeContainer.querySelector('code')!.innerHTML = escapeHtml(trimedCode); + + const numLines = countChar(trimedCode, '\n') + 1; + const codeLines = codeContainer.querySelector('.lines')!; + codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); } const importAst = (astRoot: JoinPoint, astContainer: HTMLElement, codeContainer: HTMLElement): void => { @@ -116,7 +121,7 @@ const importAst = (astRoot: JoinPoint, astContainer: HTMLElement, codeContainer: astContainer.innerHTML = ''; astContainer.appendChild(astFragment); - linkCodeToAstNodes(refinedAstRoot, codeContainer); + linkCodeToAstNodes(refinedAstRoot, codeContainer.querySelector('code')!); addEventListenersToAstNodes(refinedAstRoot); } diff --git a/Lara-JS/src-api/visualization/public/js/main.ts b/Lara-JS/src-api/visualization/public/js/main.ts index 99b9c0501..e0377115c 100644 --- a/Lara-JS/src-api/visualization/public/js/main.ts +++ b/Lara-JS/src-api/visualization/public/js/main.ts @@ -2,7 +2,7 @@ import { continueButtonOnClick, getWebSocket, webSocketOnMessage } from "./commu (() => { const astContainer = document.querySelector('#ast-container'); - const codeContainer = document.querySelector('#code-container code'); + const codeContainer = document.querySelector('#code-container'); const continueButton = document.querySelector('#continue-button'); if (!astContainer || !codeContainer || !continueButton) { diff --git a/Lara-JS/src-api/visualization/public/js/utils.ts b/Lara-JS/src-api/visualization/public/js/utils.ts index 8a8149c64..cf96c22e1 100644 --- a/Lara-JS/src-api/visualization/public/js/utils.ts +++ b/Lara-JS/src-api/visualization/public/js/utils.ts @@ -16,6 +16,15 @@ const replaceAfter = (text: string, search: string, replace: string, start: numb return text.slice(0, index) + replace + text.slice(index + search.length); } +const countChar = (str: string, char: string): number => { + let count = 0; + for (const c of str) { + if (c === char) + count++; + } + return count; +} + const createLucideIcon = (name: string): HTMLImageElement => { const icon = document.createElement('img'); icon.classList.add('icon'); @@ -24,4 +33,4 @@ const createLucideIcon = (name: string): HTMLImageElement => { return icon; } -export { escapeHtml, replaceAfter, createLucideIcon }; \ No newline at end of file +export { escapeHtml, replaceAfter, countChar, createLucideIcon }; \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index 47c349f8c..cecde7c4b 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -1,4 +1,4 @@ -import { createLucideIcon, escapeHtml, replaceAfter } from './utils.js'; +import { countChar, createLucideIcon, escapeHtml, replaceAfter } from './utils.js'; import { addEventListenersToAstNodes } from './visualization.js'; const createAstNodeElement = (nodeId, text) => { const nodeElement = document.createElement('span'); @@ -53,7 +53,7 @@ const refineAst = (root, indentation = 0) => { refineAst(child, root.type === 'CompoundStmt' ? indentation + 1 : indentation); } }; -const linkCodeToAstNodes = (root, codeContainer, codeStart = 0) => { +const linkCodeToAstNodes = (root, codeElement, codeStart = 0) => { const nodeElement = document.querySelector(`span.ast-node[data-node-id="${root.id}"]`); if (nodeElement == null) { console.warn(`Node element not found: "${root.id}"`); @@ -61,17 +61,17 @@ const linkCodeToAstNodes = (root, codeContainer, codeStart = 0) => { } const nodeCode = root.code.trim(); const nodeCodeHtml = escapeHtml(nodeCode); - const nodeCodeStart = codeContainer.innerHTML.indexOf(nodeCodeHtml, codeStart); + const nodeCodeStart = codeElement.innerHTML.indexOf(nodeCodeHtml, codeStart); if (nodeCodeStart === -1) { console.warn(`Node code not found in code container: "${nodeCodeHtml}"`); return 0; } const nodeCodeWrapper = document.createElement('span'); nodeCodeWrapper.classList.add('node-code'); - nodeCodeWrapper.dataset.nodeId = root.id.toString(); + nodeCodeWrapper.dataset.nodeId = root.id; nodeCodeWrapper.innerHTML = nodeCodeHtml; - codeContainer.innerHTML = replaceAfter(codeContainer.innerHTML, nodeCodeHtml, nodeCodeWrapper.outerHTML, codeStart); - const nodeCodeContainer = codeContainer.querySelector(`span.node-code[data-node-id="${root.id}"]`); + codeElement.innerHTML = replaceAfter(codeElement.innerHTML, nodeCodeHtml, nodeCodeWrapper.outerHTML, codeStart); + const nodeCodeContainer = codeElement.querySelector(`span.node-code[data-node-id="${root.id}"]`); let nodeCodeLowerBound = 0; for (const child of root.children) { nodeCodeLowerBound = linkCodeToAstNodes(child, nodeCodeContainer, nodeCodeLowerBound); @@ -80,7 +80,11 @@ const linkCodeToAstNodes = (root, codeContainer, codeStart = 0) => { return codeEnd; }; const importCode = (astRoot, codeContainer) => { - codeContainer.innerHTML = escapeHtml(astRoot.code); + const trimedCode = astRoot.code.trim(); + codeContainer.querySelector('code').innerHTML = escapeHtml(trimedCode); + const numLines = countChar(trimedCode, '\n') + 1; + const codeLines = codeContainer.querySelector('.lines'); + codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); }; const importAst = (astRoot, astContainer, codeContainer) => { const refinedAstRoot = astRoot.clone(); @@ -88,7 +92,7 @@ const importAst = (astRoot, astContainer, codeContainer) => { const astFragment = convertAstNodeToHtml(refinedAstRoot); astContainer.innerHTML = ''; astContainer.appendChild(astFragment); - linkCodeToAstNodes(refinedAstRoot, codeContainer); + linkCodeToAstNodes(refinedAstRoot, codeContainer.querySelector('code')); addEventListenersToAstNodes(refinedAstRoot); }; export { importCode, importAst }; diff --git a/LaraApi/src-lara/visualization/public/js/main.js b/LaraApi/src-lara/visualization/public/js/main.js index 4e26ce697..186e2682d 100644 --- a/LaraApi/src-lara/visualization/public/js/main.js +++ b/LaraApi/src-lara/visualization/public/js/main.js @@ -1,7 +1,7 @@ import { continueButtonOnClick, getWebSocket, webSocketOnMessage } from "./communication.js"; (() => { const astContainer = document.querySelector('#ast-container'); - const codeContainer = document.querySelector('#code-container code'); + const codeContainer = document.querySelector('#code-container'); const continueButton = document.querySelector('#continue-button'); if (!astContainer || !codeContainer || !continueButton) { console.error('Required elements not found'); diff --git a/LaraApi/src-lara/visualization/public/js/utils.js b/LaraApi/src-lara/visualization/public/js/utils.js index 1c46bf6c1..f5cd3b9f3 100644 --- a/LaraApi/src-lara/visualization/public/js/utils.js +++ b/LaraApi/src-lara/visualization/public/js/utils.js @@ -13,6 +13,14 @@ const replaceAfter = (text, search, replace, start) => { } return text.slice(0, index) + replace + text.slice(index + search.length); }; +const countChar = (str, char) => { + let count = 0; + for (const c of str) { + if (c === char) + count++; + } + return count; +}; const createLucideIcon = (name) => { const icon = document.createElement('img'); icon.classList.add('icon'); @@ -20,5 +28,5 @@ const createLucideIcon = (name) => { icon.alt = `${name}-icon`; return icon; }; -export { escapeHtml, replaceAfter, createLucideIcon }; +export { escapeHtml, replaceAfter, countChar, createLucideIcon }; //# sourceMappingURL=utils.js.map \ No newline at end of file From 41e19bf45487d6a99513817d011853ca9b462e47 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 16 Jul 2024 11:54:49 +0100 Subject: [PATCH 066/136] Change continue button style and migrate icon to Material Icons --- .../visualization/public/css/color-scheme.css | 6 ++++ .../visualization/public/css/imports.css | 13 ++++++++- .../visualization/public/css/styles.css | 28 +++++++++++++------ .../src-api/visualization/public/index.html | 2 +- .../visualization/public/js/ast-import.ts | 4 +-- .../src-api/visualization/public/js/utils.ts | 12 ++++---- .../visualization/public/js/visualization.ts | 4 +-- .../public/svg/lucide-icons/chevron-down.svg | 1 - .../public/svg/lucide-icons/chevron-right.svg | 1 - .../public/svg/lucide-icons/step-forward.svg | 1 - .../visualization/public/js/ast-import.js | 4 +-- .../src-lara/visualization/public/js/utils.js | 11 ++++---- .../visualization/public/js/visualization.js | 2 +- 13 files changed, 56 insertions(+), 33 deletions(-) delete mode 100644 Lara-JS/src-api/visualization/public/svg/lucide-icons/chevron-down.svg delete mode 100644 Lara-JS/src-api/visualization/public/svg/lucide-icons/chevron-right.svg delete mode 100644 Lara-JS/src-api/visualization/public/svg/lucide-icons/step-forward.svg diff --git a/Lara-JS/src-api/visualization/public/css/color-scheme.css b/Lara-JS/src-api/visualization/public/css/color-scheme.css index 89e22bef8..8f063e79b 100644 --- a/Lara-JS/src-api/visualization/public/css/color-scheme.css +++ b/Lara-JS/src-api/visualization/public/css/color-scheme.css @@ -19,7 +19,13 @@ --header-color: var(--lighter-gray); --border-color: var(--gray); --icon-color: var(--dark-gray); + --button-bg-color: var(--white); + --button-hover-bg-color: var(--lighter-gray); + --button-disabled-bg-color: var(--lighter-gray); + --button-disabled-text-color: var(--gray); + --button-disabled-icon-color: var(--gray); + --tab-bg-color: var(--white); --tab-hover-bg-color: var(--lighter-gray); --tab-active-bg-color: var(--light-gray); diff --git a/Lara-JS/src-api/visualization/public/css/imports.css b/Lara-JS/src-api/visualization/public/css/imports.css index 06a908039..9b68a9c8c 100644 --- a/Lara-JS/src-api/visualization/public/css/imports.css +++ b/Lara-JS/src-api/visualization/public/css/imports.css @@ -1,2 +1,13 @@ +/* Fonts */ @import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap'); -@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap'); \ No newline at end of file +@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap'); + +/* Material Icons */ +@import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200'); +.material-symbols-outlined { + font-variation-settings: + 'FILL' 0, + 'wght' 400, + 'GRAD' 0, + 'opsz' 24 +} diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 8fca192c8..d597f9824 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -25,11 +25,26 @@ button { font-family: inherit; cursor: pointer; + + transition: background-color 0.1s linear, color 0.1s linear; +} + +button .icon { + color: var(--icon-color); } -button .icon { - width: 1.25em; - height: 1.25em; +button:hover { + background-color: var(--button-hover-bg-color); +} + +button:disabled { + background-color: var(--button-disabled-bg-color); + color: var(--button-disabled-text-color); + cursor: not-allowed; +} + +button:disabled .icon { + color: var(--button-disabled-icon-color); } @@ -112,7 +127,6 @@ main { } .ast-node { - padding-top: 0.25em; width: min-content; font-weight: 500; @@ -124,17 +138,13 @@ main { } .ast-node button { - height: 1.25em; + height: 1.5em; padding: 0; background-color: var(--bg-color); border: none; border-radius: 50%; } -.ast-node button > .icon { - height: 100%; -} - .ast-node-dropdown { padding-left: 2em; } diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index ea7eba0aa..407c24676 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -17,7 +17,7 @@

LARA Visualization Tool

diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index 37d2a666e..e62c317e1 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -1,4 +1,4 @@ -import { countChar, createLucideIcon, escapeHtml, replaceAfter } from './utils.js'; +import { countChar, createIcon, escapeHtml, replaceAfter } from './utils.js'; import { addEventListenersToAstNodes } from './visualization.js'; import JoinPoint from './ToolJoinPoint.js'; @@ -9,7 +9,7 @@ const createAstNodeElement = (nodeId: string, text: string): HTMLSpanElement => nodeElement.dataset.nodeId = nodeId; const chevronDropdownButton = document.createElement('button'); - chevronDropdownButton.appendChild(createLucideIcon('chevron-down')); + chevronDropdownButton.appendChild(createIcon('keyboard_arrow_down')); const nodeText = document.createElement('span'); nodeText.classList.add('ast-node-text'); diff --git a/Lara-JS/src-api/visualization/public/js/utils.ts b/Lara-JS/src-api/visualization/public/js/utils.ts index cf96c22e1..49576aac5 100644 --- a/Lara-JS/src-api/visualization/public/js/utils.ts +++ b/Lara-JS/src-api/visualization/public/js/utils.ts @@ -25,12 +25,12 @@ const countChar = (str: string, char: string): number => { return count; } -const createLucideIcon = (name: string): HTMLImageElement => { - const icon = document.createElement('img'); - icon.classList.add('icon'); - icon.src = `/svg/lucide-icons/${name}.svg`; - icon.alt = `${name}-icon`; +const createIcon = (name: string): HTMLElement => { + const icon = document.createElement('span'); + icon.classList.add('icon', 'material-symbols-outlined'); + icon.textContent = name; + return icon; } -export { escapeHtml, replaceAfter, countChar, createLucideIcon }; \ No newline at end of file +export { escapeHtml, replaceAfter, countChar, createIcon }; \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index 32c9631d8..1a8876790 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -38,8 +38,8 @@ const addEventListenersToAstNodes = (root: JoinPoint): void => { nodeCollapsed = !nodeCollapsed; nodeDropdown.style.display = nodeCollapsed ? 'none' : 'block'; - const chevron = nodeDropdownButton.children[0] as HTMLImageElement; - chevron.src = `/svg/lucide-icons/chevron-${nodeCollapsed ? 'right' : 'down'}.svg`; + const chevron = nodeDropdownButton.children[0] as HTMLElement; + chevron.textContent = nodeCollapsed ? 'keyboard_arrow_right' : 'keyboard_arrow_down'; }); const nodeRelatedElements = getNodeRelatedElements(nodeId); diff --git a/Lara-JS/src-api/visualization/public/svg/lucide-icons/chevron-down.svg b/Lara-JS/src-api/visualization/public/svg/lucide-icons/chevron-down.svg deleted file mode 100644 index b26052518..000000000 --- a/Lara-JS/src-api/visualization/public/svg/lucide-icons/chevron-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/svg/lucide-icons/chevron-right.svg b/Lara-JS/src-api/visualization/public/svg/lucide-icons/chevron-right.svg deleted file mode 100644 index 387517288..000000000 --- a/Lara-JS/src-api/visualization/public/svg/lucide-icons/chevron-right.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/svg/lucide-icons/step-forward.svg b/Lara-JS/src-api/visualization/public/svg/lucide-icons/step-forward.svg deleted file mode 100644 index 983ba7447..000000000 --- a/Lara-JS/src-api/visualization/public/svg/lucide-icons/step-forward.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index cecde7c4b..4d1739ecc 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -1,11 +1,11 @@ -import { countChar, createLucideIcon, escapeHtml, replaceAfter } from './utils.js'; +import { countChar, createIcon, escapeHtml, replaceAfter } from './utils.js'; import { addEventListenersToAstNodes } from './visualization.js'; const createAstNodeElement = (nodeId, text) => { const nodeElement = document.createElement('span'); nodeElement.classList.add('ast-node'); nodeElement.dataset.nodeId = nodeId; const chevronDropdownButton = document.createElement('button'); - chevronDropdownButton.appendChild(createLucideIcon('chevron-down')); + chevronDropdownButton.appendChild(createIcon('keyboard_arrow_down')); const nodeText = document.createElement('span'); nodeText.classList.add('ast-node-text'); nodeText.textContent = text; diff --git a/LaraApi/src-lara/visualization/public/js/utils.js b/LaraApi/src-lara/visualization/public/js/utils.js index f5cd3b9f3..a92ce2a0b 100644 --- a/LaraApi/src-lara/visualization/public/js/utils.js +++ b/LaraApi/src-lara/visualization/public/js/utils.js @@ -21,12 +21,11 @@ const countChar = (str, char) => { } return count; }; -const createLucideIcon = (name) => { - const icon = document.createElement('img'); - icon.classList.add('icon'); - icon.src = `/svg/lucide-icons/${name}.svg`; - icon.alt = `${name}-icon`; +const createIcon = (name) => { + const icon = document.createElement('span'); + icon.classList.add('icon', 'material-symbols-outlined'); + icon.textContent = name; return icon; }; -export { escapeHtml, replaceAfter, countChar, createLucideIcon }; +export { escapeHtml, replaceAfter, countChar, createIcon }; //# sourceMappingURL=utils.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index 756fc5a60..22e7f192a 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -29,7 +29,7 @@ const addEventListenersToAstNodes = (root) => { nodeCollapsed = !nodeCollapsed; nodeDropdown.style.display = nodeCollapsed ? 'none' : 'block'; const chevron = nodeDropdownButton.children[0]; - chevron.src = `/svg/lucide-icons/chevron-${nodeCollapsed ? 'right' : 'down'}.svg`; + chevron.textContent = nodeCollapsed ? 'keyboard_arrow_right' : 'keyboard_arrow_down'; }); const nodeRelatedElements = getNodeRelatedElements(nodeId); for (const nodeRelatedElement of nodeRelatedElements) { From af21eaff224d90178a9460aefff166a1ede123bb Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 16 Jul 2024 12:21:46 +0100 Subject: [PATCH 067/136] Refactor highlighting --- .../visualization/public/css/styles.css | 4 --- .../visualization/public/js/visualization.ts | 34 +++++++++++++++---- .../visualization/public/js/visualization.js | 28 +++++++++++---- 3 files changed, 50 insertions(+), 16 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index d597f9824..20591bd4f 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -116,10 +116,6 @@ main { -webkit-user-select: none; } -.container * { - transition: background-color 0.05s linear; -} - #ast-container { font-weight: 500; diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index 1a8876790..d47ab680b 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -13,16 +13,38 @@ const getNodeRelatedElements = (nodeId: string): HTMLElement[] => { } const highlightNode = (nodeId: string): void => { - const elements = document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"] .ast-node-text, .node-code[data-node-id="${nodeId}"]`); - for (const element of elements) { - element.style.backgroundColor = 'yellow'; + const nodeCode = document.querySelector(`.node-code[data-node-id="${nodeId}"]`)!; + if (nodeCode) + nodeCode.style.backgroundColor = 'var(--highlight-color)'; + + const nodeElement = document.querySelector(`.ast-node[data-node-id="${nodeId}"]`)!; + const nodeText = nodeElement.querySelector('.ast-node-text')!; + nodeText.style.backgroundColor = 'var(--highlight-color)'; + + let parentNode = nodeElement.parentElement?.previousSibling; + while (parentNode instanceof HTMLElement && parentNode.classList.contains('ast-node')) { + const parentNodeText = parentNode.querySelector('.ast-node-text')!; + parentNodeText.style.backgroundColor = 'var(--secondary-highlight-color)'; + + parentNode = parentNode.parentElement?.previousSibling; } } const unhighlightNode = (nodeId: string): void => { - const elements = document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"] .ast-node-text, .node-code[data-node-id="${nodeId}"]`); - for (const element of elements) { - element.style.backgroundColor = ''; + const nodeCode = document.querySelector(`.node-code[data-node-id="${nodeId}"]`)!; + if (nodeCode) + nodeCode.style.backgroundColor = ''; + + const nodeElement = document.querySelector(`.ast-node[data-node-id="${nodeId}"]`)!; + const nodeText = nodeElement.querySelector('.ast-node-text')!; + nodeText.style.backgroundColor = ''; + + let parentNode = nodeElement.parentElement?.previousSibling as HTMLElement | null | undefined; + while (parentNode instanceof HTMLElement && parentNode.classList.contains('ast-node')) { + const parentNodeText = parentNode.querySelector('.ast-node-text')!; + parentNodeText.style.backgroundColor = ''; + + parentNode = parentNode.parentElement?.previousSibling as HTMLElement | null | undefined; } } diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index 22e7f192a..9b420a188 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -8,15 +8,31 @@ const getNodeRelatedElements = (nodeId) => { return Array.from(document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"], .node-code[data-node-id="${nodeId}"]`)); }; const highlightNode = (nodeId) => { - const elements = document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"] .ast-node-text, .node-code[data-node-id="${nodeId}"]`); - for (const element of elements) { - element.style.backgroundColor = 'yellow'; + const nodeCode = document.querySelector(`.node-code[data-node-id="${nodeId}"]`); + if (nodeCode) + nodeCode.style.backgroundColor = 'var(--highlight-color)'; + const nodeElement = document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); + const nodeText = nodeElement.querySelector('.ast-node-text'); + nodeText.style.backgroundColor = 'var(--highlight-color)'; + let parentNode = nodeElement.parentElement?.previousSibling; + while (parentNode instanceof HTMLElement && parentNode.classList.contains('ast-node')) { + const parentNodeText = parentNode.querySelector('.ast-node-text'); + parentNodeText.style.backgroundColor = 'var(--secondary-highlight-color)'; + parentNode = parentNode.parentElement?.previousSibling; } }; const unhighlightNode = (nodeId) => { - const elements = document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"] .ast-node-text, .node-code[data-node-id="${nodeId}"]`); - for (const element of elements) { - element.style.backgroundColor = ''; + const nodeCode = document.querySelector(`.node-code[data-node-id="${nodeId}"]`); + if (nodeCode) + nodeCode.style.backgroundColor = ''; + const nodeElement = document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); + const nodeText = nodeElement.querySelector('.ast-node-text'); + nodeText.style.backgroundColor = ''; + let parentNode = nodeElement.parentElement?.previousSibling; + while (parentNode instanceof HTMLElement && parentNode.classList.contains('ast-node')) { + const parentNodeText = parentNode.querySelector('.ast-node-text'); + parentNodeText.style.backgroundColor = ''; + parentNode = parentNode.parentElement?.previousSibling; } }; const addEventListenersToAstNodes = (root) => { From ff3e123554d684eabd61c750dfe6104a5557b4f3 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 16 Jul 2024 12:30:25 +0100 Subject: [PATCH 068/136] Fix child node order --- Lara-JS/src-api/visualization/VisualizationTool.ts | 1 + LaraApi/src-lara/visualization/VisualizationTool.js | 1 + 2 files changed, 2 insertions(+) diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts index d7aa39229..c1d1a9a5c 100644 --- a/Lara-JS/src-api/visualization/VisualizationTool.ts +++ b/Lara-JS/src-api/visualization/VisualizationTool.ts @@ -151,6 +151,7 @@ export default class VisualizationTool { } private static updateClient(ws: WebSocket): void { + wrapJoinPoint(JoinPoints.root()._javaObject.rebuild()); this.sendToClient(ws, { message: 'update', ast: this.toToolJpJson(JoinPoints.root()) }); } diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js index 3f203c384..0d2c3e5ed 100644 --- a/LaraApi/src-lara/visualization/VisualizationTool.js +++ b/LaraApi/src-lara/visualization/VisualizationTool.js @@ -118,6 +118,7 @@ export default class VisualizationTool { }; } static updateClient(ws) { + wrapJoinPoint(JoinPoints.root()._javaObject.rebuild()); this.sendToClient(ws, { message: 'update', ast: this.toToolJpJson(JoinPoints.root()) }); } static update() { From 621e987d8279ca34be8b48174e99ea0a6808c2c2 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 16 Jul 2024 12:38:07 +0100 Subject: [PATCH 069/136] Fix invalid HTML code --- Lara-JS/src-api/visualization/public/css/styles.css | 12 +++++++----- Lara-JS/src-api/visualization/public/index.html | 4 ++-- .../src-api/visualization/public/js/ast-import.ts | 4 ++-- .../src-lara/visualization/public/js/ast-import.js | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 20591bd4f..66f8fc032 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -147,16 +147,18 @@ main { #code-container { - font-family: "JetBrains Mono", monospace; - font-optical-sizing: auto; - display: grid; grid-template-columns: auto 1fr; gap: 1em; } -#code-container code { - font-family: inherit; +#code-container pre { + margin: 0; +} + +#code-container .lines, #code-container code { + font-family: "JetBrains Mono", monospace; + font-optical-sizing: auto; } #code-container .lines { diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 407c24676..1e9d7271b 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -21,9 +21,9 @@

LARA Visualization Tool

Continue
-
+        
- +
diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index e62c317e1..13a560bd0 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -109,8 +109,8 @@ const importCode = (astRoot: JoinPoint, codeContainer: HTMLElement): void => { codeContainer.querySelector('code')!.innerHTML = escapeHtml(trimedCode); const numLines = countChar(trimedCode, '\n') + 1; - const codeLines = codeContainer.querySelector('.lines')!; - codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); + const codeLines = codeContainer.querySelector('.lines')!; + codeLines.innerText = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); } const importAst = (astRoot: JoinPoint, astContainer: HTMLElement, codeContainer: HTMLElement): void => { diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index 4d1739ecc..6c366378f 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -84,7 +84,7 @@ const importCode = (astRoot, codeContainer) => { codeContainer.querySelector('code').innerHTML = escapeHtml(trimedCode); const numLines = countChar(trimedCode, '\n') + 1; const codeLines = codeContainer.querySelector('.lines'); - codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); + codeLines.innerText = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); }; const importAst = (astRoot, astContainer, codeContainer) => { const refinedAstRoot = astRoot.clone(); From 3b675d9833e269f006b5746ee9abaed6ba2b5a5c Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 17 Jul 2024 09:35:25 +0100 Subject: [PATCH 070/136] Start using joinPointType instead of astName --- Lara-JS/src-api/visualization/VisualizationTool.ts | 2 +- LaraApi/src-lara/visualization/VisualizationTool.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/VisualizationTool.ts index c1d1a9a5c..9dbd37a84 100644 --- a/Lara-JS/src-api/visualization/VisualizationTool.ts +++ b/Lara-JS/src-api/visualization/VisualizationTool.ts @@ -141,7 +141,7 @@ export default class VisualizationTool { private static toToolJpJson(jp: LaraJoinPoint): any { return { id: wrapJoinPoint(jp._javaObject.getAstId()), - type: wrapJoinPoint(jp._javaObject.getAstName()), + type: wrapJoinPoint(jp._javaObject.getJoinPointType()), code: wrapJoinPoint(jp._javaObject.getCode()), children: jp.children .slice() diff --git a/LaraApi/src-lara/visualization/VisualizationTool.js b/LaraApi/src-lara/visualization/VisualizationTool.js index 0d2c3e5ed..0610ce4a2 100644 --- a/LaraApi/src-lara/visualization/VisualizationTool.js +++ b/LaraApi/src-lara/visualization/VisualizationTool.js @@ -109,7 +109,7 @@ export default class VisualizationTool { static toToolJpJson(jp) { return { id: wrapJoinPoint(jp._javaObject.getAstId()), - type: wrapJoinPoint(jp._javaObject.getAstName()), + type: wrapJoinPoint(jp._javaObject.getJoinPointType()), code: wrapJoinPoint(jp._javaObject.getCode()), children: jp.children .slice() From 54f64227c86c37448cbf3028cf99740923e75684 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 17 Jul 2024 10:10:45 +0100 Subject: [PATCH 071/136] Fix AST code corrections --- Lara-JS/src-api/visualization/public/js/ast-import.ts | 8 ++++---- LaraApi/src-lara/visualization/public/js/ast-import.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index 13a560bd0..5a988b4bd 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -53,13 +53,13 @@ const refineAst = (root: JoinPoint, indentation: number = 0): void => { root.code = addIdentation(root.code.trim(), indentation); const children = root.children; - if (['WhileStmt', 'DoStmt', 'ForStmt'].includes(root.type)) { + if (root.type == 'loop') { children - .filter(child => child.type === 'ExprStmt') + .filter(child => child.type === 'exprStmt') .forEach(child => child.code = child.code.slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses } - if (root.type == 'DeclStmt') { + if (root.type == 'declStmt') { root.children .slice(1) .forEach(child => { @@ -69,7 +69,7 @@ const refineAst = (root: JoinPoint, indentation: number = 0): void => { for (const child of root.children) { - refineAst(child, root.type === 'CompoundStmt' ? indentation + 1 : indentation); + refineAst(child, ['body', 'class'].includes(root.type) ? indentation + 1 : indentation); } } diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index 6c366378f..24236c44a 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -37,12 +37,12 @@ const addIdentation = (code, indentation) => { const refineAst = (root, indentation = 0) => { root.code = addIdentation(root.code.trim(), indentation); const children = root.children; - if (['WhileStmt', 'DoStmt', 'ForStmt'].includes(root.type)) { + if (root.type == 'loop') { children - .filter(child => child.type === 'ExprStmt') + .filter(child => child.type === 'exprStmt') .forEach(child => child.code = child.code.slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses } - if (root.type == 'DeclStmt') { + if (root.type == 'declStmt') { root.children .slice(1) .forEach(child => { @@ -50,7 +50,7 @@ const refineAst = (root, indentation = 0) => { }); // Remove type from variable declarations } for (const child of root.children) { - refineAst(child, root.type === 'CompoundStmt' ? indentation + 1 : indentation); + refineAst(child, ['body', 'class'].includes(root.type) ? indentation + 1 : indentation); } }; const linkCodeToAstNodes = (root, codeElement, codeStart = 0) => { From ca4fccea9828de5354ff0390da30595ccaac20f3 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 18 Jul 2024 08:07:38 +0100 Subject: [PATCH 072/136] Pass compiler dependant actions to Clava --- .../visualization/GenericAstConverter.ts | 7 + ...ionTool.ts => GenericVisualizationTool.ts} | 60 ++++----- .../visualization/public/css/styles.css | 2 +- .../visualization/public/js/ToolJoinPoint.ts | 13 +- .../visualization/public/js/communication.ts | 2 +- .../src-api/visualization/public/js/main.ts | 2 +- .../up/fe/specs/lara/LaraApiJsResource.java | 4 +- .../visualization/GenericAstConverter.js | 2 + .../visualization/GenericAstSerializer.js | 2 + .../GenericJoinPointConverter.js | 2 + .../visualization/GenericVisualizationTool.js | 125 ++++++++++++++++++ .../visualization/public/js/ToolJoinPoint.js | 12 +- .../visualization/public/js/communication.js | 2 +- 13 files changed, 194 insertions(+), 41 deletions(-) create mode 100644 Lara-JS/src-api/visualization/GenericAstConverter.ts rename Lara-JS/src-api/visualization/{VisualizationTool.ts => GenericVisualizationTool.ts} (68%) create mode 100644 LaraApi/src-lara/visualization/GenericAstConverter.js create mode 100644 LaraApi/src-lara/visualization/GenericAstSerializer.js create mode 100644 LaraApi/src-lara/visualization/GenericJoinPointConverter.js create mode 100644 LaraApi/src-lara/visualization/GenericVisualizationTool.js diff --git a/Lara-JS/src-api/visualization/GenericAstConverter.ts b/Lara-JS/src-api/visualization/GenericAstConverter.ts new file mode 100644 index 000000000..a84b3f771 --- /dev/null +++ b/Lara-JS/src-api/visualization/GenericAstConverter.ts @@ -0,0 +1,7 @@ +import { LaraJoinPoint } from "../LaraJoinPoint.js"; +import ToolJoinPoint from "./public/js/ToolJoinPoint.js"; + +export default interface GenericAstConverter { + getToolAst(root: LaraJoinPoint): ToolJoinPoint; + getPrettyHtmlCode(root: LaraJoinPoint): string; +} \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/VisualizationTool.ts b/Lara-JS/src-api/visualization/GenericVisualizationTool.ts similarity index 68% rename from Lara-JS/src-api/visualization/VisualizationTool.ts rename to Lara-JS/src-api/visualization/GenericVisualizationTool.ts index 9dbd37a84..2c334fe42 100644 --- a/Lara-JS/src-api/visualization/VisualizationTool.ts +++ b/Lara-JS/src-api/visualization/GenericVisualizationTool.ts @@ -2,32 +2,33 @@ import express from 'express'; import http from 'http'; import path from 'path'; import { fileURLToPath } from 'url'; -import WebSocket, { WebSocketServer, MessageEvent } from 'ws'; +import WebSocket, { WebSocketServer } from 'ws'; import { AddressInfo } from 'net'; -import { LaraJoinPoint, wrapJoinPoint } from '../LaraJoinPoint.js'; +import { wrapJoinPoint } from '../LaraJoinPoint.js'; import JoinPoints from '../weaver/JoinPoints.js'; +import GenericAstConverter from './GenericAstConverter.js'; -export default class VisualizationTool { - private static hostname: string | undefined; - private static port: number | undefined; - private static wss: WebSocketServer | undefined; - private static serverClosed: boolean = false; +export default abstract class GenericVisualizationTool { + private hostname: string | undefined; + private port: number | undefined; + private wss: WebSocketServer | undefined; + private serverClosed: boolean = false; - public static isLaunched(): boolean { + public isLaunched(): boolean { return this.wss !== undefined && this.serverClosed === false; } - public static getHostname(): string | undefined { + public getHostname(): string | undefined { return this.hostname; } - public static getPort(): number | undefined { + public getPort(): number | undefined { return this.port; } - private static onWssError(error: NodeJS.ErrnoException): void { + private onWssError(error: NodeJS.ErrnoException): void { switch (error.code) { case 'EADDRINUSE': console.error(`[server]: Port ${this.port} is already in use`); @@ -45,7 +46,7 @@ export default class VisualizationTool { this.wss!.close(); } - public static async launch(hostname: string = '127.0.0.1', port?: number): Promise { + public async launch(hostname: string = '127.0.0.1', port?: number): Promise { if (this.isLaunched()) { console.warn(`Visualization tool is already running at http://${this.hostname}:${this.port}`); return; @@ -95,21 +96,21 @@ export default class VisualizationTool { }); } - private static sendToClient(ws: WebSocket, data: any): void { + private sendToClient(ws: WebSocket, data: any): void { ws.send(JSON.stringify(data)); } - private static sendToAllClients(data: any): void { + private sendToAllClients(data: any): void { this.wss!.clients.forEach(ws => this.sendToClient(ws, data)); } - private static verifyToolIsRunning(): void { + private verifyToolIsRunning(): void { if (!this.isLaunched()) { throw Error('Visualization tool is not running'); } } - public static async waitForTool(): Promise { + public async waitForTool(): Promise { this.verifyToolIsRunning(); return new Promise(res => { @@ -138,25 +139,22 @@ export default class VisualizationTool { }); } - private static toToolJpJson(jp: LaraJoinPoint): any { - return { - id: wrapJoinPoint(jp._javaObject.getAstId()), - type: wrapJoinPoint(jp._javaObject.getJoinPointType()), - code: wrapJoinPoint(jp._javaObject.getCode()), - children: jp.children - .slice() - .sort((a, b) => wrapJoinPoint(a._javaObject.getLocation()).localeCompare(wrapJoinPoint(b._javaObject.getLocation()), 'en', { numeric: true })) // TODO: Perform sorting on frontend - .map(child => this.toToolJpJson(child)) - }; - } - - private static updateClient(ws: WebSocket): void { + private updateClient(ws: WebSocket): void { wrapJoinPoint(JoinPoints.root()._javaObject.rebuild()); - this.sendToClient(ws, { message: 'update', ast: this.toToolJpJson(JoinPoints.root()) }); + this.sendToClient(ws, { + message: 'update', + ast: this.getAstConverter() + .getToolAst(JoinPoints.root()) + .toJson(), + code: this.getAstConverter() + .getPrettyHtmlCode(JoinPoints.root()) + }); } - public static update(): void { + public update(): void { this.verifyToolIsRunning(); this.wss!.clients.forEach(ws => this.updateClient(ws)); } + + protected abstract getAstConverter(): GenericAstConverter; } \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 66f8fc032..c2a0a9856 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -72,7 +72,7 @@ main { background-color: var(--bg-color); display: grid; - grid-template-columns: 1fr 1fr; + grid-template-columns: 2fr 3fr; grid-template-rows: min-content 1fr; grid-template-areas: "continue-button code" "ast code"; diff --git a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts index 60a553268..e4a2f0b9f 100644 --- a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts +++ b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts @@ -11,15 +11,24 @@ export default class ToolJoinPoint { this.children = children; } - public static fromJSON(json: any): ToolJoinPoint { + public static fromJson(json: any): ToolJoinPoint { return new ToolJoinPoint( json.id, json.type, json.code, - json.children.map((child: any) => ToolJoinPoint.fromJSON(child)), + json.children.map((child: any) => ToolJoinPoint.fromJson(child)), ); } + public toJson(): any { + return { + id: this.id, + type: this.type, + code: this.code, + children: this.children.map(child => child.toJson()), + }; + } + public clone(): ToolJoinPoint { return new ToolJoinPoint(this.id, this.type, this.code, this.children.map(child => child.clone())); } diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts index 8ced63ca2..c0279cdf3 100644 --- a/Lara-JS/src-api/visualization/public/js/communication.ts +++ b/Lara-JS/src-api/visualization/public/js/communication.ts @@ -19,7 +19,7 @@ const webSocketOnMessage = (message: MessageEvent, continueButton: HTMLButtonEle switch (data.message) { case 'update': - const ast = JoinPoint.fromJSON(data.ast); + const ast = JoinPoint.fromJson(data.ast); importCode(ast, codeContainer); importAst(ast, astContainer, codeContainer); break; diff --git a/Lara-JS/src-api/visualization/public/js/main.ts b/Lara-JS/src-api/visualization/public/js/main.ts index e0377115c..c73b4363a 100644 --- a/Lara-JS/src-api/visualization/public/js/main.ts +++ b/Lara-JS/src-api/visualization/public/js/main.ts @@ -19,4 +19,4 @@ import { continueButtonOnClick, getWebSocket, webSocketOnMessage } from "./commu setupWebSocket(); continueButton.addEventListener('click', () => continueButtonOnClick(continueButton, ws)); -})(); \ No newline at end of file +})(); diff --git a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java index 3fce72572..76828ba94 100644 --- a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java +++ b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java @@ -105,8 +105,8 @@ public enum LaraApiJsResource implements LaraResourceProvider { TIMEUNITS_JS("lara/util/TimeUnits.js"), TUPLEID_JS("lara/util/TupleId.js"), CYTOSCAPE_3_26_0_JS("libs/cytoscape-3.26.0.js"), - VISUALIZATIONTOOL_JS("visualization/VisualizationTool.js"), - VISUALIZATIONTOOLUPDATE_JS("visualization/VisualizationToolUpdate.js"), + GENERICASTCONVERTER_JS("visualization/GenericAstConverter.js"), + GENERICVISUALIZATIONTOOL_JS("visualization/GenericVisualizationTool.js"), TOOLJOINPOINT_JS("visualization/public/js/ToolJoinPoint.js"), AST_IMPORT_JS("visualization/public/js/ast-import.js"), COMMUNICATION_JS("visualization/public/js/communication.js"), diff --git a/LaraApi/src-lara/visualization/GenericAstConverter.js b/LaraApi/src-lara/visualization/GenericAstConverter.js new file mode 100644 index 000000000..9619bd83e --- /dev/null +++ b/LaraApi/src-lara/visualization/GenericAstConverter.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=GenericAstConverter.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/GenericAstSerializer.js b/LaraApi/src-lara/visualization/GenericAstSerializer.js new file mode 100644 index 000000000..ceab74e66 --- /dev/null +++ b/LaraApi/src-lara/visualization/GenericAstSerializer.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=GenericAstSerializer.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/GenericJoinPointConverter.js b/LaraApi/src-lara/visualization/GenericJoinPointConverter.js new file mode 100644 index 000000000..9ea593153 --- /dev/null +++ b/LaraApi/src-lara/visualization/GenericJoinPointConverter.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=GenericJoinPointConverter.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/GenericVisualizationTool.js b/LaraApi/src-lara/visualization/GenericVisualizationTool.js new file mode 100644 index 000000000..6f652b93d --- /dev/null +++ b/LaraApi/src-lara/visualization/GenericVisualizationTool.js @@ -0,0 +1,125 @@ +import express from 'express'; +import http from 'http'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import { WebSocketServer } from 'ws'; +import { wrapJoinPoint } from '../LaraJoinPoint.js'; +import JoinPoints from '../weaver/JoinPoints.js'; +export default class GenericVisualizationTool { + hostname; + port; + wss; + serverClosed = false; + isLaunched() { + return this.wss !== undefined && this.serverClosed === false; + } + getHostname() { + return this.hostname; + } + getPort() { + return this.port; + } + onWssError(error) { + switch (error.code) { + case 'EADDRINUSE': + console.error(`[server]: Port ${this.port} is already in use`); + break; + case 'EACCES': + console.error(`[server]: Permission denied to use port ${this.port}`); + break; + default: + console.error(`[server]: Unknown error occurred: ${error.message}`); + break; + } + ; + this.wss.close(); + } + async launch(hostname = '127.0.0.1', port) { + if (this.isLaunched()) { + console.warn(`Visualization tool is already running at http://${this.hostname}:${this.port}`); + return; + } + const app = express(); + const server = http.createServer(app); + this.wss = new WebSocketServer({ server: server }); + const filename = fileURLToPath(import.meta.url); + const dirname = path.dirname(filename); + app.use(express.static(path.join(dirname, 'public'))); + this.wss.on('connection', (ws) => { + console.log('[server]: Client connected'); + ws.on('message', (message) => { + console.log(`[server]: Received message => ${message}`); + }); + ws.addEventListener('close', () => { + console.log('[server]: Client disconnected'); + }); + }); // TODO: Remove this + this.wss.on('connection', ws => this.updateClient(ws)); + this.wss.on('close', () => { + this.serverClosed = true; + }); + this.wss.on('error', error => this.onWssError(error)); + return new Promise(res => { + server.listen(port ?? 0, hostname, () => { + const addressInfo = server.address(); + this.hostname = addressInfo.address; + this.port = addressInfo.port; + this.serverClosed = false; + console.log(`\nVisualization tool is running at http://${this.hostname}:${this.port}\n`); + // child.exec(`xdg-open http://${this.host}:${this.port}`); + // TODO: See if opening automatically is a good idea + res(); + }); + }); + } + sendToClient(ws, data) { + ws.send(JSON.stringify(data)); + } + sendToAllClients(data) { + this.wss.clients.forEach(ws => this.sendToClient(ws, data)); + } + verifyToolIsRunning() { + if (!this.isLaunched()) { + throw Error('Visualization tool is not running'); + } + } + async waitForTool() { + this.verifyToolIsRunning(); + return new Promise(res => { + let placeClientOnWait; + const waitOnMessage = (message) => { + const data = JSON.parse(message); + if (data.message === 'continue') { + this.wss.clients.forEach(ws => { + this.wss.off('connection', placeClientOnWait); + ws.off('message', waitOnMessage); + }); + this.sendToAllClients({ message: 'continue' }); + res(); + } + }; + placeClientOnWait = (ws) => { + ws.on('message', waitOnMessage); + this.sendToClient(ws, { message: 'wait' }); + }; + this.wss.clients.forEach(placeClientOnWait); + this.wss.on('connection', placeClientOnWait); + }); + } + updateClient(ws) { + wrapJoinPoint(JoinPoints.root()._javaObject.rebuild()); + this.sendToClient(ws, { + message: 'update', + ast: this.getAstConverter() + .getToolAst(JoinPoints.root()) + .toJson(), + code: this.getAstConverter() + .getPrettyHtmlCode(JoinPoints.root()) + }); + } + update() { + this.verifyToolIsRunning(); + this.wss.clients.forEach(ws => this.updateClient(ws)); + } +} +//# sourceMappingURL=GenericVisualizationTool.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js index b606dfca5..6d7da561a 100644 --- a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js +++ b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js @@ -9,8 +9,16 @@ export default class ToolJoinPoint { this.code = code; this.children = children; } - static fromJSON(json) { - return new ToolJoinPoint(json.id, json.type, json.code, json.children.map((child) => ToolJoinPoint.fromJSON(child))); + static fromJson(json) { + return new ToolJoinPoint(json.id, json.type, json.code, json.children.map((child) => ToolJoinPoint.fromJson(child))); + } + toJson() { + return { + id: this.id, + type: this.type, + code: this.code, + children: this.children.map(child => child.toJson()), + }; } clone() { return new ToolJoinPoint(this.id, this.type, this.code, this.children.map(child => child.clone())); diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js index 9505d3ec0..b7ec9148d 100644 --- a/LaraApi/src-lara/visualization/public/js/communication.js +++ b/LaraApi/src-lara/visualization/public/js/communication.js @@ -14,7 +14,7 @@ const webSocketOnMessage = (message, continueButton, astContainer, codeContainer const data = parseMessage(message); switch (data.message) { case 'update': - const ast = JoinPoint.fromJSON(data.ast); + const ast = JoinPoint.fromJson(data.ast); importCode(ast, codeContainer); importAst(ast, astContainer, codeContainer); break; From ab2667c18de40ebe99f43840f1f131273bcbaf3c Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 18 Jul 2024 09:23:21 +0100 Subject: [PATCH 073/136] Move AST refining to Clava --- .../visualization/public/js/ast-import.ts | 37 ++----------------- .../visualization/public/js/ast-import.js | 31 ++-------------- 2 files changed, 7 insertions(+), 61 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index 5a988b4bd..7df32bd12 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -45,34 +45,6 @@ const convertAstNodeToHtml = (root: JoinPoint): DocumentFragment => { return fragment; }; -const addIdentation = (code: string, indentation: number): string => { - return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); -} - -const refineAst = (root: JoinPoint, indentation: number = 0): void => { - root.code = addIdentation(root.code.trim(), indentation); - - const children = root.children; - if (root.type == 'loop') { - children - .filter(child => child.type === 'exprStmt') - .forEach(child => child.code = child.code.slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses - } - - if (root.type == 'declStmt') { - root.children - .slice(1) - .forEach(child => { - child.code = child.code.match(/(?:\S+\s+)(\S.*)/)![1]; - }); // Remove type from variable declarations - } - - - for (const child of root.children) { - refineAst(child, ['body', 'class'].includes(root.type) ? indentation + 1 : indentation); - } -} - const linkCodeToAstNodes = (root: JoinPoint, codeElement: HTMLElement, codeStart: number = 0): number => { const nodeElement = document.querySelector(`span.ast-node[data-node-id="${root.id}"]`); if (nodeElement == null) { @@ -114,15 +86,12 @@ const importCode = (astRoot: JoinPoint, codeContainer: HTMLElement): void => { } const importAst = (astRoot: JoinPoint, astContainer: HTMLElement, codeContainer: HTMLElement): void => { - const refinedAstRoot = astRoot.clone(); - refineAst(refinedAstRoot); - - const astFragment = convertAstNodeToHtml(refinedAstRoot); + const astFragment = convertAstNodeToHtml(astRoot); astContainer.innerHTML = ''; astContainer.appendChild(astFragment); - linkCodeToAstNodes(refinedAstRoot, codeContainer.querySelector('code')!); - addEventListenersToAstNodes(refinedAstRoot); + linkCodeToAstNodes(astRoot, codeContainer.querySelector('code')!); + addEventListenersToAstNodes(astRoot); } export { importCode, importAst }; diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index 24236c44a..8c480e5e0 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -31,28 +31,6 @@ const convertAstNodeToHtml = (root) => { fragment.appendChild(rootDropdown); return fragment; }; -const addIdentation = (code, indentation) => { - return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); -}; -const refineAst = (root, indentation = 0) => { - root.code = addIdentation(root.code.trim(), indentation); - const children = root.children; - if (root.type == 'loop') { - children - .filter(child => child.type === 'exprStmt') - .forEach(child => child.code = child.code.slice(0, -1)); // Remove semicolon from expression statements inside loop parentheses - } - if (root.type == 'declStmt') { - root.children - .slice(1) - .forEach(child => { - child.code = child.code.match(/(?:\S+\s+)(\S.*)/)[1]; - }); // Remove type from variable declarations - } - for (const child of root.children) { - refineAst(child, ['body', 'class'].includes(root.type) ? indentation + 1 : indentation); - } -}; const linkCodeToAstNodes = (root, codeElement, codeStart = 0) => { const nodeElement = document.querySelector(`span.ast-node[data-node-id="${root.id}"]`); if (nodeElement == null) { @@ -87,13 +65,12 @@ const importCode = (astRoot, codeContainer) => { codeLines.innerText = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); }; const importAst = (astRoot, astContainer, codeContainer) => { - const refinedAstRoot = astRoot.clone(); - refineAst(refinedAstRoot); - const astFragment = convertAstNodeToHtml(refinedAstRoot); + const astFragment = convertAstNodeToHtml(astRoot); astContainer.innerHTML = ''; astContainer.appendChild(astFragment); - linkCodeToAstNodes(refinedAstRoot, codeContainer.querySelector('code')); - addEventListenersToAstNodes(refinedAstRoot); + + linkCodeToAstNodes(astRoot, codeContainer.querySelector('code')); + addEventListenersToAstNodes(astRoot); }; export { importCode, importAst }; //# sourceMappingURL=ast-import.js.map \ No newline at end of file From 427a4f6aab18f47aa98fd926b4b90a90311ac5bc Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 18 Jul 2024 16:19:19 +0100 Subject: [PATCH 074/136] Perform node to code linking on Clava --- .../visualization/public/js/ast-import.ts | 41 ++----------------- .../visualization/public/js/communication.ts | 4 +- .../src-api/visualization/public/js/utils.ts | 20 +-------- .../visualization/public/js/ast-import.js | 37 ++--------------- .../visualization/public/js/communication.js | 5 +-- .../src-lara/visualization/public/js/utils.js | 17 +------- 6 files changed, 14 insertions(+), 110 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index 7df32bd12..74f48d0e7 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -1,4 +1,4 @@ -import { countChar, createIcon, escapeHtml, replaceAfter } from './utils.js'; +import { countChar, createIcon, } from './utils.js'; import { addEventListenersToAstNodes } from './visualization.js'; import JoinPoint from './ToolJoinPoint.js'; @@ -45,42 +45,10 @@ const convertAstNodeToHtml = (root: JoinPoint): DocumentFragment => { return fragment; }; -const linkCodeToAstNodes = (root: JoinPoint, codeElement: HTMLElement, codeStart: number = 0): number => { - const nodeElement = document.querySelector(`span.ast-node[data-node-id="${root.id}"]`); - if (nodeElement == null) { - console.warn(`Node element not found: "${root.id}"`); - return 0; - } - - const nodeCode = root.code.trim(); - const nodeCodeHtml = escapeHtml(nodeCode); - const nodeCodeStart = codeElement.innerHTML.indexOf(nodeCodeHtml, codeStart); - if (nodeCodeStart === -1) { - console.warn(`Node code not found in code container: "${nodeCodeHtml}"`); - return 0; - } - - const nodeCodeWrapper = document.createElement('span'); - nodeCodeWrapper.classList.add('node-code'); - nodeCodeWrapper.dataset.nodeId = root.id; - nodeCodeWrapper.innerHTML = nodeCodeHtml; - codeElement.innerHTML = replaceAfter(codeElement.innerHTML, nodeCodeHtml, nodeCodeWrapper.outerHTML, codeStart); - - const nodeCodeContainer = codeElement.querySelector(`span.node-code[data-node-id="${root.id}"]`); - let nodeCodeLowerBound = 0; - for (const child of root.children) { - nodeCodeLowerBound = linkCodeToAstNodes(child, nodeCodeContainer!, nodeCodeLowerBound); - } - - const codeEnd = nodeCodeStart + nodeCodeContainer!.outerHTML.length; - return codeEnd; -} - -const importCode = (astRoot: JoinPoint, codeContainer: HTMLElement): void => { - const trimedCode = astRoot.code.trim(); - codeContainer.querySelector('code')!.innerHTML = escapeHtml(trimedCode); +const importCode = (code: string, codeContainer: HTMLElement): void => { + codeContainer.querySelector('code')!.innerHTML = code; - const numLines = countChar(trimedCode, '\n') + 1; + const numLines = countChar(code, '\n') + 1; const codeLines = codeContainer.querySelector('.lines')!; codeLines.innerText = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); } @@ -90,7 +58,6 @@ const importAst = (astRoot: JoinPoint, astContainer: HTMLElement, codeContainer: astContainer.innerHTML = ''; astContainer.appendChild(astFragment); - linkCodeToAstNodes(astRoot, codeContainer.querySelector('code')!); addEventListenersToAstNodes(astRoot); } diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts index c0279cdf3..3cfd16dc4 100644 --- a/Lara-JS/src-api/visualization/public/js/communication.ts +++ b/Lara-JS/src-api/visualization/public/js/communication.ts @@ -19,8 +19,8 @@ const webSocketOnMessage = (message: MessageEvent, continueButton: HTMLButtonEle switch (data.message) { case 'update': - const ast = JoinPoint.fromJson(data.ast); - importCode(ast, codeContainer); + const { code, ast } = data; + importCode(code, codeContainer); importAst(ast, astContainer, codeContainer); break; diff --git a/Lara-JS/src-api/visualization/public/js/utils.ts b/Lara-JS/src-api/visualization/public/js/utils.ts index 49576aac5..d9c2bcb14 100644 --- a/Lara-JS/src-api/visualization/public/js/utils.ts +++ b/Lara-JS/src-api/visualization/public/js/utils.ts @@ -1,21 +1,3 @@ -const escapeHtml = (text: string): string => { - var specialCharMap: { [char: string]: string } = { - '&': '&', - '<': '<', - '>': '>', - }; - - return text.replace(/[&<>]/g, (match) => specialCharMap[match]); -} - -const replaceAfter = (text: string, search: string, replace: string, start: number): string => { - const index = text.indexOf(search, start); - if (index === -1) { - return text; - } - return text.slice(0, index) + replace + text.slice(index + search.length); -} - const countChar = (str: string, char: string): number => { let count = 0; for (const c of str) { @@ -33,4 +15,4 @@ const createIcon = (name: string): HTMLElement => { return icon; } -export { escapeHtml, replaceAfter, countChar, createIcon }; \ No newline at end of file +export { countChar, createIcon }; \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index 8c480e5e0..7a8e5dbce 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -1,4 +1,4 @@ -import { countChar, createIcon, escapeHtml, replaceAfter } from './utils.js'; +import { countChar, createIcon, } from './utils.js'; import { addEventListenersToAstNodes } from './visualization.js'; const createAstNodeElement = (nodeId, text) => { const nodeElement = document.createElement('span'); @@ -31,36 +31,9 @@ const convertAstNodeToHtml = (root) => { fragment.appendChild(rootDropdown); return fragment; }; -const linkCodeToAstNodes = (root, codeElement, codeStart = 0) => { - const nodeElement = document.querySelector(`span.ast-node[data-node-id="${root.id}"]`); - if (nodeElement == null) { - console.warn(`Node element not found: "${root.id}"`); - return 0; - } - const nodeCode = root.code.trim(); - const nodeCodeHtml = escapeHtml(nodeCode); - const nodeCodeStart = codeElement.innerHTML.indexOf(nodeCodeHtml, codeStart); - if (nodeCodeStart === -1) { - console.warn(`Node code not found in code container: "${nodeCodeHtml}"`); - return 0; - } - const nodeCodeWrapper = document.createElement('span'); - nodeCodeWrapper.classList.add('node-code'); - nodeCodeWrapper.dataset.nodeId = root.id; - nodeCodeWrapper.innerHTML = nodeCodeHtml; - codeElement.innerHTML = replaceAfter(codeElement.innerHTML, nodeCodeHtml, nodeCodeWrapper.outerHTML, codeStart); - const nodeCodeContainer = codeElement.querySelector(`span.node-code[data-node-id="${root.id}"]`); - let nodeCodeLowerBound = 0; - for (const child of root.children) { - nodeCodeLowerBound = linkCodeToAstNodes(child, nodeCodeContainer, nodeCodeLowerBound); - } - const codeEnd = nodeCodeStart + nodeCodeContainer.outerHTML.length; - return codeEnd; -}; -const importCode = (astRoot, codeContainer) => { - const trimedCode = astRoot.code.trim(); - codeContainer.querySelector('code').innerHTML = escapeHtml(trimedCode); - const numLines = countChar(trimedCode, '\n') + 1; +const importCode = (code, codeContainer) => { + codeContainer.querySelector('code').innerHTML = code; + const numLines = countChar(code, '\n') + 1; const codeLines = codeContainer.querySelector('.lines'); codeLines.innerText = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); }; @@ -68,8 +41,6 @@ const importAst = (astRoot, astContainer, codeContainer) => { const astFragment = convertAstNodeToHtml(astRoot); astContainer.innerHTML = ''; astContainer.appendChild(astFragment); - - linkCodeToAstNodes(astRoot, codeContainer.querySelector('code')); addEventListenersToAstNodes(astRoot); }; export { importCode, importAst }; diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js index b7ec9148d..190a2ed6f 100644 --- a/LaraApi/src-lara/visualization/public/js/communication.js +++ b/LaraApi/src-lara/visualization/public/js/communication.js @@ -1,5 +1,4 @@ import { importAst, importCode } from "./ast-import.js"; -import JoinPoint from "./ToolJoinPoint.js"; const getWebSocket = () => { const url = `ẁs://${window.location.host}`; return new WebSocket(url); @@ -14,8 +13,8 @@ const webSocketOnMessage = (message, continueButton, astContainer, codeContainer const data = parseMessage(message); switch (data.message) { case 'update': - const ast = JoinPoint.fromJson(data.ast); - importCode(ast, codeContainer); + const { code, ast } = data; + importCode(code, codeContainer); importAst(ast, astContainer, codeContainer); break; case 'wait': diff --git a/LaraApi/src-lara/visualization/public/js/utils.js b/LaraApi/src-lara/visualization/public/js/utils.js index a92ce2a0b..067e204a1 100644 --- a/LaraApi/src-lara/visualization/public/js/utils.js +++ b/LaraApi/src-lara/visualization/public/js/utils.js @@ -1,18 +1,3 @@ -const escapeHtml = (text) => { - var specialCharMap = { - '&': '&', - '<': '<', - '>': '>', - }; - return text.replace(/[&<>]/g, (match) => specialCharMap[match]); -}; -const replaceAfter = (text, search, replace, start) => { - const index = text.indexOf(search, start); - if (index === -1) { - return text; - } - return text.slice(0, index) + replace + text.slice(index + search.length); -}; const countChar = (str, char) => { let count = 0; for (const c of str) { @@ -27,5 +12,5 @@ const createIcon = (name) => { icon.textContent = name; return icon; }; -export { escapeHtml, replaceAfter, countChar, createIcon }; +export { countChar, createIcon }; //# sourceMappingURL=utils.js.map \ No newline at end of file From f6ae29179ee560dfa508b6a1d5e2b3752fee5c77 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 18 Jul 2024 16:28:14 +0100 Subject: [PATCH 075/136] Remove code from ToolJoinPoint --- Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts | 8 ++------ .../src-lara/visualization/public/js/ToolJoinPoint.js | 9 +++------ 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts index e4a2f0b9f..d1507d0b5 100644 --- a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts +++ b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts @@ -1,13 +1,11 @@ export default class ToolJoinPoint { id: string; type: string; - code: string; children: ToolJoinPoint[]; - constructor(id: string, type: string, code: string, children: ToolJoinPoint[]) { + constructor(id: string, type: string, children: ToolJoinPoint[]) { this.id = id; this.type = type; - this.code = code; this.children = children; } @@ -15,7 +13,6 @@ export default class ToolJoinPoint { return new ToolJoinPoint( json.id, json.type, - json.code, json.children.map((child: any) => ToolJoinPoint.fromJson(child)), ); } @@ -24,12 +21,11 @@ export default class ToolJoinPoint { return { id: this.id, type: this.type, - code: this.code, children: this.children.map(child => child.toJson()), }; } public clone(): ToolJoinPoint { - return new ToolJoinPoint(this.id, this.type, this.code, this.children.map(child => child.clone())); + return new ToolJoinPoint(this.id, this.type, this.children.map(child => child.clone())); } }; \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js index 6d7da561a..786746cd4 100644 --- a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js +++ b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js @@ -1,27 +1,24 @@ export default class ToolJoinPoint { id; type; - code; children; - constructor(id, type, code, children) { + constructor(id, type, children) { this.id = id; this.type = type; - this.code = code; this.children = children; } static fromJson(json) { - return new ToolJoinPoint(json.id, json.type, json.code, json.children.map((child) => ToolJoinPoint.fromJson(child))); + return new ToolJoinPoint(json.id, json.type, json.children.map((child) => ToolJoinPoint.fromJson(child))); } toJson() { return { id: this.id, type: this.type, - code: this.code, children: this.children.map(child => child.toJson()), }; } clone() { - return new ToolJoinPoint(this.id, this.type, this.code, this.children.map(child => child.clone())); + return new ToolJoinPoint(this.id, this.type, this.children.map(child => child.clone())); } } ; From 4d12d1361eb27ba3fc11887991a58c67414615ef Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 18 Jul 2024 17:41:28 +0100 Subject: [PATCH 076/136] Fix highlighting of nodes with multiple code blocks --- .../src-api/visualization/public/js/visualization.ts | 10 ++++------ .../src-lara/visualization/public/js/visualization.js | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index d47ab680b..fe2282ccd 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -13,9 +13,8 @@ const getNodeRelatedElements = (nodeId: string): HTMLElement[] => { } const highlightNode = (nodeId: string): void => { - const nodeCode = document.querySelector(`.node-code[data-node-id="${nodeId}"]`)!; - if (nodeCode) - nodeCode.style.backgroundColor = 'var(--highlight-color)'; + const nodeCode = document.querySelectorAll(`.node-code[data-node-id="${nodeId}"]`)!; + nodeCode.forEach(elem => elem.style.backgroundColor = 'var(--highlight-color)'); const nodeElement = document.querySelector(`.ast-node[data-node-id="${nodeId}"]`)!; const nodeText = nodeElement.querySelector('.ast-node-text')!; @@ -31,9 +30,8 @@ const highlightNode = (nodeId: string): void => { } const unhighlightNode = (nodeId: string): void => { - const nodeCode = document.querySelector(`.node-code[data-node-id="${nodeId}"]`)!; - if (nodeCode) - nodeCode.style.backgroundColor = ''; + const nodeCode = document.querySelectorAll(`.node-code[data-node-id="${nodeId}"]`)!; + nodeCode.forEach(elem => elem.style.backgroundColor = ''); const nodeElement = document.querySelector(`.ast-node[data-node-id="${nodeId}"]`)!; const nodeText = nodeElement.querySelector('.ast-node-text')!; diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index 9b420a188..c0ac7be41 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -8,9 +8,8 @@ const getNodeRelatedElements = (nodeId) => { return Array.from(document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"], .node-code[data-node-id="${nodeId}"]`)); }; const highlightNode = (nodeId) => { - const nodeCode = document.querySelector(`.node-code[data-node-id="${nodeId}"]`); - if (nodeCode) - nodeCode.style.backgroundColor = 'var(--highlight-color)'; + const nodeCode = document.querySelectorAll(`.node-code[data-node-id="${nodeId}"]`); + nodeCode.forEach(elem => elem.style.backgroundColor = 'var(--highlight-color)'); const nodeElement = document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); const nodeText = nodeElement.querySelector('.ast-node-text'); nodeText.style.backgroundColor = 'var(--highlight-color)'; @@ -22,9 +21,8 @@ const highlightNode = (nodeId) => { } }; const unhighlightNode = (nodeId) => { - const nodeCode = document.querySelector(`.node-code[data-node-id="${nodeId}"]`); - if (nodeCode) - nodeCode.style.backgroundColor = ''; + const nodeCode = document.querySelectorAll(`.node-code[data-node-id="${nodeId}"]`); + nodeCode.forEach(elem => elem.style.backgroundColor = ''); const nodeElement = document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); const nodeText = nodeElement.querySelector('.ast-node-text'); nodeText.style.backgroundColor = ''; From 2a086b9f694d5d1338a6455f2af545048bab4a02 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 19 Jul 2024 08:26:51 +0100 Subject: [PATCH 077/136] Allow fixed highlighting of node on click --- .../visualization/public/js/ToolJoinPoint.ts | 38 ++++++++++---- .../visualization/public/js/ast-import.ts | 2 +- .../visualization/public/js/communication.ts | 8 ++- .../visualization/public/js/visualization.ts | 52 +++++++++++++------ .../visualization/public/js/ToolJoinPoint.js | 29 +++++++---- .../visualization/public/js/ast-import.js | 2 +- .../visualization/public/js/communication.js | 5 +- .../visualization/public/js/visualization.js | 39 ++++++++++---- 8 files changed, 121 insertions(+), 54 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts index d1507d0b5..855787def 100644 --- a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts +++ b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts @@ -1,31 +1,47 @@ export default class ToolJoinPoint { - id: string; - type: string; - children: ToolJoinPoint[]; + #id: string; + #type: string; + #children: ToolJoinPoint[]; constructor(id: string, type: string, children: ToolJoinPoint[]) { - this.id = id; - this.type = type; - this.children = children; + this.#id = id; + this.#type = type; + this.#children = children; + } + + get id(): string { + return this.#id; + } + + get type(): string { + return this.#type; + } + + get children(): ToolJoinPoint[] { + return this.#children } public static fromJson(json: any): ToolJoinPoint { return new ToolJoinPoint( json.id, json.type, - json.children.map((child: any) => ToolJoinPoint.fromJson(child)), + json.children.map((child: any) => ToolJoinPoint.fromJson(child)) ); } public toJson(): any { return { - id: this.id, - type: this.type, - children: this.children.map(child => child.toJson()), + id: this.#id, + type: this.#type, + children: this.#children.map((child) => child.toJson()), }; } public clone(): ToolJoinPoint { - return new ToolJoinPoint(this.id, this.type, this.children.map(child => child.clone())); + return new ToolJoinPoint( + this.#id, + this.#type, + this.#children.map((child) => child.clone()) + ); } }; \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index 74f48d0e7..a88387bd1 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -53,7 +53,7 @@ const importCode = (code: string, codeContainer: HTMLElement): void => { codeLines.innerText = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); } -const importAst = (astRoot: JoinPoint, astContainer: HTMLElement, codeContainer: HTMLElement): void => { +const importAst = (astRoot: JoinPoint, astContainer: HTMLElement): void => { const astFragment = convertAstNodeToHtml(astRoot); astContainer.innerHTML = ''; astContainer.appendChild(astFragment); diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts index 3cfd16dc4..6a1cc9233 100644 --- a/Lara-JS/src-api/visualization/public/js/communication.ts +++ b/Lara-JS/src-api/visualization/public/js/communication.ts @@ -1,5 +1,4 @@ import { importAst, importCode } from "./ast-import.js"; -import JoinPoint from "./ToolJoinPoint.js"; const getWebSocket = (): WebSocket => { const url = `ẁs://${window.location.host}`; @@ -20,8 +19,13 @@ const webSocketOnMessage = (message: MessageEvent, continueButton: HTMLButtonEle switch (data.message) { case 'update': const { code, ast } = data; + const buttonDisabled = continueButton.disabled; + + continueButton.disabled = true; importCode(code, codeContainer); - importAst(ast, astContainer, codeContainer); + importAst(ast, astContainer); + continueButton.disabled = buttonDisabled; + break; case 'wait': diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index fe2282ccd..e5a3c0674 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -2,15 +2,15 @@ import JoinPoint from "./ToolJoinPoint.js"; const getNodeElement = (nodeId: string): HTMLSpanElement | null => { return document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); -} +}; const getNodeDropdown = (nodeId: string): HTMLDivElement | null => { return document.querySelector(`.ast-node-dropdown[data-node-id="${nodeId}"]`); -} +}; const getNodeRelatedElements = (nodeId: string): HTMLElement[] => { return Array.from(document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"], .node-code[data-node-id="${nodeId}"]`)); -} +}; const highlightNode = (nodeId: string): void => { const nodeCode = document.querySelectorAll(`.node-code[data-node-id="${nodeId}"]`)!; @@ -27,7 +27,7 @@ const highlightNode = (nodeId: string): void => { parentNode = parentNode.parentElement?.previousSibling; } -} +}; const unhighlightNode = (nodeId: string): void => { const nodeCode = document.querySelectorAll(`.node-code[data-node-id="${nodeId}"]`)!; @@ -44,7 +44,36 @@ const unhighlightNode = (nodeId: string): void => { parentNode = parentNode.parentElement?.previousSibling as HTMLElement | null | undefined; } -} +}; + +const addHighlighingEvents = (() => { + let selectedNodeId: string | null = null; + return (nodeId: string): void => { + const nodeRelatedElements = getNodeRelatedElements(nodeId); + for (const nodeRelatedElement of nodeRelatedElements) { + nodeRelatedElement.addEventListener('mouseover', event => { + highlightNode(nodeId); + event.stopPropagation(); + }); + nodeRelatedElement.addEventListener('mouseout', event => { + unhighlightNode(nodeId); + if (selectedNodeId !== null) { + highlightNode(selectedNodeId); + } + event.stopPropagation(); + }); + nodeRelatedElement.addEventListener('click', event => { + if (selectedNodeId !== null) { + unhighlightNode(selectedNodeId); + } + + selectedNodeId = nodeId; + highlightNode(nodeId); + event.stopPropagation(); + }); + } + }; +})(); const addEventListenersToAstNodes = (root: JoinPoint): void => { const nodeId = root.id; @@ -62,18 +91,7 @@ const addEventListenersToAstNodes = (root: JoinPoint): void => { chevron.textContent = nodeCollapsed ? 'keyboard_arrow_right' : 'keyboard_arrow_down'; }); - const nodeRelatedElements = getNodeRelatedElements(nodeId); - for (const nodeRelatedElement of nodeRelatedElements) { - nodeRelatedElement.addEventListener('mouseover', event => { - highlightNode(nodeId); - event.stopPropagation(); - }); - nodeRelatedElement.addEventListener('mouseout', event => { - unhighlightNode(nodeId); - event.stopPropagation(); - }); - } - + addHighlighingEvents(nodeId); root.children.forEach(child => addEventListenersToAstNodes(child)); }; diff --git a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js index 786746cd4..2bbcd1123 100644 --- a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js +++ b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js @@ -1,24 +1,33 @@ export default class ToolJoinPoint { - id; - type; - children; + #id; + #type; + #children; constructor(id, type, children) { - this.id = id; - this.type = type; - this.children = children; + this.#id = id; + this.#type = type; + this.#children = children; + } + get id() { + return this.#id; + } + get type() { + return this.#type; + } + get children() { + return this.#children; } static fromJson(json) { return new ToolJoinPoint(json.id, json.type, json.children.map((child) => ToolJoinPoint.fromJson(child))); } toJson() { return { - id: this.id, - type: this.type, - children: this.children.map(child => child.toJson()), + id: this.#id, + type: this.#type, + children: this.#children.map((child) => child.toJson()), }; } clone() { - return new ToolJoinPoint(this.id, this.type, this.children.map(child => child.clone())); + return new ToolJoinPoint(this.#id, this.#type, this.#children.map((child) => child.clone())); } } ; diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index 7a8e5dbce..c148e1da9 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -37,7 +37,7 @@ const importCode = (code, codeContainer) => { const codeLines = codeContainer.querySelector('.lines'); codeLines.innerText = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); }; -const importAst = (astRoot, astContainer, codeContainer) => { +const importAst = (astRoot, astContainer) => { const astFragment = convertAstNodeToHtml(astRoot); astContainer.innerHTML = ''; astContainer.appendChild(astFragment); diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js index 190a2ed6f..4a189b8da 100644 --- a/LaraApi/src-lara/visualization/public/js/communication.js +++ b/LaraApi/src-lara/visualization/public/js/communication.js @@ -14,8 +14,11 @@ const webSocketOnMessage = (message, continueButton, astContainer, codeContainer switch (data.message) { case 'update': const { code, ast } = data; + const buttonDisabled = continueButton.disabled; + continueButton.disabled = true; importCode(code, codeContainer); - importAst(ast, astContainer, codeContainer); + importAst(ast, astContainer); + continueButton.disabled = buttonDisabled; break; case 'wait': continueButton.disabled = false; diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index c0ac7be41..249e16970 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -33,6 +33,33 @@ const unhighlightNode = (nodeId) => { parentNode = parentNode.parentElement?.previousSibling; } }; +const addHighlighingEvents = (() => { + let selectedNodeId = null; + return (nodeId) => { + const nodeRelatedElements = getNodeRelatedElements(nodeId); + for (const nodeRelatedElement of nodeRelatedElements) { + nodeRelatedElement.addEventListener('mouseover', event => { + highlightNode(nodeId); + event.stopPropagation(); + }); + nodeRelatedElement.addEventListener('mouseout', event => { + unhighlightNode(nodeId); + if (selectedNodeId !== null) { + highlightNode(selectedNodeId); + } + event.stopPropagation(); + }); + nodeRelatedElement.addEventListener('click', event => { + if (selectedNodeId !== null) { + unhighlightNode(selectedNodeId); + } + selectedNodeId = nodeId; + highlightNode(nodeId); + event.stopPropagation(); + }); + } + }; +})(); const addEventListenersToAstNodes = (root) => { const nodeId = root.id; const nodeElement = getNodeElement(nodeId); @@ -45,17 +72,7 @@ const addEventListenersToAstNodes = (root) => { const chevron = nodeDropdownButton.children[0]; chevron.textContent = nodeCollapsed ? 'keyboard_arrow_right' : 'keyboard_arrow_down'; }); - const nodeRelatedElements = getNodeRelatedElements(nodeId); - for (const nodeRelatedElement of nodeRelatedElements) { - nodeRelatedElement.addEventListener('mouseover', event => { - highlightNode(nodeId); - event.stopPropagation(); - }); - nodeRelatedElement.addEventListener('mouseout', event => { - unhighlightNode(nodeId); - event.stopPropagation(); - }); - } + addHighlighingEvents(nodeId); root.children.forEach(child => addEventListenersToAstNodes(child)); }; export { addEventListenersToAstNodes }; From 5147724124bf7d14e0dd2133cebefc92ec6556dc Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 19 Jul 2024 08:40:15 +0100 Subject: [PATCH 078/136] Fix invalid CSS --- Lara-JS/src-api/visualization/public/css/styles.css | 1 - Lara-JS/src-api/visualization/public/index.html | 1 - 2 files changed, 2 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index c2a0a9856..0dac0650c 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -158,7 +158,6 @@ main { #code-container .lines, #code-container code { font-family: "JetBrains Mono", monospace; - font-optical-sizing: auto; } #code-container .lines { diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 1e9d7271b..aaf96880c 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -6,7 +6,6 @@ - LARA Visualization Tool From 97469b047f626afe2b39b3a3d476157e704bfe3b Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 19 Jul 2024 09:02:32 +0100 Subject: [PATCH 079/136] Allow scroll to position on node click --- .../src-api/visualization/public/js/visualization.ts | 11 ++++++++--- .../src-lara/visualization/public/js/visualization.js | 9 ++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index e5a3c0674..ec050b6eb 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -57,9 +57,8 @@ const addHighlighingEvents = (() => { }); nodeRelatedElement.addEventListener('mouseout', event => { unhighlightNode(nodeId); - if (selectedNodeId !== null) { + if (selectedNodeId !== null) highlightNode(selectedNodeId); - } event.stopPropagation(); }); nodeRelatedElement.addEventListener('click', event => { @@ -69,6 +68,10 @@ const addHighlighingEvents = (() => { selectedNodeId = nodeId; highlightNode(nodeId); + for (const nodeRelatedElement of nodeRelatedElements.slice(0, 2)) { + nodeRelatedElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + event.stopPropagation(); }); } @@ -83,12 +86,14 @@ const addEventListenersToAstNodes = (root: JoinPoint): void => { const nodeDropdown = getNodeDropdown(nodeId)!; let nodeCollapsed = false; - nodeDropdownButton.addEventListener('click', () => { + nodeDropdownButton.addEventListener('click', event => { nodeCollapsed = !nodeCollapsed; nodeDropdown.style.display = nodeCollapsed ? 'none' : 'block'; const chevron = nodeDropdownButton.children[0] as HTMLElement; chevron.textContent = nodeCollapsed ? 'keyboard_arrow_right' : 'keyboard_arrow_down'; + + event.stopPropagation(); }); addHighlighingEvents(nodeId); diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index 249e16970..490c33b9b 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -44,9 +44,8 @@ const addHighlighingEvents = (() => { }); nodeRelatedElement.addEventListener('mouseout', event => { unhighlightNode(nodeId); - if (selectedNodeId !== null) { + if (selectedNodeId !== null) highlightNode(selectedNodeId); - } event.stopPropagation(); }); nodeRelatedElement.addEventListener('click', event => { @@ -55,6 +54,9 @@ const addHighlighingEvents = (() => { } selectedNodeId = nodeId; highlightNode(nodeId); + for (const nodeRelatedElement of nodeRelatedElements.slice(0, 2)) { + nodeRelatedElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } event.stopPropagation(); }); } @@ -66,11 +68,12 @@ const addEventListenersToAstNodes = (root) => { const nodeDropdownButton = nodeElement.children[0]; const nodeDropdown = getNodeDropdown(nodeId); let nodeCollapsed = false; - nodeDropdownButton.addEventListener('click', () => { + nodeDropdownButton.addEventListener('click', event => { nodeCollapsed = !nodeCollapsed; nodeDropdown.style.display = nodeCollapsed ? 'none' : 'block'; const chevron = nodeDropdownButton.children[0]; chevron.textContent = nodeCollapsed ? 'keyboard_arrow_right' : 'keyboard_arrow_down'; + event.stopPropagation(); }); addHighlighingEvents(nodeId); root.children.forEach(child => addEventListenersToAstNodes(child)); From 10ce6816c3610d0d8d2d293c63a7a939c54c8b89 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 19 Jul 2024 10:04:25 +0100 Subject: [PATCH 080/136] Fix non-closed pre tag --- Lara-JS/src-api/visualization/public/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index aaf96880c..68dd2c3b8 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -23,7 +23,7 @@

LARA Visualization Tool

- +
\ No newline at end of file From 87e1a077e7b7aeb8765102fe25b335ac05635887 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 19 Jul 2024 10:42:01 +0100 Subject: [PATCH 081/136] Make hover highlight weaker --- .../src-api/visualization/public/js/visualization.ts | 12 ++++++------ .../visualization/public/js/visualization.js | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index ec050b6eb..8dda3c0f3 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -12,13 +12,13 @@ const getNodeRelatedElements = (nodeId: string): HTMLElement[] => { return Array.from(document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"], .node-code[data-node-id="${nodeId}"]`)); }; -const highlightNode = (nodeId: string): void => { +const highlightNode = (nodeId: string, strong: boolean): void => { const nodeCode = document.querySelectorAll(`.node-code[data-node-id="${nodeId}"]`)!; - nodeCode.forEach(elem => elem.style.backgroundColor = 'var(--highlight-color)'); + nodeCode.forEach(elem => elem.style.backgroundColor = strong ? 'var(--highlight-color)' : 'var(--secondary-highlight-color)'); const nodeElement = document.querySelector(`.ast-node[data-node-id="${nodeId}"]`)!; const nodeText = nodeElement.querySelector('.ast-node-text')!; - nodeText.style.backgroundColor = 'var(--highlight-color)'; + nodeText.style.backgroundColor = strong ? 'var(--highlight-color)' : 'var(--secondary-highlight-color)'; let parentNode = nodeElement.parentElement?.previousSibling; while (parentNode instanceof HTMLElement && parentNode.classList.contains('ast-node')) { @@ -52,13 +52,13 @@ const addHighlighingEvents = (() => { const nodeRelatedElements = getNodeRelatedElements(nodeId); for (const nodeRelatedElement of nodeRelatedElements) { nodeRelatedElement.addEventListener('mouseover', event => { - highlightNode(nodeId); + highlightNode(nodeId, false); event.stopPropagation(); }); nodeRelatedElement.addEventListener('mouseout', event => { unhighlightNode(nodeId); if (selectedNodeId !== null) - highlightNode(selectedNodeId); + highlightNode(selectedNodeId, true); event.stopPropagation(); }); nodeRelatedElement.addEventListener('click', event => { @@ -67,7 +67,7 @@ const addHighlighingEvents = (() => { } selectedNodeId = nodeId; - highlightNode(nodeId); + highlightNode(nodeId, true); for (const nodeRelatedElement of nodeRelatedElements.slice(0, 2)) { nodeRelatedElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); } diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index 490c33b9b..011301491 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -7,12 +7,12 @@ const getNodeDropdown = (nodeId) => { const getNodeRelatedElements = (nodeId) => { return Array.from(document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"], .node-code[data-node-id="${nodeId}"]`)); }; -const highlightNode = (nodeId) => { +const highlightNode = (nodeId, strong) => { const nodeCode = document.querySelectorAll(`.node-code[data-node-id="${nodeId}"]`); - nodeCode.forEach(elem => elem.style.backgroundColor = 'var(--highlight-color)'); + nodeCode.forEach(elem => elem.style.backgroundColor = strong ? 'var(--highlight-color)' : 'var(--secondary-highlight-color)'); const nodeElement = document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); const nodeText = nodeElement.querySelector('.ast-node-text'); - nodeText.style.backgroundColor = 'var(--highlight-color)'; + nodeText.style.backgroundColor = strong ? 'var(--highlight-color)' : 'var(--secondary-highlight-color)'; let parentNode = nodeElement.parentElement?.previousSibling; while (parentNode instanceof HTMLElement && parentNode.classList.contains('ast-node')) { const parentNodeText = parentNode.querySelector('.ast-node-text'); @@ -39,13 +39,13 @@ const addHighlighingEvents = (() => { const nodeRelatedElements = getNodeRelatedElements(nodeId); for (const nodeRelatedElement of nodeRelatedElements) { nodeRelatedElement.addEventListener('mouseover', event => { - highlightNode(nodeId); + highlightNode(nodeId, false); event.stopPropagation(); }); nodeRelatedElement.addEventListener('mouseout', event => { unhighlightNode(nodeId); if (selectedNodeId !== null) - highlightNode(selectedNodeId); + highlightNode(selectedNodeId, true); event.stopPropagation(); }); nodeRelatedElement.addEventListener('click', event => { @@ -53,7 +53,7 @@ const addHighlighingEvents = (() => { unhighlightNode(selectedNodeId); } selectedNodeId = nodeId; - highlightNode(nodeId); + highlightNode(nodeId, true); for (const nodeRelatedElement of nodeRelatedElements.slice(0, 2)) { nodeRelatedElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); } From 8fb069d2dc88eb87f01c2cacf34c474441bc8593 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 19 Jul 2024 11:11:24 +0100 Subject: [PATCH 082/136] Fix accessibility issues --- .../visualization/public/css/color-scheme.css | 13 +++++++------ .../visualization/public/js/visualization.ts | 3 +++ .../visualization/public/js/visualization.js | 2 ++ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/color-scheme.css b/Lara-JS/src-api/visualization/public/css/color-scheme.css index 8f063e79b..9785fa804 100644 --- a/Lara-JS/src-api/visualization/public/css/color-scheme.css +++ b/Lara-JS/src-api/visualization/public/css/color-scheme.css @@ -5,7 +5,8 @@ --lighter-gray: #e4e4e4; --light-gray: #d4d4d4; --gray: #a2a2a2; - --dark-gray: #313131; + --dark-gray: #747474; + --darker-gray: #313131; --black: #000; --light-blue: #19d8ff; --transparent-light-blue: #19d8ff66; @@ -15,16 +16,16 @@ :root { --bg-color: var(--white); - --text-color: var(--dark-gray); + --text-color: var(--darker-gray); --header-color: var(--lighter-gray); --border-color: var(--gray); - --icon-color: var(--dark-gray); + --icon-color: var(--darker-gray); --button-bg-color: var(--white); --button-hover-bg-color: var(--lighter-gray); --button-disabled-bg-color: var(--lighter-gray); - --button-disabled-text-color: var(--gray); - --button-disabled-icon-color: var(--gray); + --button-disabled-text-color: var(--darker-gray); + --button-disabled-icon-color: var(--darker-gray); --tab-bg-color: var(--white); --tab-hover-bg-color: var(--lighter-gray); @@ -33,5 +34,5 @@ --highlight-color: var(--light-blue); --secondary-highlight-color: var(--transparent-light-blue); - --line-num-color: var(--gray); + --line-num-color: var(--dark-gray); } \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index 8dda3c0f3..a5d6bc8b0 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -61,6 +61,9 @@ const addHighlighingEvents = (() => { highlightNode(selectedNodeId, true); event.stopPropagation(); }); + + nodeRelatedElement.tabIndex = 0; + nodeRelatedElement.role = "button"; nodeRelatedElement.addEventListener('click', event => { if (selectedNodeId !== null) { unhighlightNode(selectedNodeId); diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index 011301491..a208f5ed2 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -48,6 +48,8 @@ const addHighlighingEvents = (() => { highlightNode(selectedNodeId, true); event.stopPropagation(); }); + nodeRelatedElement.tabIndex = 0; + nodeRelatedElement.role = "button"; nodeRelatedElement.addEventListener('click', event => { if (selectedNodeId !== null) { unhighlightNode(selectedNodeId); From be0d21a3111de51368b347ba43ad77bb8fb5acf7 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 19 Jul 2024 13:28:34 +0100 Subject: [PATCH 083/136] Refactor highlighting and remove dropdown from leaf nodes --- .../visualization/public/css/color-scheme.css | 6 +- .../visualization/public/js/ast-import.ts | 59 ++++++++++++++----- .../visualization/public/js/visualization.ts | 55 ++++++++--------- .../visualization/public/js/ast-import.js | 48 +++++++++++---- .../visualization/public/js/visualization.js | 37 ++++++------ 5 files changed, 128 insertions(+), 77 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/color-scheme.css b/Lara-JS/src-api/visualization/public/css/color-scheme.css index 9785fa804..941b92900 100644 --- a/Lara-JS/src-api/visualization/public/css/color-scheme.css +++ b/Lara-JS/src-api/visualization/public/css/color-scheme.css @@ -9,7 +9,8 @@ --darker-gray: #313131; --black: #000; --light-blue: #19d8ff; - --transparent-light-blue: #19d8ff66; + --strong-translucid-light-blue: #19d8ff66; + --weak-translucid-light-blue: #19d8ff33; } /* Color Scheme */ @@ -32,7 +33,8 @@ --tab-active-bg-color: var(--light-gray); --highlight-color: var(--light-blue); - --secondary-highlight-color: var(--transparent-light-blue); + --secondary-highlight-color: var(--strong-translucid-light-blue); + --tertiary-highlight-color: var(--weak-translucid-light-blue); --line-num-color: var(--dark-gray); } \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index a88387bd1..4672e6605 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -2,20 +2,40 @@ import { countChar, createIcon, } from './utils.js'; import { addEventListenersToAstNodes } from './visualization.js'; import JoinPoint from './ToolJoinPoint.js'; -const createAstNodeElement = (nodeId: string, text: string): HTMLSpanElement => { +const createAstNodeDropdownButton = (nodeId: string): HTMLButtonElement => { + const dropdownButton = document.createElement('button'); + + const chevronIcon = createIcon("keyboard_arrow_down"); + dropdownButton.appendChild(chevronIcon); + + return dropdownButton; +}; + +const createDropdownButtonOnClick = (dropdown: HTMLElement) => { + let nodeCollapsed = false; + return (event: Event): void => { + nodeCollapsed = !nodeCollapsed; + dropdown.style.display = nodeCollapsed ? "none" : "block"; + + const dropdownButton = event.currentTarget as HTMLElement; + const chevronIcon = dropdownButton.children[0] as HTMLElement; + chevronIcon.textContent = nodeCollapsed ? "keyboard_arrow_right" : "keyboard_arrow_down"; + + event.stopPropagation(); + }; +}; + +const createAstNodeElement = (nodeId: string, text: string, dropdownButton: HTMLElement): HTMLSpanElement => { const nodeElement = document.createElement('span'); nodeElement.classList.add('ast-node'); nodeElement.dataset.nodeId = nodeId; - const chevronDropdownButton = document.createElement('button'); - chevronDropdownButton.appendChild(createIcon('keyboard_arrow_down')); - const nodeText = document.createElement('span'); nodeText.classList.add('ast-node-text'); nodeText.textContent = text; - nodeElement.appendChild(chevronDropdownButton); + nodeElement.appendChild(dropdownButton); nodeElement.appendChild(nodeText); return nodeElement; @@ -30,17 +50,28 @@ const createAstNodeDropdown = (nodeId: string): HTMLDivElement => { } const convertAstNodeToHtml = (root: JoinPoint): DocumentFragment => { - const rootElement = createAstNodeElement(root.id, root.type); - const rootDropdown = createAstNodeDropdown(root.id); + const fragment = new DocumentFragment(); + const dropdownButton = createAstNodeDropdownButton(root.id); + const rootElement = createAstNodeElement(root.id, root.type, dropdownButton); - for (const node of root.children) { - const descendantNodeElements = convertAstNodeToHtml(node); - rootDropdown.appendChild(descendantNodeElements); - } + if (root.children.length > 0) { + const rootDropdown = createAstNodeDropdown(root.id); - const fragment = new DocumentFragment(); - fragment.appendChild(rootElement); - fragment.appendChild(rootDropdown); + for (const node of root.children) { + const descendantNodeElements = convertAstNodeToHtml(node); + rootDropdown.appendChild(descendantNodeElements); + } + + dropdownButton.addEventListener('click', createDropdownButtonOnClick(rootDropdown)); + + fragment.appendChild(rootElement); + fragment.appendChild(rootDropdown); + } else { + dropdownButton.style.visibility = 'hidden'; + dropdownButton.disabled = true; + + fragment.appendChild(rootElement); + } return fragment; }; diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index a5d6bc8b0..760e92275 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -1,15 +1,11 @@ import JoinPoint from "./ToolJoinPoint.js"; -const getNodeElement = (nodeId: string): HTMLSpanElement | null => { - return document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); -}; - -const getNodeDropdown = (nodeId: string): HTMLDivElement | null => { - return document.querySelector(`.ast-node-dropdown[data-node-id="${nodeId}"]`); -}; +const getNodeElement = (nodeId: string): HTMLElement | null => { + return document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); +} const getNodeRelatedElements = (nodeId: string): HTMLElement[] => { - return Array.from(document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"], .node-code[data-node-id="${nodeId}"]`)); + return Array.from(document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"] .ast-node-text, .node-code[data-node-id="${nodeId}"]`)); }; const highlightNode = (nodeId: string, strong: boolean): void => { @@ -23,7 +19,7 @@ const highlightNode = (nodeId: string, strong: boolean): void => { let parentNode = nodeElement.parentElement?.previousSibling; while (parentNode instanceof HTMLElement && parentNode.classList.contains('ast-node')) { const parentNodeText = parentNode.querySelector('.ast-node-text')!; - parentNodeText.style.backgroundColor = 'var(--secondary-highlight-color)'; + parentNodeText.style.backgroundColor = strong ? 'var(--secondary-highlight-color)' : 'var(--tertiary-highlight-color)'; parentNode = parentNode.parentElement?.previousSibling; } @@ -53,6 +49,8 @@ const addHighlighingEvents = (() => { for (const nodeRelatedElement of nodeRelatedElements) { nodeRelatedElement.addEventListener('mouseover', event => { highlightNode(nodeId, false); + if (selectedNodeId !== null) + highlightNode(selectedNodeId, true); event.stopPropagation(); }); nodeRelatedElement.addEventListener('mouseout', event => { @@ -63,41 +61,38 @@ const addHighlighingEvents = (() => { }); nodeRelatedElement.tabIndex = 0; - nodeRelatedElement.role = "button"; nodeRelatedElement.addEventListener('click', event => { + event.stopPropagation(); + if (selectedNodeId !== null) { unhighlightNode(selectedNodeId); + if (selectedNodeId === nodeId) { + selectedNodeId = null; + return; + } } - + selectedNodeId = nodeId; highlightNode(nodeId, true); - for (const nodeRelatedElement of nodeRelatedElements.slice(0, 2)) { - nodeRelatedElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); - } - event.stopPropagation(); + const nodeElement = getNodeElement(nodeId)!; + const firstNodeCodeBlock = document.querySelector('.node-code[data-node-id]')!; + for (const element of [nodeElement, firstNodeCodeBlock]) { + element.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } }); + nodeRelatedElement.addEventListener('keydown', event => { + if (event.key === 'Enter') { + nodeRelatedElement.click(); + } + event.stopPropagation(); + }) } }; })(); const addEventListenersToAstNodes = (root: JoinPoint): void => { const nodeId = root.id; - const nodeElement = getNodeElement(nodeId)!; - const nodeDropdownButton = nodeElement.children[0] as HTMLButtonElement; - - const nodeDropdown = getNodeDropdown(nodeId)!; - let nodeCollapsed = false; - - nodeDropdownButton.addEventListener('click', event => { - nodeCollapsed = !nodeCollapsed; - nodeDropdown.style.display = nodeCollapsed ? 'none' : 'block'; - - const chevron = nodeDropdownButton.children[0] as HTMLElement; - chevron.textContent = nodeCollapsed ? 'keyboard_arrow_right' : 'keyboard_arrow_down'; - - event.stopPropagation(); - }); addHighlighingEvents(nodeId); root.children.forEach(child => addEventListenersToAstNodes(child)); diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index c148e1da9..d3fb02355 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -1,15 +1,30 @@ import { countChar, createIcon, } from './utils.js'; import { addEventListenersToAstNodes } from './visualization.js'; -const createAstNodeElement = (nodeId, text) => { +const createAstNodeDropdownButton = (nodeId) => { + const dropdownButton = document.createElement('button'); + const chevronIcon = createIcon("keyboard_arrow_down"); + dropdownButton.appendChild(chevronIcon); + return dropdownButton; +}; +const createDropdownButtonOnClick = (dropdown) => { + let nodeCollapsed = false; + return (event) => { + nodeCollapsed = !nodeCollapsed; + dropdown.style.display = nodeCollapsed ? "none" : "block"; + const dropdownButton = event.currentTarget; + const chevronIcon = dropdownButton.children[0]; + chevronIcon.textContent = nodeCollapsed ? "keyboard_arrow_right" : "keyboard_arrow_down"; + event.stopPropagation(); + }; +}; +const createAstNodeElement = (nodeId, text, dropdownButton) => { const nodeElement = document.createElement('span'); nodeElement.classList.add('ast-node'); nodeElement.dataset.nodeId = nodeId; - const chevronDropdownButton = document.createElement('button'); - chevronDropdownButton.appendChild(createIcon('keyboard_arrow_down')); const nodeText = document.createElement('span'); nodeText.classList.add('ast-node-text'); nodeText.textContent = text; - nodeElement.appendChild(chevronDropdownButton); + nodeElement.appendChild(dropdownButton); nodeElement.appendChild(nodeText); return nodeElement; }; @@ -20,15 +35,24 @@ const createAstNodeDropdown = (nodeId) => { return dropdown; }; const convertAstNodeToHtml = (root) => { - const rootElement = createAstNodeElement(root.id, root.type); - const rootDropdown = createAstNodeDropdown(root.id); - for (const node of root.children) { - const descendantNodeElements = convertAstNodeToHtml(node); - rootDropdown.appendChild(descendantNodeElements); - } const fragment = new DocumentFragment(); - fragment.appendChild(rootElement); - fragment.appendChild(rootDropdown); + const dropdownButton = createAstNodeDropdownButton(root.id); + const rootElement = createAstNodeElement(root.id, root.type, dropdownButton); + if (root.children.length > 0) { + const rootDropdown = createAstNodeDropdown(root.id); + for (const node of root.children) { + const descendantNodeElements = convertAstNodeToHtml(node); + rootDropdown.appendChild(descendantNodeElements); + } + dropdownButton.addEventListener('click', createDropdownButtonOnClick(rootDropdown)); + fragment.appendChild(rootElement); + fragment.appendChild(rootDropdown); + } + else { + dropdownButton.style.visibility = 'hidden'; + dropdownButton.disabled = true; + fragment.appendChild(rootElement); + } return fragment; }; const importCode = (code, codeContainer) => { diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index a208f5ed2..41d12c1eb 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -1,11 +1,8 @@ const getNodeElement = (nodeId) => { return document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); }; -const getNodeDropdown = (nodeId) => { - return document.querySelector(`.ast-node-dropdown[data-node-id="${nodeId}"]`); -}; const getNodeRelatedElements = (nodeId) => { - return Array.from(document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"], .node-code[data-node-id="${nodeId}"]`)); + return Array.from(document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"] .ast-node-text, .node-code[data-node-id="${nodeId}"]`)); }; const highlightNode = (nodeId, strong) => { const nodeCode = document.querySelectorAll(`.node-code[data-node-id="${nodeId}"]`); @@ -16,7 +13,7 @@ const highlightNode = (nodeId, strong) => { let parentNode = nodeElement.parentElement?.previousSibling; while (parentNode instanceof HTMLElement && parentNode.classList.contains('ast-node')) { const parentNodeText = parentNode.querySelector('.ast-node-text'); - parentNodeText.style.backgroundColor = 'var(--secondary-highlight-color)'; + parentNodeText.style.backgroundColor = strong ? 'var(--secondary-highlight-color)' : 'var(--tertiary-highlight-color)'; parentNode = parentNode.parentElement?.previousSibling; } }; @@ -40,6 +37,8 @@ const addHighlighingEvents = (() => { for (const nodeRelatedElement of nodeRelatedElements) { nodeRelatedElement.addEventListener('mouseover', event => { highlightNode(nodeId, false); + if (selectedNodeId !== null) + highlightNode(selectedNodeId, true); event.stopPropagation(); }); nodeRelatedElement.addEventListener('mouseout', event => { @@ -49,15 +48,26 @@ const addHighlighingEvents = (() => { event.stopPropagation(); }); nodeRelatedElement.tabIndex = 0; - nodeRelatedElement.role = "button"; nodeRelatedElement.addEventListener('click', event => { + event.stopPropagation(); if (selectedNodeId !== null) { unhighlightNode(selectedNodeId); + if (selectedNodeId === nodeId) { + selectedNodeId = null; + return; + } } selectedNodeId = nodeId; highlightNode(nodeId, true); - for (const nodeRelatedElement of nodeRelatedElements.slice(0, 2)) { - nodeRelatedElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); + const nodeElement = getNodeElement(nodeId); + const firstNodeCodeBlock = document.querySelector('.node-code[data-node-id]'); + for (const element of [nodeElement, firstNodeCodeBlock]) { + element.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + }); + nodeRelatedElement.addEventListener('keydown', event => { + if (event.key === 'Enter') { + nodeRelatedElement.click(); } event.stopPropagation(); }); @@ -66,17 +76,6 @@ const addHighlighingEvents = (() => { })(); const addEventListenersToAstNodes = (root) => { const nodeId = root.id; - const nodeElement = getNodeElement(nodeId); - const nodeDropdownButton = nodeElement.children[0]; - const nodeDropdown = getNodeDropdown(nodeId); - let nodeCollapsed = false; - nodeDropdownButton.addEventListener('click', event => { - nodeCollapsed = !nodeCollapsed; - nodeDropdown.style.display = nodeCollapsed ? 'none' : 'block'; - const chevron = nodeDropdownButton.children[0]; - chevron.textContent = nodeCollapsed ? 'keyboard_arrow_right' : 'keyboard_arrow_down'; - event.stopPropagation(); - }); addHighlighingEvents(nodeId); root.children.forEach(child => addEventListenersToAstNodes(child)); }; From 83c022e51cad618c75aceb77dc63a22b79649c9c Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 19 Jul 2024 15:59:15 +0100 Subject: [PATCH 084/136] Refactor aspect of AST leaf node dropdown button --- .../src-api/visualization/public/css/color-scheme.css | 3 +-- Lara-JS/src-api/visualization/public/css/styles.css | 11 ++++++----- Lara-JS/src-api/visualization/public/js/ast-import.ts | 1 - .../src-lara/visualization/public/js/ast-import.js | 1 - 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/color-scheme.css b/Lara-JS/src-api/visualization/public/css/color-scheme.css index 941b92900..f94ee20b3 100644 --- a/Lara-JS/src-api/visualization/public/css/color-scheme.css +++ b/Lara-JS/src-api/visualization/public/css/color-scheme.css @@ -21,12 +21,11 @@ --header-color: var(--lighter-gray); --border-color: var(--gray); --icon-color: var(--darker-gray); + --disabled-icon-color: var(--gray); --button-bg-color: var(--white); --button-hover-bg-color: var(--lighter-gray); --button-disabled-bg-color: var(--lighter-gray); - --button-disabled-text-color: var(--darker-gray); - --button-disabled-icon-color: var(--darker-gray); --tab-bg-color: var(--white); --tab-hover-bg-color: var(--lighter-gray); diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 0dac0650c..e2bc3657c 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -39,14 +39,9 @@ button:hover { button:disabled { background-color: var(--button-disabled-bg-color); - color: var(--button-disabled-text-color); cursor: not-allowed; } -button:disabled .icon { - color: var(--button-disabled-icon-color); -} - header { padding: 0.5em; @@ -139,6 +134,12 @@ main { background-color: var(--bg-color); border: none; border-radius: 50%; + + cursor: default; +} + +.ast-node button:disabled .icon { + color: var(--disabled-icon-color); } .ast-node-dropdown { diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index 4672e6605..f1fd86215 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -67,7 +67,6 @@ const convertAstNodeToHtml = (root: JoinPoint): DocumentFragment => { fragment.appendChild(rootElement); fragment.appendChild(rootDropdown); } else { - dropdownButton.style.visibility = 'hidden'; dropdownButton.disabled = true; fragment.appendChild(rootElement); diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index d3fb02355..e65dc7791 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -49,7 +49,6 @@ const convertAstNodeToHtml = (root) => { fragment.appendChild(rootDropdown); } else { - dropdownButton.style.visibility = 'hidden'; dropdownButton.disabled = true; fragment.appendChild(rootElement); } From a86853f53f10d543bd1b8d808706cdb4df340632 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Sat, 20 Jul 2024 11:30:54 +0100 Subject: [PATCH 085/136] Add info to ToolJoinPoint --- .../visualization/public/js/ToolJoinPoint.ts | 13 ++++++++++++- .../src-api/visualization/public/js/ast-import.ts | 4 ++-- .../visualization/public/js/ToolJoinPoint.js | 12 +++++++++--- .../src-lara/visualization/public/js/ast-import.js | 4 ++-- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts index 855787def..03eb62b50 100644 --- a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts +++ b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts @@ -1,11 +1,15 @@ +export type JoinPointInfo = { [attribute: string]: string }; + export default class ToolJoinPoint { #id: string; #type: string; + #info: JoinPointInfo; #children: ToolJoinPoint[]; - constructor(id: string, type: string, children: ToolJoinPoint[]) { + constructor(id: string, type: string, info: JoinPointInfo, children: ToolJoinPoint[]) { this.#id = id; this.#type = type; + this.#info = info; this.#children = children; } @@ -17,6 +21,10 @@ export default class ToolJoinPoint { return this.#type; } + get info(): Object { + return this.#info; + } + get children(): ToolJoinPoint[] { return this.#children } @@ -25,6 +33,7 @@ export default class ToolJoinPoint { return new ToolJoinPoint( json.id, json.type, + json.info, json.children.map((child: any) => ToolJoinPoint.fromJson(child)) ); } @@ -33,6 +42,7 @@ export default class ToolJoinPoint { return { id: this.#id, type: this.#type, + info: this.#info, children: this.#children.map((child) => child.toJson()), }; } @@ -41,6 +51,7 @@ export default class ToolJoinPoint { return new ToolJoinPoint( this.#id, this.#type, + this.#info, this.#children.map((child) => child.clone()) ); } diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index f1fd86215..fbf5cee84 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -2,7 +2,7 @@ import { countChar, createIcon, } from './utils.js'; import { addEventListenersToAstNodes } from './visualization.js'; import JoinPoint from './ToolJoinPoint.js'; -const createAstNodeDropdownButton = (nodeId: string): HTMLButtonElement => { +const createAstNodeDropdownButton = (): HTMLButtonElement => { const dropdownButton = document.createElement('button'); const chevronIcon = createIcon("keyboard_arrow_down"); @@ -51,7 +51,7 @@ const createAstNodeDropdown = (nodeId: string): HTMLDivElement => { const convertAstNodeToHtml = (root: JoinPoint): DocumentFragment => { const fragment = new DocumentFragment(); - const dropdownButton = createAstNodeDropdownButton(root.id); + const dropdownButton = createAstNodeDropdownButton(); const rootElement = createAstNodeElement(root.id, root.type, dropdownButton); if (root.children.length > 0) { diff --git a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js index 2bbcd1123..bea947486 100644 --- a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js +++ b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js @@ -1,10 +1,12 @@ export default class ToolJoinPoint { #id; #type; + #info; #children; - constructor(id, type, children) { + constructor(id, type, info, children) { this.#id = id; this.#type = type; + this.#info = info; this.#children = children; } get id() { @@ -13,21 +15,25 @@ export default class ToolJoinPoint { get type() { return this.#type; } + get info() { + return this.#info; + } get children() { return this.#children; } static fromJson(json) { - return new ToolJoinPoint(json.id, json.type, json.children.map((child) => ToolJoinPoint.fromJson(child))); + return new ToolJoinPoint(json.id, json.type, json.info, json.children.map((child) => ToolJoinPoint.fromJson(child))); } toJson() { return { id: this.#id, type: this.#type, + info: this.#info, children: this.#children.map((child) => child.toJson()), }; } clone() { - return new ToolJoinPoint(this.#id, this.#type, this.#children.map((child) => child.clone())); + return new ToolJoinPoint(this.#id, this.#type, this.#info, this.#children.map((child) => child.clone())); } } ; diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index e65dc7791..109ea6fce 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -1,6 +1,6 @@ import { countChar, createIcon, } from './utils.js'; import { addEventListenersToAstNodes } from './visualization.js'; -const createAstNodeDropdownButton = (nodeId) => { +const createAstNodeDropdownButton = () => { const dropdownButton = document.createElement('button'); const chevronIcon = createIcon("keyboard_arrow_down"); dropdownButton.appendChild(chevronIcon); @@ -36,7 +36,7 @@ const createAstNodeDropdown = (nodeId) => { }; const convertAstNodeToHtml = (root) => { const fragment = new DocumentFragment(); - const dropdownButton = createAstNodeDropdownButton(root.id); + const dropdownButton = createAstNodeDropdownButton(); const rootElement = createAstNodeElement(root.id, root.type, dropdownButton); if (root.children.length > 0) { const rootDropdown = createAstNodeDropdown(root.id); From cae11918dcc720d4692c48175deb89e9a5fde828 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Sat, 20 Jul 2024 13:47:39 +0100 Subject: [PATCH 086/136] Create node info container --- Lara-JS/src-api/visualization/public/css/styles.css | 10 ++++++++++ Lara-JS/src-api/visualization/public/index.html | 1 + 2 files changed, 11 insertions(+) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index e2bc3657c..582f6e70d 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -1,6 +1,7 @@ body { height: 100vh; margin: 0; + position: relative; font-family: 'Roboto', sans-serif; color: var(--text-color); @@ -164,3 +165,12 @@ main { #code-container .lines { color: var(--line-num-color); } + + +#node-info-container { + position: absolute; + right: 3em; + bottom: 3em; + + display: none; +} diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 68dd2c3b8..4bd5bf71a 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -24,6 +24,7 @@

LARA Visualization Tool

+
\ No newline at end of file From 6fd78d6f35934945fe835375bbdac930e13383e3 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Sat, 20 Jul 2024 17:49:07 +0100 Subject: [PATCH 087/136] Refactor node info container style and behavior --- .../visualization/public/css/styles.css | 25 ++++++++- .../visualization/public/js/ToolJoinPoint.ts | 2 +- .../visualization/public/js/ast-import.ts | 2 +- .../visualization/public/js/visualization.ts | 51 +++++++++++++++---- .../visualization/public/js/ast-import.js | 2 +- .../visualization/public/js/visualization.js | 40 +++++++++++---- 6 files changed, 97 insertions(+), 25 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 582f6e70d..35f1db833 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -135,7 +135,9 @@ main { background-color: var(--bg-color); border: none; border-radius: 50%; +} +.ast-node button:disabled { cursor: default; } @@ -164,13 +166,32 @@ main { #code-container .lines { color: var(--line-num-color); + + white-space: pre; } #node-info-container { + width: fit-content; + height: fit-content; + padding-inline: 0.5em; + background-color: var(--bg-color); + position: absolute; - right: 3em; - bottom: 3em; + right: 2.5em; + bottom: 2.5em; display: none; } + +#node-info-container p { + margin: 0; +} + +#node-info-container p > span:first-of-type { + font-weight: bold; +} + +#node-info-container p > span:first-of-type::after { + content: ': '; +} diff --git a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts index 03eb62b50..9e9dc6638 100644 --- a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts +++ b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts @@ -21,7 +21,7 @@ export default class ToolJoinPoint { return this.#type; } - get info(): Object { + get info(): JoinPointInfo { return this.#info; } diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index fbf5cee84..40de37889 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -80,7 +80,7 @@ const importCode = (code: string, codeContainer: HTMLElement): void => { const numLines = countChar(code, '\n') + 1; const codeLines = codeContainer.querySelector('.lines')!; - codeLines.innerText = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); + codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); } const importAst = (astRoot: JoinPoint, astContainer: HTMLElement): void => { diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index 760e92275..3379144cd 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -1,4 +1,4 @@ -import JoinPoint from "./ToolJoinPoint.js"; +import JoinPoint, { JoinPointInfo } from "./ToolJoinPoint.js"; const getNodeElement = (nodeId: string): HTMLElement | null => { return document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); @@ -42,19 +42,43 @@ const unhighlightNode = (nodeId: string): void => { } }; +const showNodeInfo = (nodeInfo: JoinPointInfo): void => { + const nodeInfoContainer = document.querySelector('#node-info-container')!; + nodeInfoContainer.style.display = 'block'; + nodeInfoContainer.innerHTML = '' + + for (const [name, value] of Object.entries(nodeInfo)) { + const attributeName = document.createElement('span'); + attributeName.textContent = name; + + const attributeValue = document.createElement('span'); + attributeValue.textContent = value; + + const line = document.createElement('p'); + line.append(attributeName, attributeValue); + nodeInfoContainer.appendChild(line); + } +} + +const hideNodeInfo = (): void => { + const nodeInfoContainer = document.querySelector('#node-info-container')!; + nodeInfoContainer.style.display = 'none'; + nodeInfoContainer.innerHTML = ''; +} + const addHighlighingEvents = (() => { let selectedNodeId: string | null = null; - return (nodeId: string): void => { - const nodeRelatedElements = getNodeRelatedElements(nodeId); + return (node: JoinPoint): void => { + const nodeRelatedElements = getNodeRelatedElements(node.id); for (const nodeRelatedElement of nodeRelatedElements) { nodeRelatedElement.addEventListener('mouseover', event => { - highlightNode(nodeId, false); + highlightNode(node.id, false); if (selectedNodeId !== null) highlightNode(selectedNodeId, true); event.stopPropagation(); }); nodeRelatedElement.addEventListener('mouseout', event => { - unhighlightNode(nodeId); + unhighlightNode(node.id); if (selectedNodeId !== null) highlightNode(selectedNodeId, true); event.stopPropagation(); @@ -66,27 +90,32 @@ const addHighlighingEvents = (() => { if (selectedNodeId !== null) { unhighlightNode(selectedNodeId); - if (selectedNodeId === nodeId) { + if (selectedNodeId === node.id) { selectedNodeId = null; + hideNodeInfo(); return; } } - selectedNodeId = nodeId; - highlightNode(nodeId, true); + selectedNodeId = node.id; + highlightNode(node.id, true); - const nodeElement = getNodeElement(nodeId)!; + const nodeElement = getNodeElement(node.id)!; const firstNodeCodeBlock = document.querySelector('.node-code[data-node-id]')!; for (const element of [nodeElement, firstNodeCodeBlock]) { element.scrollIntoView({ behavior: 'smooth', block: 'center' }); } + + showNodeInfo(node.info); }); + + // For keyboard accessibility nodeRelatedElement.addEventListener('keydown', event => { if (event.key === 'Enter') { nodeRelatedElement.click(); } event.stopPropagation(); - }) + }); } }; })(); @@ -94,7 +123,7 @@ const addHighlighingEvents = (() => { const addEventListenersToAstNodes = (root: JoinPoint): void => { const nodeId = root.id; - addHighlighingEvents(nodeId); + addHighlighingEvents(root); root.children.forEach(child => addEventListenersToAstNodes(child)); }; diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index 109ea6fce..78c77095e 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -58,7 +58,7 @@ const importCode = (code, codeContainer) => { codeContainer.querySelector('code').innerHTML = code; const numLines = countChar(code, '\n') + 1; const codeLines = codeContainer.querySelector('.lines'); - codeLines.innerText = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); + codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); }; const importAst = (astRoot, astContainer) => { const astFragment = convertAstNodeToHtml(astRoot); diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index 41d12c1eb..067c00ded 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -30,19 +30,38 @@ const unhighlightNode = (nodeId) => { parentNode = parentNode.parentElement?.previousSibling; } }; +const showNodeInfo = (nodeInfo) => { + const nodeInfoContainer = document.querySelector('#node-info-container'); + nodeInfoContainer.style.display = 'block'; + nodeInfoContainer.innerHTML = ''; + for (const [name, value] of Object.entries(nodeInfo)) { + const attributeName = document.createElement('span'); + attributeName.textContent = name; + const attributeValue = document.createElement('span'); + attributeValue.textContent = value; + const line = document.createElement('p'); + line.append(attributeName, attributeValue); + nodeInfoContainer.appendChild(line); + } +}; +const hideNodeInfo = () => { + const nodeInfoContainer = document.querySelector('#node-info-container'); + nodeInfoContainer.style.display = 'none'; + nodeInfoContainer.innerHTML = ''; +}; const addHighlighingEvents = (() => { let selectedNodeId = null; - return (nodeId) => { - const nodeRelatedElements = getNodeRelatedElements(nodeId); + return (node) => { + const nodeRelatedElements = getNodeRelatedElements(node.id); for (const nodeRelatedElement of nodeRelatedElements) { nodeRelatedElement.addEventListener('mouseover', event => { - highlightNode(nodeId, false); + highlightNode(node.id, false); if (selectedNodeId !== null) highlightNode(selectedNodeId, true); event.stopPropagation(); }); nodeRelatedElement.addEventListener('mouseout', event => { - unhighlightNode(nodeId); + unhighlightNode(node.id); if (selectedNodeId !== null) highlightNode(selectedNodeId, true); event.stopPropagation(); @@ -52,19 +71,22 @@ const addHighlighingEvents = (() => { event.stopPropagation(); if (selectedNodeId !== null) { unhighlightNode(selectedNodeId); - if (selectedNodeId === nodeId) { + if (selectedNodeId === node.id) { selectedNodeId = null; + hideNodeInfo(); return; } } - selectedNodeId = nodeId; - highlightNode(nodeId, true); - const nodeElement = getNodeElement(nodeId); + selectedNodeId = node.id; + highlightNode(node.id, true); + const nodeElement = getNodeElement(node.id); const firstNodeCodeBlock = document.querySelector('.node-code[data-node-id]'); for (const element of [nodeElement, firstNodeCodeBlock]) { element.scrollIntoView({ behavior: 'smooth', block: 'center' }); } + showNodeInfo(node.info); }); + // For keyboard accessibility nodeRelatedElement.addEventListener('keydown', event => { if (event.key === 'Enter') { nodeRelatedElement.click(); @@ -76,7 +98,7 @@ const addHighlighingEvents = (() => { })(); const addEventListenersToAstNodes = (root) => { const nodeId = root.id; - addHighlighingEvents(nodeId); + addHighlighingEvents(root); root.children.forEach(child => addEventListenersToAstNodes(child)); }; export { addEventListenersToAstNodes }; From 813c2bdfb5d3fd34b656bf453a0af01d745e2263 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Sat, 20 Jul 2024 21:50:53 +0100 Subject: [PATCH 088/136] Fix scroll to code on click --- Lara-JS/src-api/visualization/GenericVisualizationTool.ts | 2 +- Lara-JS/src-api/visualization/public/js/visualization.ts | 4 ++-- LaraApi/src-lara/visualization/GenericVisualizationTool.js | 2 +- LaraApi/src-lara/visualization/public/js/visualization.js | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Lara-JS/src-api/visualization/GenericVisualizationTool.ts b/Lara-JS/src-api/visualization/GenericVisualizationTool.ts index 2c334fe42..8cbc00e5c 100644 --- a/Lara-JS/src-api/visualization/GenericVisualizationTool.ts +++ b/Lara-JS/src-api/visualization/GenericVisualizationTool.ts @@ -140,7 +140,7 @@ export default abstract class GenericVisualizationTool { } private updateClient(ws: WebSocket): void { - wrapJoinPoint(JoinPoints.root()._javaObject.rebuild()); + wrapJoinPoint(JoinPoints.root()._javaObject.rebuild()); // TODO: Perform rebuild on AstConverter this.sendToClient(ws, { message: 'update', ast: this.getAstConverter() diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index 3379144cd..6da2d77e6 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -101,9 +101,9 @@ const addHighlighingEvents = (() => { highlightNode(node.id, true); const nodeElement = getNodeElement(node.id)!; - const firstNodeCodeBlock = document.querySelector('.node-code[data-node-id]')!; + const firstNodeCodeBlock = document.querySelector(`.node-code[data-node-id="${node.id}"]`); for (const element of [nodeElement, firstNodeCodeBlock]) { - element.scrollIntoView({ behavior: 'smooth', block: 'center' }); + element?.scrollIntoView({ behavior: 'smooth', block: 'center' }); } showNodeInfo(node.info); diff --git a/LaraApi/src-lara/visualization/GenericVisualizationTool.js b/LaraApi/src-lara/visualization/GenericVisualizationTool.js index 6f652b93d..ea72f928e 100644 --- a/LaraApi/src-lara/visualization/GenericVisualizationTool.js +++ b/LaraApi/src-lara/visualization/GenericVisualizationTool.js @@ -107,7 +107,7 @@ export default class GenericVisualizationTool { }); } updateClient(ws) { - wrapJoinPoint(JoinPoints.root()._javaObject.rebuild()); + wrapJoinPoint(JoinPoints.root()._javaObject.rebuild()); // TODO: Perform rebuild on AstConverter this.sendToClient(ws, { message: 'update', ast: this.getAstConverter() diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index 067c00ded..b2d4c3d97 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -80,9 +80,9 @@ const addHighlighingEvents = (() => { selectedNodeId = node.id; highlightNode(node.id, true); const nodeElement = getNodeElement(node.id); - const firstNodeCodeBlock = document.querySelector('.node-code[data-node-id]'); + const firstNodeCodeBlock = document.querySelector(`.node-code[data-node-id="${node.id}"]`); for (const element of [nodeElement, firstNodeCodeBlock]) { - element.scrollIntoView({ behavior: 'smooth', block: 'center' }); + element?.scrollIntoView({ behavior: 'smooth', block: 'center' }); } showNodeInfo(node.info); }); From 0acb3b835700e2789173482ea559e2eabf27e282 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 22 Jul 2024 10:40:30 +0100 Subject: [PATCH 089/136] Allow updating with random root --- .../visualization/GenericVisualizationTool.ts | 16 +++++++++++----- .../visualization/GenericVisualizationTool.js | 11 +++++++---- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Lara-JS/src-api/visualization/GenericVisualizationTool.ts b/Lara-JS/src-api/visualization/GenericVisualizationTool.ts index 8cbc00e5c..a99b3f063 100644 --- a/Lara-JS/src-api/visualization/GenericVisualizationTool.ts +++ b/Lara-JS/src-api/visualization/GenericVisualizationTool.ts @@ -5,7 +5,7 @@ import { fileURLToPath } from 'url'; import WebSocket, { WebSocketServer } from 'ws'; import { AddressInfo } from 'net'; -import { wrapJoinPoint } from '../LaraJoinPoint.js'; +import { LaraJoinPoint, wrapJoinPoint } from '../LaraJoinPoint.js'; import JoinPoints from '../weaver/JoinPoints.js'; import GenericAstConverter from './GenericAstConverter.js'; @@ -15,6 +15,7 @@ export default abstract class GenericVisualizationTool { private port: number | undefined; private wss: WebSocketServer | undefined; private serverClosed: boolean = false; + private astRoot: LaraJoinPoint | undefined; public isLaunched(): boolean { return this.wss !== undefined && this.serverClosed === false; @@ -46,12 +47,14 @@ export default abstract class GenericVisualizationTool { this.wss!.close(); } - public async launch(hostname: string = '127.0.0.1', port?: number): Promise { + public async launch(hostname: string = '127.0.0.1', port?: number, astRoot: LaraJoinPoint = JoinPoints.root()): Promise { if (this.isLaunched()) { console.warn(`Visualization tool is already running at http://${this.hostname}:${this.port}`); return; } + this.astRoot = astRoot; + const app = express(); const server = http.createServer(app); this.wss = new WebSocketServer({ server: server }); @@ -141,18 +144,21 @@ export default abstract class GenericVisualizationTool { private updateClient(ws: WebSocket): void { wrapJoinPoint(JoinPoints.root()._javaObject.rebuild()); // TODO: Perform rebuild on AstConverter + this.sendToClient(ws, { message: 'update', ast: this.getAstConverter() - .getToolAst(JoinPoints.root()) + .getToolAst(this.astRoot!) .toJson(), code: this.getAstConverter() - .getPrettyHtmlCode(JoinPoints.root()) + .getPrettyHtmlCode(this.astRoot!), }); } - public update(): void { + public update(astRoot: LaraJoinPoint = JoinPoints.root()): void { this.verifyToolIsRunning(); + + this.astRoot = astRoot; this.wss!.clients.forEach(ws => this.updateClient(ws)); } diff --git a/LaraApi/src-lara/visualization/GenericVisualizationTool.js b/LaraApi/src-lara/visualization/GenericVisualizationTool.js index ea72f928e..862fc0039 100644 --- a/LaraApi/src-lara/visualization/GenericVisualizationTool.js +++ b/LaraApi/src-lara/visualization/GenericVisualizationTool.js @@ -10,6 +10,7 @@ export default class GenericVisualizationTool { port; wss; serverClosed = false; + astRoot; isLaunched() { return this.wss !== undefined && this.serverClosed === false; } @@ -34,11 +35,12 @@ export default class GenericVisualizationTool { ; this.wss.close(); } - async launch(hostname = '127.0.0.1', port) { + async launch(hostname = '127.0.0.1', port, astRoot = JoinPoints.root()) { if (this.isLaunched()) { console.warn(`Visualization tool is already running at http://${this.hostname}:${this.port}`); return; } + this.astRoot = astRoot; const app = express(); const server = http.createServer(app); this.wss = new WebSocketServer({ server: server }); @@ -111,14 +113,15 @@ export default class GenericVisualizationTool { this.sendToClient(ws, { message: 'update', ast: this.getAstConverter() - .getToolAst(JoinPoints.root()) + .getToolAst(this.astRoot) .toJson(), code: this.getAstConverter() - .getPrettyHtmlCode(JoinPoints.root()) + .getPrettyHtmlCode(this.astRoot), }); } - update() { + update(astRoot = JoinPoints.root()) { this.verifyToolIsRunning(); + this.astRoot = astRoot; this.wss.clients.forEach(ws => this.updateClient(ws)); } } From f81f57c981456afa1c57177a504aa90edd242b13 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 22 Jul 2024 11:40:46 +0100 Subject: [PATCH 090/136] Fix highlighting after update --- .../visualization/public/js/visualization.ts | 105 +++++++++--------- .../visualization/public/js/visualization.js | 92 ++++++++------- 2 files changed, 97 insertions(+), 100 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index 6da2d77e6..671b4d023 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -66,62 +66,61 @@ const hideNodeInfo = (): void => { nodeInfoContainer.innerHTML = ''; } -const addHighlighingEvents = (() => { - let selectedNodeId: string | null = null; - return (node: JoinPoint): void => { - const nodeRelatedElements = getNodeRelatedElements(node.id); - for (const nodeRelatedElement of nodeRelatedElements) { - nodeRelatedElement.addEventListener('mouseover', event => { - highlightNode(node.id, false); - if (selectedNodeId !== null) - highlightNode(selectedNodeId, true); - event.stopPropagation(); - }); - nodeRelatedElement.addEventListener('mouseout', event => { - unhighlightNode(node.id); - if (selectedNodeId !== null) - highlightNode(selectedNodeId, true); - event.stopPropagation(); - }); - - nodeRelatedElement.tabIndex = 0; - nodeRelatedElement.addEventListener('click', event => { - event.stopPropagation(); - - if (selectedNodeId !== null) { - unhighlightNode(selectedNodeId); - if (selectedNodeId === node.id) { - selectedNodeId = null; - hideNodeInfo(); - return; - } +let selectedNodeId: string | null = null; + +const addHighlighingEvents = (node: JoinPoint): void => { + const nodeRelatedElements = getNodeRelatedElements(node.id); + for (const nodeRelatedElement of nodeRelatedElements) { + nodeRelatedElement.addEventListener('mouseover', event => { + highlightNode(node.id, false); + if (selectedNodeId !== null) + highlightNode(selectedNodeId, true); + event.stopPropagation(); + }); + nodeRelatedElement.addEventListener('mouseout', event => { + unhighlightNode(node.id); + if (selectedNodeId !== null) + highlightNode(selectedNodeId, true); + event.stopPropagation(); + }); + + nodeRelatedElement.tabIndex = 0; + nodeRelatedElement.addEventListener('click', event => { + event.stopPropagation(); + + if (selectedNodeId !== null) { + unhighlightNode(selectedNodeId); + if (selectedNodeId === node.id) { + selectedNodeId = null; + hideNodeInfo(); + return; } - - selectedNodeId = node.id; - highlightNode(node.id, true); - - const nodeElement = getNodeElement(node.id)!; - const firstNodeCodeBlock = document.querySelector(`.node-code[data-node-id="${node.id}"]`); - for (const element of [nodeElement, firstNodeCodeBlock]) { - element?.scrollIntoView({ behavior: 'smooth', block: 'center' }); - } - - showNodeInfo(node.info); - }); - - // For keyboard accessibility - nodeRelatedElement.addEventListener('keydown', event => { - if (event.key === 'Enter') { - nodeRelatedElement.click(); - } - event.stopPropagation(); - }); - } - }; -})(); + } + + selectedNodeId = node.id; + highlightNode(node.id, true); + + const nodeElement = getNodeElement(node.id)!; + const firstNodeCodeBlock = document.querySelector(`.node-code[data-node-id="${node.id}"]`); + for (const element of [nodeElement, firstNodeCodeBlock]) { + element?.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + } + + showNodeInfo(node.info); + }); + + // For keyboard accessibility + nodeRelatedElement.addEventListener('keydown', event => { + if (event.key === 'Enter') { + nodeRelatedElement.click(); + } + event.stopPropagation(); + }); + } +}; const addEventListenersToAstNodes = (root: JoinPoint): void => { - const nodeId = root.id; + selectedNodeId = null; // To prevent invalid references addHighlighingEvents(root); root.children.forEach(child => addEventListenersToAstNodes(child)); diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index b2d4c3d97..c5528e999 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -49,55 +49,53 @@ const hideNodeInfo = () => { nodeInfoContainer.style.display = 'none'; nodeInfoContainer.innerHTML = ''; }; -const addHighlighingEvents = (() => { - let selectedNodeId = null; - return (node) => { - const nodeRelatedElements = getNodeRelatedElements(node.id); - for (const nodeRelatedElement of nodeRelatedElements) { - nodeRelatedElement.addEventListener('mouseover', event => { - highlightNode(node.id, false); - if (selectedNodeId !== null) - highlightNode(selectedNodeId, true); - event.stopPropagation(); - }); - nodeRelatedElement.addEventListener('mouseout', event => { - unhighlightNode(node.id); - if (selectedNodeId !== null) - highlightNode(selectedNodeId, true); - event.stopPropagation(); - }); - nodeRelatedElement.tabIndex = 0; - nodeRelatedElement.addEventListener('click', event => { - event.stopPropagation(); - if (selectedNodeId !== null) { - unhighlightNode(selectedNodeId); - if (selectedNodeId === node.id) { - selectedNodeId = null; - hideNodeInfo(); - return; - } +let selectedNodeId = null; +const addHighlighingEvents = (node) => { + const nodeRelatedElements = getNodeRelatedElements(node.id); + for (const nodeRelatedElement of nodeRelatedElements) { + nodeRelatedElement.addEventListener('mouseover', event => { + highlightNode(node.id, false); + if (selectedNodeId !== null) + highlightNode(selectedNodeId, true); + event.stopPropagation(); + }); + nodeRelatedElement.addEventListener('mouseout', event => { + unhighlightNode(node.id); + if (selectedNodeId !== null) + highlightNode(selectedNodeId, true); + event.stopPropagation(); + }); + nodeRelatedElement.tabIndex = 0; + nodeRelatedElement.addEventListener('click', event => { + event.stopPropagation(); + if (selectedNodeId !== null) { + unhighlightNode(selectedNodeId); + if (selectedNodeId === node.id) { + selectedNodeId = null; + hideNodeInfo(); + return; } - selectedNodeId = node.id; - highlightNode(node.id, true); - const nodeElement = getNodeElement(node.id); - const firstNodeCodeBlock = document.querySelector(`.node-code[data-node-id="${node.id}"]`); - for (const element of [nodeElement, firstNodeCodeBlock]) { - element?.scrollIntoView({ behavior: 'smooth', block: 'center' }); - } - showNodeInfo(node.info); - }); - // For keyboard accessibility - nodeRelatedElement.addEventListener('keydown', event => { - if (event.key === 'Enter') { - nodeRelatedElement.click(); - } - event.stopPropagation(); - }); - } - }; -})(); + } + selectedNodeId = node.id; + highlightNode(node.id, true); + const nodeElement = getNodeElement(node.id); + const firstNodeCodeBlock = document.querySelector(`.node-code[data-node-id="${node.id}"]`); + for (const element of [nodeElement, firstNodeCodeBlock]) { + element?.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + } + showNodeInfo(node.info); + }); + // For keyboard accessibility + nodeRelatedElement.addEventListener('keydown', event => { + if (event.key === 'Enter') { + nodeRelatedElement.click(); + } + event.stopPropagation(); + }); + } +}; const addEventListenersToAstNodes = (root) => { - const nodeId = root.id; + selectedNodeId = null; // To prevent invalid references addHighlighingEvents(root); root.children.forEach(child => addEventListenersToAstNodes(child)); }; From ed79f1705f5ec4c7bc6d69d0a87995558632c3db Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 22 Jul 2024 14:44:52 +0100 Subject: [PATCH 091/136] Refactor scroll --- .../visualization/public/css/styles.css | 12 ++++++--- .../visualization/public/js/ast-import.ts | 4 +-- .../visualization/public/js/communication.ts | 2 +- .../visualization/public/js/visualization.ts | 26 ++++++++++++++----- .../visualization/public/js/ast-import.js | 4 +-- .../visualization/public/js/communication.js | 2 +- .../visualization/public/js/visualization.js | 25 +++++++++++++----- 7 files changed, 51 insertions(+), 24 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 35f1db833..09c8b4716 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -19,7 +19,7 @@ button { padding: 0 1.25em; background-color: var(--button-bg-color); border: 1px solid var(--border-color); - border-radius: 2px; + border-radius: 4px; font-size: 1em; font-weight: 500; @@ -102,20 +102,21 @@ main { padding: 0.25em 0.5em; margin: 0; border: 1px solid var(--border-color); - border-radius: 2px; + border-radius: 4px; display: flex; flex-direction: column; overflow: auto; - user-select: none; - -webkit-user-select: none; } #ast-container { font-weight: 500; line-height: 1.25em; + + user-select: none; + -webkit-user-select: none; } .ast-node { @@ -154,6 +155,9 @@ main { display: grid; grid-template-columns: auto 1fr; gap: 1em; + + user-select: none; + -webkit-user-select: none; } #code-container pre { diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index 40de37889..2f057485f 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -83,12 +83,12 @@ const importCode = (code: string, codeContainer: HTMLElement): void => { codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); } -const importAst = (astRoot: JoinPoint, astContainer: HTMLElement): void => { +const importAst = (astRoot: JoinPoint, astContainer: HTMLElement, codeContainer: HTMLElement): void => { const astFragment = convertAstNodeToHtml(astRoot); astContainer.innerHTML = ''; astContainer.appendChild(astFragment); - addEventListenersToAstNodes(astRoot); + addEventListenersToAstNodes(astRoot, astContainer, codeContainer); } export { importCode, importAst }; diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts index 6a1cc9233..a0ec86c2c 100644 --- a/Lara-JS/src-api/visualization/public/js/communication.ts +++ b/Lara-JS/src-api/visualization/public/js/communication.ts @@ -23,7 +23,7 @@ const webSocketOnMessage = (message: MessageEvent, continueButton: HTMLButtonEle continueButton.disabled = true; importCode(code, codeContainer); - importAst(ast, astContainer); + importAst(ast, astContainer, codeContainer); continueButton.disabled = buttonDisabled; break; diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index 671b4d023..c4859f51d 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -66,9 +66,21 @@ const hideNodeInfo = (): void => { nodeInfoContainer.innerHTML = ''; } +const scrollIntoViewIfNeeded = (element: HTMLElement, parent: HTMLElement): void => { + const rect = element.getBoundingClientRect(); + const parentRect = parent.getBoundingClientRect(); + console.log(rect, parentRect); + if (rect.bottom < parentRect.top || rect.top > parentRect.bottom) { + const scrollPos = rect.height <= parentRect.height + ? (rect.top + rect.bottom - parentRect.top - parentRect.bottom) / 2 + : rect.top - parentRect.top; + parent.scrollBy({ top: scrollPos, left: rect.left, behavior: 'smooth' }); + } +}; + let selectedNodeId: string | null = null; -const addHighlighingEvents = (node: JoinPoint): void => { +const addHighlighingEvents = (node: JoinPoint, astContainer: HTMLElement, codeContainer: HTMLElement): void => { const nodeRelatedElements = getNodeRelatedElements(node.id); for (const nodeRelatedElement of nodeRelatedElements) { nodeRelatedElement.addEventListener('mouseover', event => { @@ -101,10 +113,10 @@ const addHighlighingEvents = (node: JoinPoint): void => { highlightNode(node.id, true); const nodeElement = getNodeElement(node.id)!; + scrollIntoViewIfNeeded(nodeElement, astContainer); const firstNodeCodeBlock = document.querySelector(`.node-code[data-node-id="${node.id}"]`); - for (const element of [nodeElement, firstNodeCodeBlock]) { - element?.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); - } + if (firstNodeCodeBlock) + scrollIntoViewIfNeeded(firstNodeCodeBlock!, codeContainer); showNodeInfo(node.info); }); @@ -119,11 +131,11 @@ const addHighlighingEvents = (node: JoinPoint): void => { } }; -const addEventListenersToAstNodes = (root: JoinPoint): void => { +const addEventListenersToAstNodes = (root: JoinPoint, astContainer: HTMLElement, codeContainer: HTMLElement): void => { selectedNodeId = null; // To prevent invalid references - addHighlighingEvents(root); - root.children.forEach(child => addEventListenersToAstNodes(child)); + addHighlighingEvents(root, astContainer, codeContainer); + root.children.forEach(child => addEventListenersToAstNodes(child, astContainer, codeContainer)); }; export { addEventListenersToAstNodes }; diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index 78c77095e..c5618e9d3 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -60,11 +60,11 @@ const importCode = (code, codeContainer) => { const codeLines = codeContainer.querySelector('.lines'); codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); }; -const importAst = (astRoot, astContainer) => { +const importAst = (astRoot, astContainer, codeContainer) => { const astFragment = convertAstNodeToHtml(astRoot); astContainer.innerHTML = ''; astContainer.appendChild(astFragment); - addEventListenersToAstNodes(astRoot); + addEventListenersToAstNodes(astRoot, astContainer, codeContainer); }; export { importCode, importAst }; //# sourceMappingURL=ast-import.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js index 4a189b8da..2a09bd4a6 100644 --- a/LaraApi/src-lara/visualization/public/js/communication.js +++ b/LaraApi/src-lara/visualization/public/js/communication.js @@ -17,7 +17,7 @@ const webSocketOnMessage = (message, continueButton, astContainer, codeContainer const buttonDisabled = continueButton.disabled; continueButton.disabled = true; importCode(code, codeContainer); - importAst(ast, astContainer); + importAst(ast, astContainer, codeContainer); continueButton.disabled = buttonDisabled; break; case 'wait': diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index c5528e999..dff730b29 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -49,8 +49,19 @@ const hideNodeInfo = () => { nodeInfoContainer.style.display = 'none'; nodeInfoContainer.innerHTML = ''; }; +const scrollIntoViewIfNeeded = (element, parent) => { + const rect = element.getBoundingClientRect(); + const parentRect = parent.getBoundingClientRect(); + console.log(rect, parentRect); + if (rect.bottom < parentRect.top || rect.top > parentRect.bottom) { + const scrollPos = rect.height <= parentRect.height + ? (rect.top + rect.bottom - parentRect.top - parentRect.bottom) / 2 + : rect.top - parentRect.top; + parent.scrollBy({ top: scrollPos, left: rect.left, behavior: 'smooth' }); + } +}; let selectedNodeId = null; -const addHighlighingEvents = (node) => { +const addHighlighingEvents = (node, astContainer, codeContainer) => { const nodeRelatedElements = getNodeRelatedElements(node.id); for (const nodeRelatedElement of nodeRelatedElements) { nodeRelatedElement.addEventListener('mouseover', event => { @@ -79,10 +90,10 @@ const addHighlighingEvents = (node) => { selectedNodeId = node.id; highlightNode(node.id, true); const nodeElement = getNodeElement(node.id); + scrollIntoViewIfNeeded(nodeElement, astContainer); const firstNodeCodeBlock = document.querySelector(`.node-code[data-node-id="${node.id}"]`); - for (const element of [nodeElement, firstNodeCodeBlock]) { - element?.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); - } + if (firstNodeCodeBlock) + scrollIntoViewIfNeeded(firstNodeCodeBlock, codeContainer); showNodeInfo(node.info); }); // For keyboard accessibility @@ -94,10 +105,10 @@ const addHighlighingEvents = (node) => { }); } }; -const addEventListenersToAstNodes = (root) => { +const addEventListenersToAstNodes = (root, astContainer, codeContainer) => { selectedNodeId = null; // To prevent invalid references - addHighlighingEvents(root); - root.children.forEach(child => addEventListenersToAstNodes(child)); + addHighlighingEvents(root, astContainer, codeContainer); + root.children.forEach(child => addEventListenersToAstNodes(child, astContainer, codeContainer)); }; export { addEventListenersToAstNodes }; //# sourceMappingURL=visualization.js.map \ No newline at end of file From d3d6c233c350552f8a4155d039430622a71a0e07 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 22 Jul 2024 15:27:25 +0100 Subject: [PATCH 092/136] Create loading text --- .../visualization/public/css/styles.css | 33 ++++++++++++++++--- .../src-api/visualization/public/index.html | 8 +++-- .../visualization/public/js/ast-import.ts | 13 ++++++-- .../visualization/public/js/ast-import.js | 11 +++++-- 4 files changed, 54 insertions(+), 11 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 09c8b4716..cd40147a1 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -122,8 +122,6 @@ main { .ast-node { width: min-content; - font-weight: 500; - display: flex; justify-content: left; align-items: center; @@ -170,8 +168,6 @@ main { #code-container .lines { color: var(--line-num-color); - - white-space: pre; } @@ -199,3 +195,32 @@ main { #node-info-container p > span:first-of-type::after { content: ': '; } + + +.loading { + color: var(--text-color); + margin: auto; + + font-family: 'Roboto', sans-serif; + font-size: 1.5em; + font-weight: 500; + + display: block; + + animation: loading-animation 1.5s ease-in-out infinite; +} +#code-container > .loading { + grid-column-end: span 2; +} + +@keyframes loading-animation { + 0% { + opacity: 0.6; + } + 50% { + opacity: 1; + } + 100% { + opacity: 0.6; + } +} \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 4bd5bf71a..5e6232b32 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -19,10 +19,12 @@

LARA Visualization Tool

resume Continue -
+
+
Loading...
+
-
-
+
Loading...
+
diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index 2f057485f..29945611a 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -76,10 +76,19 @@ const convertAstNodeToHtml = (root: JoinPoint): DocumentFragment => { }; const importCode = (code: string, codeContainer: HTMLElement): void => { - codeContainer.querySelector('code')!.innerHTML = code; + codeContainer.innerHTML = ''; + + const codeLines = document.createElement('pre'); + codeLines.classList.add('lines'); + codeContainer.appendChild(codeLines); + + const codeWrapper = document.createElement('pre'); + const codeElement = document.createElement('code'); + codeElement.innerHTML = code; + codeWrapper.appendChild(codeElement); + codeContainer.appendChild(codeWrapper); const numLines = countChar(code, '\n') + 1; - const codeLines = codeContainer.querySelector('.lines')!; codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); } diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index c5618e9d3..54870f419 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -55,9 +55,16 @@ const convertAstNodeToHtml = (root) => { return fragment; }; const importCode = (code, codeContainer) => { - codeContainer.querySelector('code').innerHTML = code; + codeContainer.innerHTML = ''; + const codeLines = document.createElement('pre'); + codeLines.classList.add('lines'); + codeContainer.appendChild(codeLines); + const codeWrapper = document.createElement('pre'); + const codeElement = document.createElement('code'); + codeElement.innerHTML = code; + codeWrapper.appendChild(codeElement); + codeContainer.appendChild(codeWrapper); const numLines = countChar(code, '\n') + 1; - const codeLines = codeContainer.querySelector('.lines'); codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); }; const importAst = (astRoot, astContainer, codeContainer) => { From ff94c73d0c2cd950aa3a0422831e0e0df807c806 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 22 Jul 2024 15:42:43 +0100 Subject: [PATCH 093/136] Create divider --- .../visualization/public/css/styles.css | 19 +++++++++++++++---- .../src-api/visualization/public/index.html | 1 + 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index cd40147a1..0f2ba7e68 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -68,11 +68,11 @@ main { background-color: var(--bg-color); display: grid; - grid-template-columns: 2fr 3fr; + grid-template-columns: 2fr 1em 3fr; grid-template-rows: min-content 1fr; - grid-template-areas: "continue-button code" - "ast code"; - gap: 1em; + grid-template-areas: "continue-button . code" + "ast divider code"; + row-gap: 1em; } @@ -197,6 +197,17 @@ main { } +.divider { + width: 0.2em; + height: 1.2em; + border-inline: 2px solid var(--border-color); + + grid-area: divider; + justify-self: center; + align-self: center; +} + + .loading { color: var(--text-color); margin: auto; diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 5e6232b32..e7c14daf9 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -19,6 +19,7 @@

LARA Visualization Tool

resume Continue +
Loading...
From 6e28c453c0a812270cef3451dcaff8b74ecb1c07 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 22 Jul 2024 17:04:32 +0100 Subject: [PATCH 094/136] Implement resizer behavior --- .../visualization/public/css/styles.css | 12 ++++--- .../src-api/visualization/public/index.html | 4 +-- .../src-api/visualization/public/js/main.ts | 6 +++- .../visualization/public/js/visualization.ts | 33 +++++++++++++++++-- .../src-lara/visualization/public/js/main.js | 5 ++- .../visualization/public/js/visualization.js | 26 +++++++++++++-- 6 files changed, 74 insertions(+), 12 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 0f2ba7e68..cfcd92c13 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -1,3 +1,7 @@ +:root { + --ast-container-width: 0.5fr; +} + body { height: 100vh; margin: 0; @@ -68,10 +72,10 @@ main { background-color: var(--bg-color); display: grid; - grid-template-columns: 2fr 1em 3fr; + grid-template-columns: var(--ast-container-width) 1em 1fr; grid-template-rows: min-content 1fr; grid-template-areas: "continue-button . code" - "ast divider code"; + "ast resizer code"; row-gap: 1em; } @@ -197,12 +201,12 @@ main { } -.divider { +#resizer { width: 0.2em; height: 1.2em; border-inline: 2px solid var(--border-color); - grid-area: divider; + grid-area: resizer; justify-self: center; align-self: center; } diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index e7c14daf9..a93b674f7 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -10,6 +10,7 @@ LARA Visualization Tool +

LARA Visualization Tool

@@ -19,13 +20,12 @@

LARA Visualization Tool

resume Continue -
+
Loading...
Loading...
-
diff --git a/Lara-JS/src-api/visualization/public/js/main.ts b/Lara-JS/src-api/visualization/public/js/main.ts index c73b4363a..30e650fe9 100644 --- a/Lara-JS/src-api/visualization/public/js/main.ts +++ b/Lara-JS/src-api/visualization/public/js/main.ts @@ -1,15 +1,19 @@ import { continueButtonOnClick, getWebSocket, webSocketOnMessage } from "./communication.js"; +import { addDividerEventListeners } from "./visualization.js"; (() => { const astContainer = document.querySelector('#ast-container'); const codeContainer = document.querySelector('#code-container'); const continueButton = document.querySelector('#continue-button'); + const resizer = document.querySelector('#resizer'); - if (!astContainer || !codeContainer || !continueButton) { + if (!astContainer || !codeContainer || !continueButton || !resizer) { console.error('Required elements not found'); return; } + addDividerEventListeners(resizer, astContainer, codeContainer, continueButton); + let ws: WebSocket; const setupWebSocket = () => { ws = getWebSocket(); diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index c4859f51d..414e5fa0f 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -69,7 +69,7 @@ const hideNodeInfo = (): void => { const scrollIntoViewIfNeeded = (element: HTMLElement, parent: HTMLElement): void => { const rect = element.getBoundingClientRect(); const parentRect = parent.getBoundingClientRect(); - console.log(rect, parentRect); + if (rect.bottom < parentRect.top || rect.top > parentRect.bottom) { const scrollPos = rect.height <= parentRect.height ? (rect.top + rect.bottom - parentRect.top - parentRect.bottom) / 2 @@ -138,4 +138,33 @@ const addEventListenersToAstNodes = (root: JoinPoint, astContainer: HTMLElement, root.children.forEach(child => addEventListenersToAstNodes(child, astContainer, codeContainer)); }; -export { addEventListenersToAstNodes }; +const addDividerEventListeners = (resizer: HTMLElement, astContainer: HTMLElement, codeContainer: HTMLElement, continueButton: HTMLElement): void => { + let drag = false; + let width = astContainer.offsetWidth; + + const rootStyle = document.documentElement.style; + + const astLeft = astContainer.getBoundingClientRect().left; + const maxWidth = codeContainer.getBoundingClientRect().right - astLeft - 160; + + resizer.addEventListener('mousedown', () => { + drag = true; + }); + + document.addEventListener('mouseup', () => { + drag = false; + }); + + document.addEventListener('mousemove', event => { + if (drag) { + width = event.x - astLeft; + if (width < continueButton.offsetWidth) + width = continueButton.offsetWidth; + else if (width > maxWidth) + width = maxWidth; + rootStyle.setProperty('--ast-container-width', `${width}px`); + } + }); +}; + +export { addEventListenersToAstNodes, addDividerEventListeners }; diff --git a/LaraApi/src-lara/visualization/public/js/main.js b/LaraApi/src-lara/visualization/public/js/main.js index 186e2682d..e4ef618e9 100644 --- a/LaraApi/src-lara/visualization/public/js/main.js +++ b/LaraApi/src-lara/visualization/public/js/main.js @@ -1,12 +1,15 @@ import { continueButtonOnClick, getWebSocket, webSocketOnMessage } from "./communication.js"; +import { addDividerEventListeners } from "./visualization.js"; (() => { const astContainer = document.querySelector('#ast-container'); const codeContainer = document.querySelector('#code-container'); const continueButton = document.querySelector('#continue-button'); - if (!astContainer || !codeContainer || !continueButton) { + const resizer = document.querySelector('#resizer'); + if (!astContainer || !codeContainer || !continueButton || !resizer) { console.error('Required elements not found'); return; } + addDividerEventListeners(resizer, astContainer, codeContainer, continueButton); let ws; const setupWebSocket = () => { ws = getWebSocket(); diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index dff730b29..7a0c22755 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -52,7 +52,6 @@ const hideNodeInfo = () => { const scrollIntoViewIfNeeded = (element, parent) => { const rect = element.getBoundingClientRect(); const parentRect = parent.getBoundingClientRect(); - console.log(rect, parentRect); if (rect.bottom < parentRect.top || rect.top > parentRect.bottom) { const scrollPos = rect.height <= parentRect.height ? (rect.top + rect.bottom - parentRect.top - parentRect.bottom) / 2 @@ -110,5 +109,28 @@ const addEventListenersToAstNodes = (root, astContainer, codeContainer) => { addHighlighingEvents(root, astContainer, codeContainer); root.children.forEach(child => addEventListenersToAstNodes(child, astContainer, codeContainer)); }; -export { addEventListenersToAstNodes }; +const addDividerEventListeners = (resizer, astContainer, codeContainer, continueButton) => { + let drag = false; + let width = astContainer.offsetWidth; + const rootStyle = document.documentElement.style; + const astLeft = astContainer.getBoundingClientRect().left; + const maxWidth = codeContainer.getBoundingClientRect().right - astLeft - 160; + resizer.addEventListener('mousedown', () => { + drag = true; + }); + document.addEventListener('mouseup', () => { + drag = false; + }); + document.addEventListener('mousemove', event => { + if (drag) { + width = event.x - astLeft; + if (width < continueButton.offsetWidth) + width = continueButton.offsetWidth; + else if (width > maxWidth) + width = maxWidth; + rootStyle.setProperty('--ast-container-width', `${width}px`); + } + }); +}; +export { addEventListenersToAstNodes, addDividerEventListeners }; //# sourceMappingURL=visualization.js.map \ No newline at end of file From 5b2d3a896103d2343b8215ef0b1a3262da6ce36d Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 23 Jul 2024 08:04:10 +0100 Subject: [PATCH 095/136] Add file tabs --- .../visualization/public/css/styles.css | 29 +++++++++++++++---- .../src-api/visualization/public/index.html | 2 ++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index cfcd92c13..d16306ac3 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -5,7 +5,8 @@ body { height: 100vh; margin: 0; - position: relative; + + z-index: 0; font-family: 'Roboto', sans-serif; color: var(--text-color); @@ -73,10 +74,9 @@ main { display: grid; grid-template-columns: var(--ast-container-width) 1em 1fr; - grid-template-rows: min-content 1fr; - grid-template-areas: "continue-button . code" + grid-template-rows: 3.5em 1fr; + grid-template-areas: "continue-button . file-tabs" "ast resizer code"; - row-gap: 1em; } @@ -107,6 +107,7 @@ main { margin: 0; border: 1px solid var(--border-color); border-radius: 4px; + background-color: var(--bg-color); display: flex; flex-direction: column; @@ -154,6 +155,8 @@ main { #code-container { + z-index: 1; + display: grid; grid-template-columns: auto 1fr; gap: 1em; @@ -179,11 +182,11 @@ main { width: fit-content; height: fit-content; padding-inline: 0.5em; - background-color: var(--bg-color); position: absolute; right: 2.5em; bottom: 2.5em; + z-index: 2; display: none; } @@ -212,6 +215,22 @@ main { } +#file-tabs { + height: 3em; + width: fit-content; + grid-area: file-tabs; + position: relative; + bottom: -1em; +} + +#file-tabs:has(*) { + border-width: 1px 1px 0; + border-style: solid; + border-color: var(--border-color); + border-radius: 4px 4px 0 0; +} + + .loading { color: var(--text-color); margin: auto; diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index a93b674f7..6a3989d22 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -24,6 +24,8 @@

LARA Visualization Tool

Loading...
+
+
Loading...
From 1cbc5b6d87d4aeefd7a989cf8420c52821c60b88 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 23 Jul 2024 08:15:27 +0100 Subject: [PATCH 096/136] Clean CSS --- Lara-JS/src-api/visualization/public/css/styles.css | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index d16306ac3..046aed8d9 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -1,13 +1,11 @@ :root { - --ast-container-width: 0.5fr; + --ast-container-width: 0.6fr; } body { height: 100vh; margin: 0; - z-index: 0; - font-family: 'Roboto', sans-serif; color: var(--text-color); @@ -102,16 +100,12 @@ main { .container { box-sizing: border-box; - height: 100%; padding: 0.25em 0.5em; margin: 0; border: 1px solid var(--border-color); border-radius: 4px; background-color: var(--bg-color); - display: flex; - flex-direction: column; - overflow: auto; } @@ -120,6 +114,9 @@ main { font-weight: 500; line-height: 1.25em; + display: flex; + flex-direction: column; + user-select: none; -webkit-user-select: none; } From 080cb012204f9ead38630e44403e0dd9b70c0912 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 23 Jul 2024 10:21:25 +0100 Subject: [PATCH 097/136] Style file tabs and refactor some CSS --- .../visualization/public/css/styles.css | 87 +++++++++++++++---- .../src-api/visualization/public/index.html | 19 +++- .../visualization/public/js/visualization.ts | 6 +- .../visualization/public/js/visualization.js | 4 +- 4 files changed, 93 insertions(+), 23 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 046aed8d9..f3a1e05c4 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -1,5 +1,5 @@ :root { - --ast-container-width: 0.6fr; + --ast-container-width: 35vw; } body { @@ -7,6 +7,7 @@ body { margin: 0; font-family: 'Roboto', sans-serif; + font-weight: 500; color: var(--text-color); display: grid; @@ -67,11 +68,15 @@ header h1 { main { + --code-container-width: calc(100% - var(--ast-container-width) - 1em); + + width: 100vw; + box-sizing: border-box; padding: 1em 2em 2em; background-color: var(--bg-color); display: grid; - grid-template-columns: var(--ast-container-width) 1em 1fr; + grid-template-columns: var(--ast-container-width) 1em var(--code-container-width); grid-template-rows: 3.5em 1fr; grid-template-areas: "continue-button . file-tabs" "ast resizer code"; @@ -111,7 +116,6 @@ main { #ast-container { - font-weight: 500; line-height: 1.25em; display: flex; @@ -162,12 +166,13 @@ main { -webkit-user-select: none; } -#code-container pre { - margin: 0; +#code-container .lines, #code-container pre code { + font-family: "JetBrains Mono", monospace; + font-weight: 400; } -#code-container .lines, #code-container code { - font-family: "JetBrains Mono", monospace; +#code-container pre { + margin: 0; } #code-container .lines { @@ -180,6 +185,8 @@ main { height: fit-content; padding-inline: 0.5em; + font-weight: 400; + position: absolute; right: 2.5em; bottom: 2.5em; @@ -200,31 +207,77 @@ main { content: ': '; } - #resizer { - width: 0.2em; + width: 1em; height: 1.2em; - border-inline: 2px solid var(--border-color); grid-area: resizer; justify-self: center; align-self: center; + + display: flex; + justify-content: center; +} + +#resizer > div { + width: 0.2em; + border-inline: 2px solid var(--border-color); } #file-tabs { - height: 3em; + box-sizing: border-box; + height: 3.5em; width: fit-content; - grid-area: file-tabs; - position: relative; - bottom: -1em; -} + max-width: 100%; + padding: 0.125em; -#file-tabs:has(*) { border-width: 1px 1px 0; border-style: solid; border-color: var(--border-color); border-radius: 4px 4px 0 0; + + grid-area: file-tabs; + position: relative; + bottom: calc(-1em + 1px); + + display: flex; + align-items: start; + justify-content: left; + gap: 0.125em; + + overflow-x: auto; + overflow-y: hidden; +} + +#file-tabs:not(:has(*)) { + display: none; +} + +.file-tab { + height: 2.25em; + padding-inline: 0.75em; + border-radius: 2px; + background-color: var(--tab-bg-color); + + font-weight: 500; + + display: flex; + align-items: center; + justify-content: center; + + user-select: none; + -webkit-user-select: none; + + transition: background-color 0.1s linear; +} + +.file-tab:hover { + background-color: var(--tab-hover-bg-color); +} + +.file-tab.active { + background-color: var(--tab-active-bg-color); } @@ -234,12 +287,12 @@ main { font-family: 'Roboto', sans-serif; font-size: 1.5em; - font-weight: 500; display: block; animation: loading-animation 1.5s ease-in-out infinite; } + #code-container > .loading { grid-column-end: span 2; } diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 6a3989d22..1aa346ced 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -20,11 +20,28 @@

LARA Visualization Tool

resume Continue -
+
Loading...
+
main.cpp
+
main.cpp
+
main.cpp
+
main.cpp
+
main.cpp
+
main.cpp
+
main.cpp
+
main.cpp
+
main.cpp
+
main.cpp
+
main.cpp
+
main.cpp
+
main.cpp
+
main.cpp
+
main.cpp
+
main.cpp
+
main.cpp
Loading...
diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index 414e5fa0f..6d43344c7 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -144,9 +144,6 @@ const addDividerEventListeners = (resizer: HTMLElement, astContainer: HTMLElemen const rootStyle = document.documentElement.style; - const astLeft = astContainer.getBoundingClientRect().left; - const maxWidth = codeContainer.getBoundingClientRect().right - astLeft - 160; - resizer.addEventListener('mousedown', () => { drag = true; }); @@ -157,6 +154,9 @@ const addDividerEventListeners = (resizer: HTMLElement, astContainer: HTMLElemen document.addEventListener('mousemove', event => { if (drag) { + const astLeft = astContainer.getBoundingClientRect().left; + const maxWidth = codeContainer.getBoundingClientRect().right - astLeft - 160; + width = event.x - astLeft; if (width < continueButton.offsetWidth) width = continueButton.offsetWidth; diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index 7a0c22755..e91611b11 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -113,8 +113,6 @@ const addDividerEventListeners = (resizer, astContainer, codeContainer, continue let drag = false; let width = astContainer.offsetWidth; const rootStyle = document.documentElement.style; - const astLeft = astContainer.getBoundingClientRect().left; - const maxWidth = codeContainer.getBoundingClientRect().right - astLeft - 160; resizer.addEventListener('mousedown', () => { drag = true; }); @@ -123,6 +121,8 @@ const addDividerEventListeners = (resizer, astContainer, codeContainer, continue }); document.addEventListener('mousemove', event => { if (drag) { + const astLeft = astContainer.getBoundingClientRect().left; + const maxWidth = codeContainer.getBoundingClientRect().right - astLeft - 160; width = event.x - astLeft; if (width < continueButton.offsetWidth) width = continueButton.offsetWidth; From 2189d7ff13f97321c47975271b153dcc3083546c Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 23 Jul 2024 11:18:57 +0100 Subject: [PATCH 098/136] Start division of code into files --- .../visualization/GenericAstConverter.ts | 6 +++++- Lara-JS/src-api/visualization/public/index.html | 17 ----------------- .../visualization/public/js/communication.ts | 2 +- .../visualization/public/js/communication.js | 2 +- 4 files changed, 7 insertions(+), 20 deletions(-) diff --git a/Lara-JS/src-api/visualization/GenericAstConverter.ts b/Lara-JS/src-api/visualization/GenericAstConverter.ts index a84b3f771..b5e76f47a 100644 --- a/Lara-JS/src-api/visualization/GenericAstConverter.ts +++ b/Lara-JS/src-api/visualization/GenericAstConverter.ts @@ -1,7 +1,11 @@ import { LaraJoinPoint } from "../LaraJoinPoint.js"; import ToolJoinPoint from "./public/js/ToolJoinPoint.js"; +export type FilesCode = { + [file: string]: string; +}; + export default interface GenericAstConverter { getToolAst(root: LaraJoinPoint): ToolJoinPoint; - getPrettyHtmlCode(root: LaraJoinPoint): string; + getPrettyHtmlCode(root: LaraJoinPoint): FilesCode; } \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 1aa346ced..1ca8cf1f1 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -25,23 +25,6 @@

LARA Visualization Tool

Loading...
-
main.cpp
-
main.cpp
-
main.cpp
-
main.cpp
-
main.cpp
-
main.cpp
-
main.cpp
-
main.cpp
-
main.cpp
-
main.cpp
-
main.cpp
-
main.cpp
-
main.cpp
-
main.cpp
-
main.cpp
-
main.cpp
-
main.cpp
Loading...
diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts index a0ec86c2c..842bd1d38 100644 --- a/Lara-JS/src-api/visualization/public/js/communication.ts +++ b/Lara-JS/src-api/visualization/public/js/communication.ts @@ -22,7 +22,7 @@ const webSocketOnMessage = (message: MessageEvent, continueButton: HTMLButtonEle const buttonDisabled = continueButton.disabled; continueButton.disabled = true; - importCode(code, codeContainer); + importCode(code[""], codeContainer); importAst(ast, astContainer, codeContainer); continueButton.disabled = buttonDisabled; diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js index 2a09bd4a6..d7edcd992 100644 --- a/LaraApi/src-lara/visualization/public/js/communication.js +++ b/LaraApi/src-lara/visualization/public/js/communication.js @@ -16,7 +16,7 @@ const webSocketOnMessage = (message, continueButton, astContainer, codeContainer const { code, ast } = data; const buttonDisabled = continueButton.disabled; continueButton.disabled = true; - importCode(code, codeContainer); + importCode(code[""], codeContainer); importAst(ast, astContainer, codeContainer); continueButton.disabled = buttonDisabled; break; From 235659958cc32c015289a9543235cd02dd5297fe Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 23 Jul 2024 13:57:36 +0100 Subject: [PATCH 099/136] Finish division of code into files --- .../visualization/public/css/styles.css | 2 +- .../visualization/public/js/ast-import.ts | 67 ++++++++++++++----- .../visualization/public/js/communication.ts | 16 ++++- .../src-api/visualization/public/js/main.ts | 5 +- .../visualization/public/js/ast-import.js | 50 ++++++++++---- .../visualization/public/js/communication.js | 11 ++- .../src-lara/visualization/public/js/main.js | 5 +- 7 files changed, 113 insertions(+), 43 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index f3a1e05c4..2ece81a0d 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -256,7 +256,7 @@ main { .file-tab { height: 2.25em; - padding-inline: 0.75em; + padding-inline: 1em; border-radius: 2px; background-color: var(--tab-bg-color); diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index 29945611a..a09b030d0 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -1,6 +1,54 @@ import { countChar, createIcon, } from './utils.js'; import { addEventListenersToAstNodes } from './visualization.js'; import JoinPoint from './ToolJoinPoint.js'; +import ToolJoinPoint from './ToolJoinPoint.js'; + +const [getSelectedFilename, selectFile] = (() => { + let selectedFilename: string | null = null; + + const getSelectedFilename = (): string | null => selectedFilename; + + const selectFile = (filename: string, code: string, codeContainer: HTMLElement, astRoot: ToolJoinPoint, astContainer: HTMLElement) => { + if (filename !== selectedFilename) { + importCode(code, codeContainer); + importAst(astRoot, astContainer, astContainer); + + console.log(filename); + document.querySelectorAll('.file-tab').forEach(tab => tab.classList.remove('active')); + document.querySelector(`.file-tab[data-filename="${filename}"]`)!.classList.add('active'); + } + }; + + return [getSelectedFilename, selectFile]; +})(); + +const createFileTab = (filename: string, code: string, codeContainer: HTMLElement, astRoot: ToolJoinPoint, astContainer: HTMLElement): HTMLDivElement => { + const fileTab = document.createElement('div'); + fileTab.classList.add('file-tab'); + fileTab.dataset.filename = filename; + fileTab.textContent = filename; + + fileTab.addEventListener('click', () => selectFile(filename, code, codeContainer, astRoot, astContainer)); + + return fileTab; +}; + +const importCode = (code: string, codeContainer: HTMLElement): void => { + codeContainer.innerHTML = ''; + + const codeLines = document.createElement('pre'); + codeLines.classList.add('lines'); + codeContainer.appendChild(codeLines); + + const codeWrapper = document.createElement('pre'); + const codeElement = document.createElement('code'); + codeElement.innerHTML = code; + codeWrapper.appendChild(codeElement); + codeContainer.appendChild(codeWrapper); + + const numLines = countChar(code, '\n') + 1; + codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); +} const createAstNodeDropdownButton = (): HTMLButtonElement => { const dropdownButton = document.createElement('button'); @@ -75,23 +123,6 @@ const convertAstNodeToHtml = (root: JoinPoint): DocumentFragment => { return fragment; }; -const importCode = (code: string, codeContainer: HTMLElement): void => { - codeContainer.innerHTML = ''; - - const codeLines = document.createElement('pre'); - codeLines.classList.add('lines'); - codeContainer.appendChild(codeLines); - - const codeWrapper = document.createElement('pre'); - const codeElement = document.createElement('code'); - codeElement.innerHTML = code; - codeWrapper.appendChild(codeElement); - codeContainer.appendChild(codeWrapper); - - const numLines = countChar(code, '\n') + 1; - codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); -} - const importAst = (astRoot: JoinPoint, astContainer: HTMLElement, codeContainer: HTMLElement): void => { const astFragment = convertAstNodeToHtml(astRoot); astContainer.innerHTML = ''; @@ -100,4 +131,4 @@ const importAst = (astRoot: JoinPoint, astContainer: HTMLElement, codeContainer: addEventListenersToAstNodes(astRoot, astContainer, codeContainer); } -export { importCode, importAst }; +export { getSelectedFilename, createFileTab, importAst }; diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts index 842bd1d38..6b37ac002 100644 --- a/Lara-JS/src-api/visualization/public/js/communication.ts +++ b/Lara-JS/src-api/visualization/public/js/communication.ts @@ -1,4 +1,4 @@ -import { importAst, importCode } from "./ast-import.js"; +import { createFileTab, importAst } from "./ast-import.js"; const getWebSocket = (): WebSocket => { const url = `ẁs://${window.location.host}`; @@ -13,7 +13,9 @@ const parseMessage = (message: MessageEvent): any => { return JSON.parse(message.data); }; -const webSocketOnMessage = (message: MessageEvent, continueButton: HTMLButtonElement, astContainer: HTMLElement, codeContainer: HTMLElement): void => { +const webSocketOnMessage = (message: MessageEvent, continueButton: HTMLButtonElement, + astContainer: HTMLElement, codeContainer: HTMLElement, fileTabs: HTMLElement): void => { + const data = parseMessage(message); switch (data.message) { @@ -22,8 +24,16 @@ const webSocketOnMessage = (message: MessageEvent, continueButton: HTMLButtonEle const buttonDisabled = continueButton.disabled; continueButton.disabled = true; - importCode(code[""], codeContainer); + importAst(ast, astContainer, codeContainer); + + const fileTabArray = Object + .entries(code) + .map(([filename, filecode]: any[]) => createFileTab(filename, filecode, codeContainer, ast, astContainer)); + fileTabs.innerHTML = ''; + fileTabs.append(...fileTabArray); + fileTabArray[0].click(); + continueButton.disabled = buttonDisabled; break; diff --git a/Lara-JS/src-api/visualization/public/js/main.ts b/Lara-JS/src-api/visualization/public/js/main.ts index 30e650fe9..5dc705af8 100644 --- a/Lara-JS/src-api/visualization/public/js/main.ts +++ b/Lara-JS/src-api/visualization/public/js/main.ts @@ -6,8 +6,9 @@ import { addDividerEventListeners } from "./visualization.js"; const codeContainer = document.querySelector('#code-container'); const continueButton = document.querySelector('#continue-button'); const resizer = document.querySelector('#resizer'); + const fileTabs = document.querySelector('#file-tabs') - if (!astContainer || !codeContainer || !continueButton || !resizer) { + if (!astContainer || !codeContainer || !continueButton || !resizer || !fileTabs) { console.error('Required elements not found'); return; } @@ -17,7 +18,7 @@ import { addDividerEventListeners } from "./visualization.js"; let ws: WebSocket; const setupWebSocket = () => { ws = getWebSocket(); - ws.addEventListener('message', event => webSocketOnMessage(event, continueButton, astContainer, codeContainer)); + ws.addEventListener('message', event => webSocketOnMessage(event, continueButton, astContainer, codeContainer, fileTabs)); ws.addEventListener('close', () => setupWebSocket()); }; setupWebSocket(); diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index 54870f419..aa8d331ac 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -1,5 +1,40 @@ import { countChar, createIcon, } from './utils.js'; import { addEventListenersToAstNodes } from './visualization.js'; +const [getSelectedFilename, selectFile] = (() => { + let selectedFilename = null; + const getSelectedFilename = () => selectedFilename; + const selectFile = (filename, code, codeContainer, astRoot, astContainer) => { + if (filename !== selectedFilename) { + importCode(code, codeContainer); + importAst(astRoot, astContainer, astContainer); + console.log(filename); + document.querySelectorAll('.file-tab').forEach(tab => tab.classList.remove('active')); + document.querySelector(`.file-tab[data-filename="${filename}"]`).classList.add('active'); + } + }; + return [getSelectedFilename, selectFile]; +})(); +const createFileTab = (filename, code, codeContainer, astRoot, astContainer) => { + const fileTab = document.createElement('div'); + fileTab.classList.add('file-tab'); + fileTab.dataset.filename = filename; + fileTab.textContent = filename; + fileTab.addEventListener('click', () => selectFile(filename, code, codeContainer, astRoot, astContainer)); + return fileTab; +}; +const importCode = (code, codeContainer) => { + codeContainer.innerHTML = ''; + const codeLines = document.createElement('pre'); + codeLines.classList.add('lines'); + codeContainer.appendChild(codeLines); + const codeWrapper = document.createElement('pre'); + const codeElement = document.createElement('code'); + codeElement.innerHTML = code; + codeWrapper.appendChild(codeElement); + codeContainer.appendChild(codeWrapper); + const numLines = countChar(code, '\n') + 1; + codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); +}; const createAstNodeDropdownButton = () => { const dropdownButton = document.createElement('button'); const chevronIcon = createIcon("keyboard_arrow_down"); @@ -54,24 +89,11 @@ const convertAstNodeToHtml = (root) => { } return fragment; }; -const importCode = (code, codeContainer) => { - codeContainer.innerHTML = ''; - const codeLines = document.createElement('pre'); - codeLines.classList.add('lines'); - codeContainer.appendChild(codeLines); - const codeWrapper = document.createElement('pre'); - const codeElement = document.createElement('code'); - codeElement.innerHTML = code; - codeWrapper.appendChild(codeElement); - codeContainer.appendChild(codeWrapper); - const numLines = countChar(code, '\n') + 1; - codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); -}; const importAst = (astRoot, astContainer, codeContainer) => { const astFragment = convertAstNodeToHtml(astRoot); astContainer.innerHTML = ''; astContainer.appendChild(astFragment); addEventListenersToAstNodes(astRoot, astContainer, codeContainer); }; -export { importCode, importAst }; +export { getSelectedFilename, createFileTab, importAst }; //# sourceMappingURL=ast-import.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js index d7edcd992..ffd70abb4 100644 --- a/LaraApi/src-lara/visualization/public/js/communication.js +++ b/LaraApi/src-lara/visualization/public/js/communication.js @@ -1,4 +1,4 @@ -import { importAst, importCode } from "./ast-import.js"; +import { createFileTab, importAst } from "./ast-import.js"; const getWebSocket = () => { const url = `ẁs://${window.location.host}`; return new WebSocket(url); @@ -9,15 +9,20 @@ const sendData = (ws, data) => { const parseMessage = (message) => { return JSON.parse(message.data); }; -const webSocketOnMessage = (message, continueButton, astContainer, codeContainer) => { +const webSocketOnMessage = (message, continueButton, astContainer, codeContainer, fileTabs) => { const data = parseMessage(message); switch (data.message) { case 'update': const { code, ast } = data; const buttonDisabled = continueButton.disabled; continueButton.disabled = true; - importCode(code[""], codeContainer); importAst(ast, astContainer, codeContainer); + const fileTabArray = Object + .entries(code) + .map(([filename, filecode]) => createFileTab(filename, filecode, codeContainer, ast, astContainer)); + fileTabs.innerHTML = ''; + fileTabs.append(...fileTabArray); + fileTabArray[0].click(); continueButton.disabled = buttonDisabled; break; case 'wait': diff --git a/LaraApi/src-lara/visualization/public/js/main.js b/LaraApi/src-lara/visualization/public/js/main.js index e4ef618e9..376d18a75 100644 --- a/LaraApi/src-lara/visualization/public/js/main.js +++ b/LaraApi/src-lara/visualization/public/js/main.js @@ -5,7 +5,8 @@ import { addDividerEventListeners } from "./visualization.js"; const codeContainer = document.querySelector('#code-container'); const continueButton = document.querySelector('#continue-button'); const resizer = document.querySelector('#resizer'); - if (!astContainer || !codeContainer || !continueButton || !resizer) { + const fileTabs = document.querySelector('#file-tabs'); + if (!astContainer || !codeContainer || !continueButton || !resizer || !fileTabs) { console.error('Required elements not found'); return; } @@ -13,7 +14,7 @@ import { addDividerEventListeners } from "./visualization.js"; let ws; const setupWebSocket = () => { ws = getWebSocket(); - ws.addEventListener('message', event => webSocketOnMessage(event, continueButton, astContainer, codeContainer)); + ws.addEventListener('message', event => webSocketOnMessage(event, continueButton, astContainer, codeContainer, fileTabs)); ws.addEventListener('close', () => setupWebSocket()); }; setupWebSocket(); From 69f9aa6d13735df1074db2bb1a26598b5976e27b Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 23 Jul 2024 14:36:30 +0100 Subject: [PATCH 100/136] Create components script --- .../visualization/public/js/components.ts | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Lara-JS/src-api/visualization/public/js/components.ts diff --git a/Lara-JS/src-api/visualization/public/js/components.ts b/Lara-JS/src-api/visualization/public/js/components.ts new file mode 100644 index 000000000..e361c44c2 --- /dev/null +++ b/Lara-JS/src-api/visualization/public/js/components.ts @@ -0,0 +1,47 @@ +const getAstContainer = (() => { + const astContainer = document.querySelector('#ast-container'); + if (!astContainer) { + throw new Error('Could not find AST container'); + } + return (): HTMLDivElement => astContainer; +})(); + +const getCodeContainer = (() => { + const codeContainer = document.querySelector('#code-container'); + if (!codeContainer) { + throw new Error('Could not find code container'); + } + return (): HTMLDivElement => codeContainer; +})(); + +const getContinueButton = (() => { + const continueButton = document.querySelector('#continue-button'); + if (!continueButton) { + throw new Error('Could not find continue button'); + } + return (): HTMLButtonElement => continueButton; +})(); + +const getResizer = (() => { + const resizer = document.querySelector('#resizer'); + if (!resizer) { + throw new Error('Could not find resizer'); + } + return (): HTMLDivElement => resizer; +})(); + +const getFileTabs = (() => { + const fileTabs = document.querySelector('#file-tabs'); + if (!fileTabs) { + throw new Error('Could not find file tabs'); + } + return (): HTMLDivElement => fileTabs; +})(); + +export { + getAstContainer, + getCodeContainer, + getContinueButton, + getResizer, + getFileTabs, +}; \ No newline at end of file From b17f59454e5611802ead849e2284083cf8f8c74f Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 23 Jul 2024 17:28:46 +0100 Subject: [PATCH 101/136] Refactor code file element logic --- .../visualization/public/css/styles.css | 8 +++ .../visualization/public/js/ast-import.ts | 64 +++++++------------ .../visualization/public/js/communication.ts | 16 ++--- .../visualization/public/js/components.ts | 10 +++ .../src-api/visualization/public/js/files.ts | 46 +++++++++++++ .../up/fe/specs/lara/LaraApiJsResource.java | 2 + .../visualization/public/js/ast-import.js | 49 ++++++-------- .../visualization/public/js/communication.js | 13 ++-- .../visualization/public/js/components.js | 44 +++++++++++++ .../src-lara/visualization/public/js/files.js | 34 ++++++++++ 10 files changed, 201 insertions(+), 85 deletions(-) create mode 100644 Lara-JS/src-api/visualization/public/js/files.ts create mode 100644 LaraApi/src-lara/visualization/public/js/components.js create mode 100644 LaraApi/src-lara/visualization/public/js/files.js diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 2ece81a0d..f8d4ff412 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -179,6 +179,14 @@ main { color: var(--line-num-color); } +#code-container code { + display: none; +} + +#code-container code.active { + display: block; +} + #node-info-container { width: fit-content; diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index a09b030d0..a713d6d1c 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -1,53 +1,37 @@ import { countChar, createIcon, } from './utils.js'; import { addEventListenersToAstNodes } from './visualization.js'; import JoinPoint from './ToolJoinPoint.js'; -import ToolJoinPoint from './ToolJoinPoint.js'; +import { getCodeContainer } from './components.js'; -const [getSelectedFilename, selectFile] = (() => { - let selectedFilename: string | null = null; +const updateLines = (): void => { + const codeContainer = getCodeContainer(); + const codeLines = codeContainer.querySelector('.lines')!; + const code = codeContainer.querySelector('code.active')!.textContent!; - const getSelectedFilename = (): string | null => selectedFilename; - - const selectFile = (filename: string, code: string, codeContainer: HTMLElement, astRoot: ToolJoinPoint, astContainer: HTMLElement) => { - if (filename !== selectedFilename) { - importCode(code, codeContainer); - importAst(astRoot, astContainer, astContainer); - - console.log(filename); - document.querySelectorAll('.file-tab').forEach(tab => tab.classList.remove('active')); - document.querySelector(`.file-tab[data-filename="${filename}"]`)!.classList.add('active'); - } - }; - - return [getSelectedFilename, selectFile]; -})(); - -const createFileTab = (filename: string, code: string, codeContainer: HTMLElement, astRoot: ToolJoinPoint, astContainer: HTMLElement): HTMLDivElement => { - const fileTab = document.createElement('div'); - fileTab.classList.add('file-tab'); - fileTab.dataset.filename = filename; - fileTab.textContent = filename; - - fileTab.addEventListener('click', () => selectFile(filename, code, codeContainer, astRoot, astContainer)); - - return fileTab; + const numLines = countChar(code, '\n') + 1; + codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); }; -const importCode = (code: string, codeContainer: HTMLElement): void => { - codeContainer.innerHTML = ''; +const initCodeContainer = (codeContainer: HTMLElement): void => { + codeContainer.innerHTML = ''; + + const codePre = document.createElement('pre'); + codePre.classList.add('lines'); const codeLines = document.createElement('pre'); - codeLines.classList.add('lines'); - codeContainer.appendChild(codeLines); + codeLines.classList.add('code-wrapper'); + + codeContainer.append(codePre, codeLines) +} - const codeWrapper = document.createElement('pre'); +const addCode = (code: string, filename: string): void => { const codeElement = document.createElement('code'); - codeElement.innerHTML = code; - codeWrapper.appendChild(codeElement); - codeContainer.appendChild(codeWrapper); - - const numLines = countChar(code, '\n') + 1; - codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); + codeElement.dataset.filename = filename; + codeElement.innerHTML = code; + + const codeContainer = getCodeContainer(); + const codePre = codeContainer.querySelector('pre.code-wrapper')!; + codePre.appendChild(codeElement); } const createAstNodeDropdownButton = (): HTMLButtonElement => { @@ -131,4 +115,4 @@ const importAst = (astRoot: JoinPoint, astContainer: HTMLElement, codeContainer: addEventListenersToAstNodes(astRoot, astContainer, codeContainer); } -export { getSelectedFilename, createFileTab, importAst }; +export { importAst, initCodeContainer, addCode, updateLines }; diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts index 6b37ac002..1423cec1b 100644 --- a/Lara-JS/src-api/visualization/public/js/communication.ts +++ b/Lara-JS/src-api/visualization/public/js/communication.ts @@ -1,4 +1,5 @@ -import { createFileTab, importAst } from "./ast-import.js"; +import { importAst, initCodeContainer } from "./ast-import.js"; +import { addFile, selectFile } from "./files.js"; const getWebSocket = (): WebSocket => { const url = `ẁs://${window.location.host}`; @@ -25,14 +26,13 @@ const webSocketOnMessage = (message: MessageEvent, continueButton: HTMLButtonEle continueButton.disabled = true; - importAst(ast, astContainer, codeContainer); + initCodeContainer(codeContainer); - const fileTabArray = Object - .entries(code) - .map(([filename, filecode]: any[]) => createFileTab(filename, filecode, codeContainer, ast, astContainer)); - fileTabs.innerHTML = ''; - fileTabs.append(...fileTabArray); - fileTabArray[0].click(); + for (const [filename, filecode] of Object.entries(code)) + addFile(filename, filecode as string); + + importAst(ast, astContainer, codeContainer); + selectFile(Object.keys(code)[0]); continueButton.disabled = buttonDisabled; diff --git a/Lara-JS/src-api/visualization/public/js/components.ts b/Lara-JS/src-api/visualization/public/js/components.ts index e361c44c2..0c0ab4d18 100644 --- a/Lara-JS/src-api/visualization/public/js/components.ts +++ b/Lara-JS/src-api/visualization/public/js/components.ts @@ -38,10 +38,20 @@ const getFileTabs = (() => { return (): HTMLDivElement => fileTabs; })(); +const createFileTab = (filename: string, code: string) => { + const fileTab = document.createElement('div'); + fileTab.classList.add('file-tab'); + fileTab.dataset.filename = filename; + fileTab.textContent = filename; + + return fileTab; +}; + export { getAstContainer, getCodeContainer, getContinueButton, getResizer, getFileTabs, + createFileTab, }; \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/files.ts b/Lara-JS/src-api/visualization/public/js/files.ts new file mode 100644 index 000000000..0f40bbef5 --- /dev/null +++ b/Lara-JS/src-api/visualization/public/js/files.ts @@ -0,0 +1,46 @@ +import { addCode, updateLines } from "./ast-import.js"; +import { createFileTab, getCodeContainer, getFileTabs } from "./components.js"; + +let selectedFilename: string | null = null; + +const addFile = (name: string, code: string): void => { + addCode(code, name); + + const fileTab = createFileTab(name, code); + fileTab.addEventListener('click', () => selectFile(name)); + + const fileTabs = getFileTabs(); + fileTabs.appendChild(fileTab); +} + +const clearFiles = (): void => { + const fileTabs = getFileTabs(); + fileTabs.innerHTML = ''; + + const codeContainer = getCodeContainer(); + codeContainer.querySelector('pre')!.innerHTML = ''; +} + + +const selectFile = (filename: string): void => { + const fileTabs = getFileTabs(); + + if (filename !== selectedFilename) { + const codeContainer = getCodeContainer(); + + const selectedTab = fileTabs.querySelector(`.file-tab[data-filename="${filename}"]`)!; + fileTabs.querySelector('.file-tab.active')?.classList.remove('active'); + selectedTab.classList.add('active'); + + const fileCode = codeContainer.querySelector(`code[data-filename="${filename}"]`)!; + const activeCode = codeContainer.querySelector('code.active'); + if (activeCode) + activeCode.classList.remove('active'); + fileCode.classList.add('active'); + updateLines(); + + selectedFilename = filename; + } +} + +export { addFile, clearFiles, selectFile }; diff --git a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java index 76828ba94..b0dbf900e 100644 --- a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java +++ b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java @@ -110,6 +110,8 @@ public enum LaraApiJsResource implements LaraResourceProvider { TOOLJOINPOINT_JS("visualization/public/js/ToolJoinPoint.js"), AST_IMPORT_JS("visualization/public/js/ast-import.js"), COMMUNICATION_JS("visualization/public/js/communication.js"), + COMPONENTS_JS("visualization/public/js/components.js"), + FILES_JS("visualization/public/js/files.js"), MAIN_JS("visualization/public/js/main.js"), UTILS_JS("visualization/public/js/utils.js"), VISUALIZATION_JS("visualization/public/js/visualization.js"), diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index aa8d331ac..0143301a6 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -1,39 +1,28 @@ import { countChar, createIcon, } from './utils.js'; import { addEventListenersToAstNodes } from './visualization.js'; -const [getSelectedFilename, selectFile] = (() => { - let selectedFilename = null; - const getSelectedFilename = () => selectedFilename; - const selectFile = (filename, code, codeContainer, astRoot, astContainer) => { - if (filename !== selectedFilename) { - importCode(code, codeContainer); - importAst(astRoot, astContainer, astContainer); - console.log(filename); - document.querySelectorAll('.file-tab').forEach(tab => tab.classList.remove('active')); - document.querySelector(`.file-tab[data-filename="${filename}"]`).classList.add('active'); - } - }; - return [getSelectedFilename, selectFile]; -})(); -const createFileTab = (filename, code, codeContainer, astRoot, astContainer) => { - const fileTab = document.createElement('div'); - fileTab.classList.add('file-tab'); - fileTab.dataset.filename = filename; - fileTab.textContent = filename; - fileTab.addEventListener('click', () => selectFile(filename, code, codeContainer, astRoot, astContainer)); - return fileTab; +import { getCodeContainer } from './components.js'; +const updateLines = () => { + const codeContainer = getCodeContainer(); + const codeLines = codeContainer.querySelector('.lines'); + const code = codeContainer.querySelector('code.active').textContent; + const numLines = countChar(code, '\n') + 1; + codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); }; -const importCode = (code, codeContainer) => { +const initCodeContainer = (codeContainer) => { codeContainer.innerHTML = ''; + const codePre = document.createElement('pre'); + codePre.classList.add('lines'); const codeLines = document.createElement('pre'); - codeLines.classList.add('lines'); - codeContainer.appendChild(codeLines); - const codeWrapper = document.createElement('pre'); + codeLines.classList.add('code-wrapper'); + codeContainer.append(codePre, codeLines); +}; +const addCode = (code, filename) => { const codeElement = document.createElement('code'); + codeElement.dataset.filename = filename; codeElement.innerHTML = code; - codeWrapper.appendChild(codeElement); - codeContainer.appendChild(codeWrapper); - const numLines = countChar(code, '\n') + 1; - codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); + const codeContainer = getCodeContainer(); + const codePre = codeContainer.querySelector('pre.code-wrapper'); + codePre.appendChild(codeElement); }; const createAstNodeDropdownButton = () => { const dropdownButton = document.createElement('button'); @@ -95,5 +84,5 @@ const importAst = (astRoot, astContainer, codeContainer) => { astContainer.appendChild(astFragment); addEventListenersToAstNodes(astRoot, astContainer, codeContainer); }; -export { getSelectedFilename, createFileTab, importAst }; +export { importAst, initCodeContainer, addCode, updateLines }; //# sourceMappingURL=ast-import.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js index ffd70abb4..4a264888b 100644 --- a/LaraApi/src-lara/visualization/public/js/communication.js +++ b/LaraApi/src-lara/visualization/public/js/communication.js @@ -1,4 +1,5 @@ -import { createFileTab, importAst } from "./ast-import.js"; +import { importAst, initCodeContainer } from "./ast-import.js"; +import { addFile, selectFile } from "./files.js"; const getWebSocket = () => { const url = `ẁs://${window.location.host}`; return new WebSocket(url); @@ -16,13 +17,11 @@ const webSocketOnMessage = (message, continueButton, astContainer, codeContainer const { code, ast } = data; const buttonDisabled = continueButton.disabled; continueButton.disabled = true; + initCodeContainer(codeContainer); + for (const [filename, filecode] of Object.entries(code)) + addFile(filename, filecode); importAst(ast, astContainer, codeContainer); - const fileTabArray = Object - .entries(code) - .map(([filename, filecode]) => createFileTab(filename, filecode, codeContainer, ast, astContainer)); - fileTabs.innerHTML = ''; - fileTabs.append(...fileTabArray); - fileTabArray[0].click(); + selectFile(Object.keys(code)[0]); continueButton.disabled = buttonDisabled; break; case 'wait': diff --git a/LaraApi/src-lara/visualization/public/js/components.js b/LaraApi/src-lara/visualization/public/js/components.js new file mode 100644 index 000000000..b860a9750 --- /dev/null +++ b/LaraApi/src-lara/visualization/public/js/components.js @@ -0,0 +1,44 @@ +const getAstContainer = (() => { + const astContainer = document.querySelector('#ast-container'); + if (!astContainer) { + throw new Error('Could not find AST container'); + } + return () => astContainer; +})(); +const getCodeContainer = (() => { + const codeContainer = document.querySelector('#code-container'); + if (!codeContainer) { + throw new Error('Could not find code container'); + } + return () => codeContainer; +})(); +const getContinueButton = (() => { + const continueButton = document.querySelector('#continue-button'); + if (!continueButton) { + throw new Error('Could not find continue button'); + } + return () => continueButton; +})(); +const getResizer = (() => { + const resizer = document.querySelector('#resizer'); + if (!resizer) { + throw new Error('Could not find resizer'); + } + return () => resizer; +})(); +const getFileTabs = (() => { + const fileTabs = document.querySelector('#file-tabs'); + if (!fileTabs) { + throw new Error('Could not find file tabs'); + } + return () => fileTabs; +})(); +const createFileTab = (filename, code) => { + const fileTab = document.createElement('div'); + fileTab.classList.add('file-tab'); + fileTab.dataset.filename = filename; + fileTab.textContent = filename; + return fileTab; +}; +export { getAstContainer, getCodeContainer, getContinueButton, getResizer, getFileTabs, createFileTab, }; +//# sourceMappingURL=components.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/files.js b/LaraApi/src-lara/visualization/public/js/files.js new file mode 100644 index 000000000..8743a8ed5 --- /dev/null +++ b/LaraApi/src-lara/visualization/public/js/files.js @@ -0,0 +1,34 @@ +import { addCode, updateLines } from "./ast-import.js"; +import { createFileTab, getCodeContainer, getFileTabs } from "./components.js"; +let selectedFilename = null; +const addFile = (name, code) => { + addCode(code, name); + const fileTab = createFileTab(name, code); + fileTab.addEventListener('click', () => selectFile(name)); + const fileTabs = getFileTabs(); + fileTabs.appendChild(fileTab); +}; +const clearFiles = () => { + const fileTabs = getFileTabs(); + fileTabs.innerHTML = ''; + const codeContainer = getCodeContainer(); + codeContainer.querySelector('pre').innerHTML = ''; +}; +const selectFile = (filename) => { + const fileTabs = getFileTabs(); + if (filename !== selectedFilename) { + const codeContainer = getCodeContainer(); + const selectedTab = fileTabs.querySelector(`.file-tab[data-filename="${filename}"]`); + fileTabs.querySelector('.file-tab.active')?.classList.remove('active'); + selectedTab.classList.add('active'); + const fileCode = codeContainer.querySelector(`code[data-filename="${filename}"]`); + const activeCode = codeContainer.querySelector('code.active'); + if (activeCode) + activeCode.classList.remove('active'); + fileCode.classList.add('active'); + updateLines(); + selectedFilename = filename; + } +}; +export { addFile, clearFiles, selectFile }; +//# sourceMappingURL=files.js.map \ No newline at end of file From c898a8e86168dc62f04d9b1ac6118933aa789209 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 24 Jul 2024 07:50:32 +0100 Subject: [PATCH 102/136] Allow file focus on node click --- .../src-api/visualization/public/js/ToolJoinPoint.ts | 11 ++++++++++- .../src-api/visualization/public/js/communication.ts | 3 ++- .../src-api/visualization/public/js/visualization.ts | 6 +++++- .../visualization/public/js/ToolJoinPoint.js | 12 +++++++++--- .../visualization/public/js/communication.js | 3 ++- .../visualization/public/js/visualization.js | 6 +++++- 6 files changed, 33 insertions(+), 8 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts index 9e9dc6638..f9f7a492d 100644 --- a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts +++ b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts @@ -3,12 +3,14 @@ export type JoinPointInfo = { [attribute: string]: string }; export default class ToolJoinPoint { #id: string; #type: string; + #filename: string | undefined; #info: JoinPointInfo; #children: ToolJoinPoint[]; - constructor(id: string, type: string, info: JoinPointInfo, children: ToolJoinPoint[]) { + constructor(id: string, type: string, filename: string | undefined, info: JoinPointInfo, children: ToolJoinPoint[]) { this.#id = id; this.#type = type; + this.#filename = filename; this.#info = info; this.#children = children; } @@ -21,6 +23,10 @@ export default class ToolJoinPoint { return this.#type; } + get filename(): string | undefined { + return this.#filename; + } + get info(): JoinPointInfo { return this.#info; } @@ -33,6 +39,7 @@ export default class ToolJoinPoint { return new ToolJoinPoint( json.id, json.type, + json.filename, json.info, json.children.map((child: any) => ToolJoinPoint.fromJson(child)) ); @@ -42,6 +49,7 @@ export default class ToolJoinPoint { return { id: this.#id, type: this.#type, + filename: this.#filename, info: this.#info, children: this.#children.map((child) => child.toJson()), }; @@ -51,6 +59,7 @@ export default class ToolJoinPoint { return new ToolJoinPoint( this.#id, this.#type, + this.#filename, this.#info, this.#children.map((child) => child.clone()) ); diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts index 1423cec1b..10bef34bc 100644 --- a/Lara-JS/src-api/visualization/public/js/communication.ts +++ b/Lara-JS/src-api/visualization/public/js/communication.ts @@ -1,5 +1,5 @@ import { importAst, initCodeContainer } from "./ast-import.js"; -import { addFile, selectFile } from "./files.js"; +import { addFile, clearFiles, selectFile } from "./files.js"; const getWebSocket = (): WebSocket => { const url = `ẁs://${window.location.host}`; @@ -28,6 +28,7 @@ const webSocketOnMessage = (message: MessageEvent, continueButton: HTMLButtonEle initCodeContainer(codeContainer); + clearFiles(); for (const [filename, filecode] of Object.entries(code)) addFile(filename, filecode as string); diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index 6d43344c7..4a03aea38 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -1,3 +1,4 @@ +import { selectFile } from "./files.js"; import JoinPoint, { JoinPointInfo } from "./ToolJoinPoint.js"; const getNodeElement = (nodeId: string): HTMLElement | null => { @@ -9,7 +10,7 @@ const getNodeRelatedElements = (nodeId: string): HTMLElement[] => { }; const highlightNode = (nodeId: string, strong: boolean): void => { - const nodeCode = document.querySelectorAll(`.node-code[data-node-id="${nodeId}"]`)!; + const nodeCode = document.querySelectorAll(` .node-code[data-node-id="${nodeId}"]`)!; nodeCode.forEach(elem => elem.style.backgroundColor = strong ? 'var(--highlight-color)' : 'var(--secondary-highlight-color)'); const nodeElement = document.querySelector(`.ast-node[data-node-id="${nodeId}"]`)!; @@ -84,6 +85,7 @@ const addHighlighingEvents = (node: JoinPoint, astContainer: HTMLElement, codeCo const nodeRelatedElements = getNodeRelatedElements(node.id); for (const nodeRelatedElement of nodeRelatedElements) { nodeRelatedElement.addEventListener('mouseover', event => { + console.log(node.id); highlightNode(node.id, false); if (selectedNodeId !== null) highlightNode(selectedNodeId, true); @@ -111,6 +113,8 @@ const addHighlighingEvents = (node: JoinPoint, astContainer: HTMLElement, codeCo selectedNodeId = node.id; highlightNode(node.id, true); + if (node.filename) + selectFile(node.filename); const nodeElement = getNodeElement(node.id)!; scrollIntoViewIfNeeded(nodeElement, astContainer); diff --git a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js index bea947486..5dd7fa5c4 100644 --- a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js +++ b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js @@ -1,11 +1,13 @@ export default class ToolJoinPoint { #id; #type; + #filename; #info; #children; - constructor(id, type, info, children) { + constructor(id, type, filename, info, children) { this.#id = id; this.#type = type; + this.#filename = filename; this.#info = info; this.#children = children; } @@ -15,6 +17,9 @@ export default class ToolJoinPoint { get type() { return this.#type; } + get filename() { + return this.#filename; + } get info() { return this.#info; } @@ -22,18 +27,19 @@ export default class ToolJoinPoint { return this.#children; } static fromJson(json) { - return new ToolJoinPoint(json.id, json.type, json.info, json.children.map((child) => ToolJoinPoint.fromJson(child))); + return new ToolJoinPoint(json.id, json.type, json.filename, json.info, json.children.map((child) => ToolJoinPoint.fromJson(child))); } toJson() { return { id: this.#id, type: this.#type, + filename: this.#filename, info: this.#info, children: this.#children.map((child) => child.toJson()), }; } clone() { - return new ToolJoinPoint(this.#id, this.#type, this.#info, this.#children.map((child) => child.clone())); + return new ToolJoinPoint(this.#id, this.#type, this.#filename, this.#info, this.#children.map((child) => child.clone())); } } ; diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js index 4a264888b..c8282b3e5 100644 --- a/LaraApi/src-lara/visualization/public/js/communication.js +++ b/LaraApi/src-lara/visualization/public/js/communication.js @@ -1,5 +1,5 @@ import { importAst, initCodeContainer } from "./ast-import.js"; -import { addFile, selectFile } from "./files.js"; +import { addFile, clearFiles, selectFile } from "./files.js"; const getWebSocket = () => { const url = `ẁs://${window.location.host}`; return new WebSocket(url); @@ -18,6 +18,7 @@ const webSocketOnMessage = (message, continueButton, astContainer, codeContainer const buttonDisabled = continueButton.disabled; continueButton.disabled = true; initCodeContainer(codeContainer); + clearFiles(); for (const [filename, filecode] of Object.entries(code)) addFile(filename, filecode); importAst(ast, astContainer, codeContainer); diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index e91611b11..0e62d0460 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -1,3 +1,4 @@ +import { selectFile } from "./files.js"; const getNodeElement = (nodeId) => { return document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); }; @@ -5,7 +6,7 @@ const getNodeRelatedElements = (nodeId) => { return Array.from(document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"] .ast-node-text, .node-code[data-node-id="${nodeId}"]`)); }; const highlightNode = (nodeId, strong) => { - const nodeCode = document.querySelectorAll(`.node-code[data-node-id="${nodeId}"]`); + const nodeCode = document.querySelectorAll(` .node-code[data-node-id="${nodeId}"]`); nodeCode.forEach(elem => elem.style.backgroundColor = strong ? 'var(--highlight-color)' : 'var(--secondary-highlight-color)'); const nodeElement = document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); const nodeText = nodeElement.querySelector('.ast-node-text'); @@ -64,6 +65,7 @@ const addHighlighingEvents = (node, astContainer, codeContainer) => { const nodeRelatedElements = getNodeRelatedElements(node.id); for (const nodeRelatedElement of nodeRelatedElements) { nodeRelatedElement.addEventListener('mouseover', event => { + console.log(node.id); highlightNode(node.id, false); if (selectedNodeId !== null) highlightNode(selectedNodeId, true); @@ -88,6 +90,8 @@ const addHighlighingEvents = (node, astContainer, codeContainer) => { } selectedNodeId = node.id; highlightNode(node.id, true); + if (node.filename) + selectFile(node.filename); const nodeElement = getNodeElement(node.id); scrollIntoViewIfNeeded(nodeElement, astContainer); const firstNodeCodeBlock = document.querySelector(`.node-code[data-node-id="${node.id}"]`); From 43641fe68dc87b81cec5b5c42d6e12a561c373cf Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 24 Jul 2024 08:40:20 +0100 Subject: [PATCH 103/136] Fix files clearing --- Lara-JS/src-api/visualization/public/js/files.ts | 2 ++ Lara-JS/src-api/visualization/public/js/visualization.ts | 1 - LaraApi/src-lara/visualization/public/js/files.js | 1 + LaraApi/src-lara/visualization/public/js/visualization.js | 1 - 4 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/files.ts b/Lara-JS/src-api/visualization/public/js/files.ts index 0f40bbef5..a85b58ae7 100644 --- a/Lara-JS/src-api/visualization/public/js/files.ts +++ b/Lara-JS/src-api/visualization/public/js/files.ts @@ -19,6 +19,8 @@ const clearFiles = (): void => { const codeContainer = getCodeContainer(); codeContainer.querySelector('pre')!.innerHTML = ''; + + selectedFilename = null; } diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index 4a03aea38..5256550cb 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -85,7 +85,6 @@ const addHighlighingEvents = (node: JoinPoint, astContainer: HTMLElement, codeCo const nodeRelatedElements = getNodeRelatedElements(node.id); for (const nodeRelatedElement of nodeRelatedElements) { nodeRelatedElement.addEventListener('mouseover', event => { - console.log(node.id); highlightNode(node.id, false); if (selectedNodeId !== null) highlightNode(selectedNodeId, true); diff --git a/LaraApi/src-lara/visualization/public/js/files.js b/LaraApi/src-lara/visualization/public/js/files.js index 8743a8ed5..c3ac9997b 100644 --- a/LaraApi/src-lara/visualization/public/js/files.js +++ b/LaraApi/src-lara/visualization/public/js/files.js @@ -13,6 +13,7 @@ const clearFiles = () => { fileTabs.innerHTML = ''; const codeContainer = getCodeContainer(); codeContainer.querySelector('pre').innerHTML = ''; + selectedFilename = null; }; const selectFile = (filename) => { const fileTabs = getFileTabs(); diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index 0e62d0460..680c108df 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -65,7 +65,6 @@ const addHighlighingEvents = (node, astContainer, codeContainer) => { const nodeRelatedElements = getNodeRelatedElements(node.id); for (const nodeRelatedElement of nodeRelatedElements) { nodeRelatedElement.addEventListener('mouseover', event => { - console.log(node.id); highlightNode(node.id, false); if (selectedNodeId !== null) highlightNode(selectedNodeId, true); From 3e95a51c2141c4eacd7740308ee5df7d824e38e8 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 24 Jul 2024 10:11:57 +0100 Subject: [PATCH 104/136] Add syntax higlighting color scheme and classes --- .../visualization/public/css/color-scheme.css | 4 +++- .../public/css/syntax-highlighting.css | 17 +++++++++++++++++ Lara-JS/src-api/visualization/public/index.html | 1 + 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 Lara-JS/src-api/visualization/public/css/syntax-highlighting.css diff --git a/Lara-JS/src-api/visualization/public/css/color-scheme.css b/Lara-JS/src-api/visualization/public/css/color-scheme.css index f94ee20b3..0979e7a59 100644 --- a/Lara-JS/src-api/visualization/public/css/color-scheme.css +++ b/Lara-JS/src-api/visualization/public/css/color-scheme.css @@ -11,9 +11,11 @@ --light-blue: #19d8ff; --strong-translucid-light-blue: #19d8ff66; --weak-translucid-light-blue: #19d8ff33; + --blue: blue; + --purple: purple; } -/* Color Scheme */ +/* Color scheme */ :root { --bg-color: var(--white); diff --git a/Lara-JS/src-api/visualization/public/css/syntax-highlighting.css b/Lara-JS/src-api/visualization/public/css/syntax-highlighting.css new file mode 100644 index 000000000..f96efd75f --- /dev/null +++ b/Lara-JS/src-api/visualization/public/css/syntax-highlighting.css @@ -0,0 +1,17 @@ +/* Syntax highlighting color scheme (using base colors in color-scheme.css) */ + +:root { + --comment: var(--dark-gray); + --keyword: var(--purple); + --type: var(--purple); + --string: var(--blue); + --literal: var(--blue); +} + +/* Syntax highlighting classes */ + +.comment { color: var(--comment); } +.keyword { color: var(--keyword); } +.type { color: var(--type); } +.string { color: var(--string); } +.literal { color: var(--literal); } \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/index.html b/Lara-JS/src-api/visualization/public/index.html index 1ca8cf1f1..1283556a1 100644 --- a/Lara-JS/src-api/visualization/public/index.html +++ b/Lara-JS/src-api/visualization/public/index.html @@ -6,6 +6,7 @@ + LARA Visualization Tool From a87cf4d3a7adaff3a2f4715d45181e33a2fd112e Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 00:53:13 +0100 Subject: [PATCH 105/136] Fix scrollIntoViewIfNeeded and add overflow to node info container --- Lara-JS/src-api/visualization/public/css/styles.css | 4 ++++ Lara-JS/src-api/visualization/public/js/visualization.ts | 2 +- LaraApi/src-lara/visualization/public/js/visualization.js | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index f8d4ff412..943491697 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -190,7 +190,9 @@ main { #node-info-container { width: fit-content; + max-width: 50vw; height: fit-content; + max-height: 50vh; padding-inline: 0.5em; font-weight: 400; @@ -201,6 +203,8 @@ main { z-index: 2; display: none; + + overflow: auto; } #node-info-container p { diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index 5256550cb..f5aeb0ec1 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -75,7 +75,7 @@ const scrollIntoViewIfNeeded = (element: HTMLElement, parent: HTMLElement): void const scrollPos = rect.height <= parentRect.height ? (rect.top + rect.bottom - parentRect.top - parentRect.bottom) / 2 : rect.top - parentRect.top; - parent.scrollBy({ top: scrollPos, left: rect.left, behavior: 'smooth' }); + parent.scrollBy({ top: scrollPos, left: 0, behavior: 'smooth' }); } }; diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index 680c108df..0383cf041 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -57,7 +57,7 @@ const scrollIntoViewIfNeeded = (element, parent) => { const scrollPos = rect.height <= parentRect.height ? (rect.top + rect.bottom - parentRect.top - parentRect.bottom) / 2 : rect.top - parentRect.top; - parent.scrollBy({ top: scrollPos, left: rect.left, behavior: 'smooth' }); + parent.scrollBy({ top: scrollPos, left: 0, behavior: 'smooth' }); } }; let selectedNodeId = null; From 296985ef8e55cc3d1b33911655c3e96c6b64c9e6 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 01:27:47 +0100 Subject: [PATCH 106/136] Show node code when it cannot be found --- .../visualization/public/css/color-scheme.css | 1 + .../visualization/public/css/styles.css | 9 +++++++-- .../visualization/public/js/ToolJoinPoint.ts | 11 ++++++++++- .../visualization/public/js/components.ts | 4 ++-- .../src-api/visualization/public/js/files.ts | 2 +- .../visualization/public/js/visualization.ts | 19 ++++++++++++++++--- .../visualization/public/js/ToolJoinPoint.js | 12 +++++++++--- .../visualization/public/js/components.js | 4 ++-- .../src-lara/visualization/public/js/files.js | 2 +- .../visualization/public/js/visualization.js | 16 +++++++++++++--- 10 files changed, 62 insertions(+), 18 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/color-scheme.css b/Lara-JS/src-api/visualization/public/css/color-scheme.css index 0979e7a59..8cc8ab8d8 100644 --- a/Lara-JS/src-api/visualization/public/css/color-scheme.css +++ b/Lara-JS/src-api/visualization/public/css/color-scheme.css @@ -38,4 +38,5 @@ --tertiary-highlight-color: var(--weak-translucid-light-blue); --line-num-color: var(--dark-gray); + --secondary-code-bg-color: var(--lighter-gray); } \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 943491697..73b900bc7 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -211,14 +211,19 @@ main { margin: 0; } -#node-info-container p > span:first-of-type { +#node-info-container p > span:first-of-type, #node-info-container .alert { font-weight: bold; } -#node-info-container p > span:first-of-type::after { +#node-info-container p > span:first-of-type::after, #node-info-container .alert::after { content: ': '; } +#node-info-container pre { + border-radius: 4px; + background-color: var(--secondary-code-bg-color); +} + #resizer { width: 1em; height: 1.2em; diff --git a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts index f9f7a492d..bf2423357 100644 --- a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts +++ b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts @@ -3,14 +3,16 @@ export type JoinPointInfo = { [attribute: string]: string }; export default class ToolJoinPoint { #id: string; #type: string; + #code: string; #filename: string | undefined; #info: JoinPointInfo; #children: ToolJoinPoint[]; - constructor(id: string, type: string, filename: string | undefined, info: JoinPointInfo, children: ToolJoinPoint[]) { + constructor(id: string, type: string, code: string, filename: string | undefined, info: JoinPointInfo, children: ToolJoinPoint[]) { this.#id = id; this.#type = type; this.#filename = filename; + this.#code = code; this.#info = info; this.#children = children; } @@ -23,6 +25,10 @@ export default class ToolJoinPoint { return this.#type; } + get code(): string { + return this.#code; + } + get filename(): string | undefined { return this.#filename; } @@ -39,6 +45,7 @@ export default class ToolJoinPoint { return new ToolJoinPoint( json.id, json.type, + json.code, json.filename, json.info, json.children.map((child: any) => ToolJoinPoint.fromJson(child)) @@ -49,6 +56,7 @@ export default class ToolJoinPoint { return { id: this.#id, type: this.#type, + code: this.#code, filename: this.#filename, info: this.#info, children: this.#children.map((child) => child.toJson()), @@ -59,6 +67,7 @@ export default class ToolJoinPoint { return new ToolJoinPoint( this.#id, this.#type, + this.#code, this.#filename, this.#info, this.#children.map((child) => child.clone()) diff --git a/Lara-JS/src-api/visualization/public/js/components.ts b/Lara-JS/src-api/visualization/public/js/components.ts index 0c0ab4d18..43d809129 100644 --- a/Lara-JS/src-api/visualization/public/js/components.ts +++ b/Lara-JS/src-api/visualization/public/js/components.ts @@ -38,11 +38,11 @@ const getFileTabs = (() => { return (): HTMLDivElement => fileTabs; })(); -const createFileTab = (filename: string, code: string) => { +const createFileTab = (filename: string) => { const fileTab = document.createElement('div'); fileTab.classList.add('file-tab'); fileTab.dataset.filename = filename; - fileTab.textContent = filename; + fileTab.textContent = filename ?? ''; return fileTab; }; diff --git a/Lara-JS/src-api/visualization/public/js/files.ts b/Lara-JS/src-api/visualization/public/js/files.ts index a85b58ae7..c0b608817 100644 --- a/Lara-JS/src-api/visualization/public/js/files.ts +++ b/Lara-JS/src-api/visualization/public/js/files.ts @@ -6,7 +6,7 @@ let selectedFilename: string | null = null; const addFile = (name: string, code: string): void => { addCode(code, name); - const fileTab = createFileTab(name, code); + const fileTab = createFileTab(name); fileTab.addEventListener('click', () => selectFile(name)); const fileTabs = getFileTabs(); diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index f5aeb0ec1..3cfdc99e5 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -43,12 +43,12 @@ const unhighlightNode = (nodeId: string): void => { } }; -const showNodeInfo = (nodeInfo: JoinPointInfo): void => { +const showNodeInfo = (node: JoinPoint): void => { const nodeInfoContainer = document.querySelector('#node-info-container')!; nodeInfoContainer.style.display = 'block'; nodeInfoContainer.innerHTML = '' - for (const [name, value] of Object.entries(nodeInfo)) { + for (const [name, value] of Object.entries(node.info)) { const attributeName = document.createElement('span'); attributeName.textContent = name; @@ -59,6 +59,19 @@ const showNodeInfo = (nodeInfo: JoinPointInfo): void => { line.append(attributeName, attributeValue); nodeInfoContainer.appendChild(line); } + + if (!document.querySelector(`.node-code[data-node-id="${node.id}"]`)) { + const codeAlert = document.createElement('p'); + codeAlert.classList.add('alert'); + codeAlert.textContent = 'Node code not found'; + + const codeWrapper = document.createElement('pre'); + const code = document.createElement('code'); + code.textContent = node.code; + codeWrapper.appendChild(code) + + nodeInfoContainer.append(codeAlert, codeWrapper); + } } const hideNodeInfo = (): void => { @@ -121,7 +134,7 @@ const addHighlighingEvents = (node: JoinPoint, astContainer: HTMLElement, codeCo if (firstNodeCodeBlock) scrollIntoViewIfNeeded(firstNodeCodeBlock!, codeContainer); - showNodeInfo(node.info); + showNodeInfo(node); }); // For keyboard accessibility diff --git a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js index 5dd7fa5c4..d3db75539 100644 --- a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js +++ b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js @@ -1,13 +1,15 @@ export default class ToolJoinPoint { #id; #type; + #code; #filename; #info; #children; - constructor(id, type, filename, info, children) { + constructor(id, type, code, filename, info, children) { this.#id = id; this.#type = type; this.#filename = filename; + this.#code = code; this.#info = info; this.#children = children; } @@ -17,6 +19,9 @@ export default class ToolJoinPoint { get type() { return this.#type; } + get code() { + return this.#code; + } get filename() { return this.#filename; } @@ -27,19 +32,20 @@ export default class ToolJoinPoint { return this.#children; } static fromJson(json) { - return new ToolJoinPoint(json.id, json.type, json.filename, json.info, json.children.map((child) => ToolJoinPoint.fromJson(child))); + return new ToolJoinPoint(json.id, json.type, json.code, json.filename, json.info, json.children.map((child) => ToolJoinPoint.fromJson(child))); } toJson() { return { id: this.#id, type: this.#type, + code: this.#code, filename: this.#filename, info: this.#info, children: this.#children.map((child) => child.toJson()), }; } clone() { - return new ToolJoinPoint(this.#id, this.#type, this.#filename, this.#info, this.#children.map((child) => child.clone())); + return new ToolJoinPoint(this.#id, this.#type, this.#code, this.#filename, this.#info, this.#children.map((child) => child.clone())); } } ; diff --git a/LaraApi/src-lara/visualization/public/js/components.js b/LaraApi/src-lara/visualization/public/js/components.js index b860a9750..04d628762 100644 --- a/LaraApi/src-lara/visualization/public/js/components.js +++ b/LaraApi/src-lara/visualization/public/js/components.js @@ -33,11 +33,11 @@ const getFileTabs = (() => { } return () => fileTabs; })(); -const createFileTab = (filename, code) => { +const createFileTab = (filename) => { const fileTab = document.createElement('div'); fileTab.classList.add('file-tab'); fileTab.dataset.filename = filename; - fileTab.textContent = filename; + fileTab.textContent = filename ?? ''; return fileTab; }; export { getAstContainer, getCodeContainer, getContinueButton, getResizer, getFileTabs, createFileTab, }; diff --git a/LaraApi/src-lara/visualization/public/js/files.js b/LaraApi/src-lara/visualization/public/js/files.js index c3ac9997b..9e59a92ff 100644 --- a/LaraApi/src-lara/visualization/public/js/files.js +++ b/LaraApi/src-lara/visualization/public/js/files.js @@ -3,7 +3,7 @@ import { createFileTab, getCodeContainer, getFileTabs } from "./components.js"; let selectedFilename = null; const addFile = (name, code) => { addCode(code, name); - const fileTab = createFileTab(name, code); + const fileTab = createFileTab(name); fileTab.addEventListener('click', () => selectFile(name)); const fileTabs = getFileTabs(); fileTabs.appendChild(fileTab); diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index 0383cf041..d2ca39b93 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -31,11 +31,11 @@ const unhighlightNode = (nodeId) => { parentNode = parentNode.parentElement?.previousSibling; } }; -const showNodeInfo = (nodeInfo) => { +const showNodeInfo = (node) => { const nodeInfoContainer = document.querySelector('#node-info-container'); nodeInfoContainer.style.display = 'block'; nodeInfoContainer.innerHTML = ''; - for (const [name, value] of Object.entries(nodeInfo)) { + for (const [name, value] of Object.entries(node.info)) { const attributeName = document.createElement('span'); attributeName.textContent = name; const attributeValue = document.createElement('span'); @@ -44,6 +44,16 @@ const showNodeInfo = (nodeInfo) => { line.append(attributeName, attributeValue); nodeInfoContainer.appendChild(line); } + if (!document.querySelector(`.node-code[data-node-id="${node.id}"]`)) { + const codeAlert = document.createElement('p'); + codeAlert.classList.add('alert'); + codeAlert.textContent = 'Node code not found'; + const codeWrapper = document.createElement('pre'); + const code = document.createElement('code'); + code.textContent = node.code; + codeWrapper.appendChild(code); + nodeInfoContainer.append(codeAlert, codeWrapper); + } }; const hideNodeInfo = () => { const nodeInfoContainer = document.querySelector('#node-info-container'); @@ -96,7 +106,7 @@ const addHighlighingEvents = (node, astContainer, codeContainer) => { const firstNodeCodeBlock = document.querySelector(`.node-code[data-node-id="${node.id}"]`); if (firstNodeCodeBlock) scrollIntoViewIfNeeded(firstNodeCodeBlock, codeContainer); - showNodeInfo(node.info); + showNodeInfo(node); }); // For keyboard accessibility nodeRelatedElement.addEventListener('keydown', event => { From 0c22ffb9ba736ad949c95ea074c1b9bb330254b4 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 14:48:28 +0100 Subject: [PATCH 107/136] Implement dark mode --- .../visualization/public/css/color-scheme.css | 40 +++++++++++++++++-- .../visualization/public/css/styles.css | 3 +- .../public/css/syntax-highlighting.css | 12 +++++- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/color-scheme.css b/Lara-JS/src-api/visualization/public/css/color-scheme.css index 8cc8ab8d8..4a0b473e0 100644 --- a/Lara-JS/src-api/visualization/public/css/color-scheme.css +++ b/Lara-JS/src-api/visualization/public/css/color-scheme.css @@ -7,15 +7,19 @@ --gray: #a2a2a2; --dark-gray: #747474; --darker-gray: #313131; - --black: #000; + --black: #161616; --light-blue: #19d8ff; --strong-translucid-light-blue: #19d8ff66; --weak-translucid-light-blue: #19d8ff33; - --blue: blue; - --purple: purple; + --gray-blue: #005cb3; + --strong-translucid-gray-blue: #005cb3aa; + --weak-translucid-gray-blue: #005cb355; + --blue: #0000ff; + --violet: #c58bff; + --purple: #8c00c4; } -/* Color scheme */ +/* Light color scheme */ :root { --bg-color: var(--white); @@ -39,4 +43,32 @@ --line-num-color: var(--dark-gray); --secondary-code-bg-color: var(--lighter-gray); +} + +/* Dark color scheme */ + +@media (prefers-color-scheme: dark) { + :root { + --bg-color: var(--darker-gray); + --text-color: var(--white); + --header-color: var(--black); + --border-color: var(--lighter-gray); + --icon-color: var(--white); + --disabled-icon-color: var(--gray); + + --button-bg-color: var(--darker-gray); + --button-hover-bg-color: var(--dark-gray); + --button-disabled-bg-color: var(--dark-gray); + + --tab-bg-color: var(--darker-gray); + --tab-hover-bg-color: var(--dark-gray); + --tab-active-bg-color: var(--dark-gray); + + --highlight-color: var(--gray-blue); + --secondary-highlight-color: var(--strong-translucid-gray-blue); + --tertiary-highlight-color: var(--weak-translucid-gray-blue); + + --line-num-color: var(--gray); + --secondary-code-bg-color: var(--dark-gray); + } } \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 73b900bc7..5ef819d5a 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -21,9 +21,10 @@ body { button { height: 2.5em; padding: 0 1.25em; - background-color: var(--button-bg-color); border: 1px solid var(--border-color); border-radius: 4px; + background-color: var(--button-bg-color); + color: var(--text-color); font-size: 1em; font-weight: 500; diff --git a/Lara-JS/src-api/visualization/public/css/syntax-highlighting.css b/Lara-JS/src-api/visualization/public/css/syntax-highlighting.css index f96efd75f..d52cf1bdd 100644 --- a/Lara-JS/src-api/visualization/public/css/syntax-highlighting.css +++ b/Lara-JS/src-api/visualization/public/css/syntax-highlighting.css @@ -8,10 +8,20 @@ --literal: var(--blue); } +@media (prefers-color-scheme: dark) { + :root { + --comment: var(--gray); + --keyword: var(--violet); + --type: var(--violet); + --string: var(--light-blue); + --literal: var(--light-blue); + } +} + /* Syntax highlighting classes */ .comment { color: var(--comment); } .keyword { color: var(--keyword); } .type { color: var(--type); } .string { color: var(--string); } -.literal { color: var(--literal); } \ No newline at end of file +.literal { color: var(--literal); } From 7b13c2ba8bc0d676fecc3daa6d22482d12842136 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 14:59:24 +0100 Subject: [PATCH 108/136] Display when node does not have code --- .../visualization/public/css/styles.css | 8 ++------ .../visualization/public/js/ToolJoinPoint.ts | 6 +++--- .../visualization/public/js/visualization.ts | 20 ++++++++++++------- .../visualization/public/js/visualization.js | 20 ++++++++++++------- 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 5ef819d5a..d9baf3755 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -191,9 +191,9 @@ main { #node-info-container { width: fit-content; - max-width: 50vw; + max-width: 40vw; height: fit-content; - max-height: 50vh; + max-height: 40vh; padding-inline: 0.5em; font-weight: 400; @@ -216,10 +216,6 @@ main { font-weight: bold; } -#node-info-container p > span:first-of-type::after, #node-info-container .alert::after { - content: ': '; -} - #node-info-container pre { border-radius: 4px; background-color: var(--secondary-code-bg-color); diff --git a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts index bf2423357..0c184c25c 100644 --- a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts +++ b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts @@ -3,12 +3,12 @@ export type JoinPointInfo = { [attribute: string]: string }; export default class ToolJoinPoint { #id: string; #type: string; - #code: string; + #code: string | undefined; #filename: string | undefined; #info: JoinPointInfo; #children: ToolJoinPoint[]; - constructor(id: string, type: string, code: string, filename: string | undefined, info: JoinPointInfo, children: ToolJoinPoint[]) { + constructor(id: string, type: string, code: string | undefined, filename: string | undefined, info: JoinPointInfo, children: ToolJoinPoint[]) { this.#id = id; this.#type = type; this.#filename = filename; @@ -25,7 +25,7 @@ export default class ToolJoinPoint { return this.#type; } - get code(): string { + get code(): string | undefined { return this.#code; } diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index 3cfdc99e5..1ee3bd703 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -50,7 +50,7 @@ const showNodeInfo = (node: JoinPoint): void => { for (const [name, value] of Object.entries(node.info)) { const attributeName = document.createElement('span'); - attributeName.textContent = name; + attributeName.textContent = name + ': '; const attributeValue = document.createElement('span'); attributeValue.textContent = value; @@ -63,14 +63,20 @@ const showNodeInfo = (node: JoinPoint): void => { if (!document.querySelector(`.node-code[data-node-id="${node.id}"]`)) { const codeAlert = document.createElement('p'); codeAlert.classList.add('alert'); - codeAlert.textContent = 'Node code not found'; - const codeWrapper = document.createElement('pre'); - const code = document.createElement('code'); - code.textContent = node.code; - codeWrapper.appendChild(code) + if (node.code !== undefined) { + codeAlert.textContent = 'Node code not found:'; - nodeInfoContainer.append(codeAlert, codeWrapper); + const codeWrapper = document.createElement('pre'); + const code = document.createElement('code'); + code.textContent = node.code; + codeWrapper.appendChild(code) + + nodeInfoContainer.append(codeAlert, codeWrapper); + } else { + codeAlert.textContent = 'Node does not have code!' + nodeInfoContainer.appendChild(codeAlert); + } } } diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index d2ca39b93..2e46728b8 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -37,7 +37,7 @@ const showNodeInfo = (node) => { nodeInfoContainer.innerHTML = ''; for (const [name, value] of Object.entries(node.info)) { const attributeName = document.createElement('span'); - attributeName.textContent = name; + attributeName.textContent = name + ': '; const attributeValue = document.createElement('span'); attributeValue.textContent = value; const line = document.createElement('p'); @@ -47,12 +47,18 @@ const showNodeInfo = (node) => { if (!document.querySelector(`.node-code[data-node-id="${node.id}"]`)) { const codeAlert = document.createElement('p'); codeAlert.classList.add('alert'); - codeAlert.textContent = 'Node code not found'; - const codeWrapper = document.createElement('pre'); - const code = document.createElement('code'); - code.textContent = node.code; - codeWrapper.appendChild(code); - nodeInfoContainer.append(codeAlert, codeWrapper); + if (node.code !== undefined) { + codeAlert.textContent = 'Node code not found:'; + const codeWrapper = document.createElement('pre'); + const code = document.createElement('code'); + code.textContent = node.code; + codeWrapper.appendChild(code); + nodeInfoContainer.append(codeAlert, codeWrapper); + } + else { + codeAlert.textContent = 'Node does not have code!'; + nodeInfoContainer.appendChild(codeAlert); + } } }; const hideNodeInfo = () => { From e5b3b68d6206c8acf24687eb58302b94c7c2fdcb Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 15:05:59 +0100 Subject: [PATCH 109/136] Switch from filename to filepath --- .../visualization/public/js/ToolJoinPoint.ts | 16 +++++++------- .../visualization/public/js/components.ts | 6 ++--- .../src-api/visualization/public/js/files.ts | 22 +++++++++---------- .../visualization/public/js/visualization.ts | 4 ++-- .../visualization/public/js/ToolJoinPoint.js | 16 +++++++------- .../visualization/public/js/components.js | 6 ++--- .../src-lara/visualization/public/js/files.js | 22 +++++++++---------- .../visualization/public/js/visualization.js | 4 ++-- 8 files changed, 48 insertions(+), 48 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts index 0c184c25c..f2c62e654 100644 --- a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts +++ b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts @@ -4,14 +4,14 @@ export default class ToolJoinPoint { #id: string; #type: string; #code: string | undefined; - #filename: string | undefined; + #filepath: string | undefined; #info: JoinPointInfo; #children: ToolJoinPoint[]; - constructor(id: string, type: string, code: string | undefined, filename: string | undefined, info: JoinPointInfo, children: ToolJoinPoint[]) { + constructor(id: string, type: string, code: string | undefined, filepath: string | undefined, info: JoinPointInfo, children: ToolJoinPoint[]) { this.#id = id; this.#type = type; - this.#filename = filename; + this.#filepath = filepath; this.#code = code; this.#info = info; this.#children = children; @@ -29,8 +29,8 @@ export default class ToolJoinPoint { return this.#code; } - get filename(): string | undefined { - return this.#filename; + get filepath(): string | undefined { + return this.#filepath; } get info(): JoinPointInfo { @@ -46,7 +46,7 @@ export default class ToolJoinPoint { json.id, json.type, json.code, - json.filename, + json.filepath, json.info, json.children.map((child: any) => ToolJoinPoint.fromJson(child)) ); @@ -57,7 +57,7 @@ export default class ToolJoinPoint { id: this.#id, type: this.#type, code: this.#code, - filename: this.#filename, + filepath: this.#filepath, info: this.#info, children: this.#children.map((child) => child.toJson()), }; @@ -68,7 +68,7 @@ export default class ToolJoinPoint { this.#id, this.#type, this.#code, - this.#filename, + this.#filepath, this.#info, this.#children.map((child) => child.clone()) ); diff --git a/Lara-JS/src-api/visualization/public/js/components.ts b/Lara-JS/src-api/visualization/public/js/components.ts index 43d809129..0a2c7d770 100644 --- a/Lara-JS/src-api/visualization/public/js/components.ts +++ b/Lara-JS/src-api/visualization/public/js/components.ts @@ -38,11 +38,11 @@ const getFileTabs = (() => { return (): HTMLDivElement => fileTabs; })(); -const createFileTab = (filename: string) => { +const createFileTab = (filepath: string) => { const fileTab = document.createElement('div'); fileTab.classList.add('file-tab'); - fileTab.dataset.filename = filename; - fileTab.textContent = filename ?? ''; + fileTab.dataset.filepath = filepath; + fileTab.textContent = filepath ?? ''; return fileTab; }; diff --git a/Lara-JS/src-api/visualization/public/js/files.ts b/Lara-JS/src-api/visualization/public/js/files.ts index c0b608817..c6482ce59 100644 --- a/Lara-JS/src-api/visualization/public/js/files.ts +++ b/Lara-JS/src-api/visualization/public/js/files.ts @@ -1,13 +1,13 @@ import { addCode, updateLines } from "./ast-import.js"; import { createFileTab, getCodeContainer, getFileTabs } from "./components.js"; -let selectedFilename: string | null = null; +let selectedFilepath: string | null = null; -const addFile = (name: string, code: string): void => { - addCode(code, name); +const addFile = (path: string, code: string): void => { + addCode(code, path); - const fileTab = createFileTab(name); - fileTab.addEventListener('click', () => selectFile(name)); + const fileTab = createFileTab(path); + fileTab.addEventListener('click', () => selectFile(path)); const fileTabs = getFileTabs(); fileTabs.appendChild(fileTab); @@ -20,28 +20,28 @@ const clearFiles = (): void => { const codeContainer = getCodeContainer(); codeContainer.querySelector('pre')!.innerHTML = ''; - selectedFilename = null; + selectedFilepath = null; } -const selectFile = (filename: string): void => { +const selectFile = (filepath: string): void => { const fileTabs = getFileTabs(); - if (filename !== selectedFilename) { + if (filepath !== selectedFilepath) { const codeContainer = getCodeContainer(); - const selectedTab = fileTabs.querySelector(`.file-tab[data-filename="${filename}"]`)!; + const selectedTab = fileTabs.querySelector(`.file-tab[data-filepath="${filepath}"]`)!; fileTabs.querySelector('.file-tab.active')?.classList.remove('active'); selectedTab.classList.add('active'); - const fileCode = codeContainer.querySelector(`code[data-filename="${filename}"]`)!; + const fileCode = codeContainer.querySelector(`code[data-filepath="${filepath}"]`)!; const activeCode = codeContainer.querySelector('code.active'); if (activeCode) activeCode.classList.remove('active'); fileCode.classList.add('active'); updateLines(); - selectedFilename = filename; + selectedFilepath = filepath; } } diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index 1ee3bd703..35ed77c1b 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -131,8 +131,8 @@ const addHighlighingEvents = (node: JoinPoint, astContainer: HTMLElement, codeCo selectedNodeId = node.id; highlightNode(node.id, true); - if (node.filename) - selectFile(node.filename); + if (node.filepath) + selectFile(node.filepath); const nodeElement = getNodeElement(node.id)!; scrollIntoViewIfNeeded(nodeElement, astContainer); diff --git a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js index d3db75539..1a86cda3e 100644 --- a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js +++ b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js @@ -2,13 +2,13 @@ export default class ToolJoinPoint { #id; #type; #code; - #filename; + #filepath; #info; #children; - constructor(id, type, code, filename, info, children) { + constructor(id, type, code, filepath, info, children) { this.#id = id; this.#type = type; - this.#filename = filename; + this.#filepath = filepath; this.#code = code; this.#info = info; this.#children = children; @@ -22,8 +22,8 @@ export default class ToolJoinPoint { get code() { return this.#code; } - get filename() { - return this.#filename; + get filepath() { + return this.#filepath; } get info() { return this.#info; @@ -32,20 +32,20 @@ export default class ToolJoinPoint { return this.#children; } static fromJson(json) { - return new ToolJoinPoint(json.id, json.type, json.code, json.filename, json.info, json.children.map((child) => ToolJoinPoint.fromJson(child))); + return new ToolJoinPoint(json.id, json.type, json.code, json.filepath, json.info, json.children.map((child) => ToolJoinPoint.fromJson(child))); } toJson() { return { id: this.#id, type: this.#type, code: this.#code, - filename: this.#filename, + filepath: this.#filepath, info: this.#info, children: this.#children.map((child) => child.toJson()), }; } clone() { - return new ToolJoinPoint(this.#id, this.#type, this.#code, this.#filename, this.#info, this.#children.map((child) => child.clone())); + return new ToolJoinPoint(this.#id, this.#type, this.#code, this.#filepath, this.#info, this.#children.map((child) => child.clone())); } } ; diff --git a/LaraApi/src-lara/visualization/public/js/components.js b/LaraApi/src-lara/visualization/public/js/components.js index 04d628762..359fa2c3b 100644 --- a/LaraApi/src-lara/visualization/public/js/components.js +++ b/LaraApi/src-lara/visualization/public/js/components.js @@ -33,11 +33,11 @@ const getFileTabs = (() => { } return () => fileTabs; })(); -const createFileTab = (filename) => { +const createFileTab = (filepath) => { const fileTab = document.createElement('div'); fileTab.classList.add('file-tab'); - fileTab.dataset.filename = filename; - fileTab.textContent = filename ?? ''; + fileTab.dataset.filepath = filepath; + fileTab.textContent = filepath ?? ''; return fileTab; }; export { getAstContainer, getCodeContainer, getContinueButton, getResizer, getFileTabs, createFileTab, }; diff --git a/LaraApi/src-lara/visualization/public/js/files.js b/LaraApi/src-lara/visualization/public/js/files.js index 9e59a92ff..223c575bd 100644 --- a/LaraApi/src-lara/visualization/public/js/files.js +++ b/LaraApi/src-lara/visualization/public/js/files.js @@ -1,10 +1,10 @@ import { addCode, updateLines } from "./ast-import.js"; import { createFileTab, getCodeContainer, getFileTabs } from "./components.js"; -let selectedFilename = null; -const addFile = (name, code) => { - addCode(code, name); - const fileTab = createFileTab(name); - fileTab.addEventListener('click', () => selectFile(name)); +let selectedFilepath = null; +const addFile = (path, code) => { + addCode(code, path); + const fileTab = createFileTab(path); + fileTab.addEventListener('click', () => selectFile(path)); const fileTabs = getFileTabs(); fileTabs.appendChild(fileTab); }; @@ -13,22 +13,22 @@ const clearFiles = () => { fileTabs.innerHTML = ''; const codeContainer = getCodeContainer(); codeContainer.querySelector('pre').innerHTML = ''; - selectedFilename = null; + selectedFilepath = null; }; -const selectFile = (filename) => { +const selectFile = (filepath) => { const fileTabs = getFileTabs(); - if (filename !== selectedFilename) { + if (filepath !== selectedFilepath) { const codeContainer = getCodeContainer(); - const selectedTab = fileTabs.querySelector(`.file-tab[data-filename="${filename}"]`); + const selectedTab = fileTabs.querySelector(`.file-tab[data-filepath="${filepath}"]`); fileTabs.querySelector('.file-tab.active')?.classList.remove('active'); selectedTab.classList.add('active'); - const fileCode = codeContainer.querySelector(`code[data-filename="${filename}"]`); + const fileCode = codeContainer.querySelector(`code[data-filename="${filepath}"]`); const activeCode = codeContainer.querySelector('code.active'); if (activeCode) activeCode.classList.remove('active'); fileCode.classList.add('active'); updateLines(); - selectedFilename = filename; + selectedFilepath = filepath; } }; export { addFile, clearFiles, selectFile }; diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index 2e46728b8..2bf49f1f7 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -105,8 +105,8 @@ const addHighlighingEvents = (node, astContainer, codeContainer) => { } selectedNodeId = node.id; highlightNode(node.id, true); - if (node.filename) - selectFile(node.filename); + if (node.filepath) + selectFile(node.filepath); const nodeElement = getNodeElement(node.id); scrollIntoViewIfNeeded(nodeElement, astContainer); const firstNodeCodeBlock = document.querySelector(`.node-code[data-node-id="${node.id}"]`); From 14023d6e830df111c8b017ae635f51432f1669a9 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 16:12:10 +0100 Subject: [PATCH 110/136] Make file tab use full file path in title --- Lara-JS/src-api/visualization/public/css/styles.css | 7 +++---- Lara-JS/src-api/visualization/public/js/ast-import.ts | 4 ++-- Lara-JS/src-api/visualization/public/js/components.ts | 4 +++- Lara-JS/src-api/visualization/public/js/files.ts | 3 +++ LaraApi/src-lara/visualization/public/js/ast-import.js | 4 ++-- LaraApi/src-lara/visualization/public/js/components.js | 3 ++- LaraApi/src-lara/visualization/public/js/files.js | 4 +++- 7 files changed, 18 insertions(+), 11 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index d9baf3755..6196d9061 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -269,16 +269,15 @@ main { } .file-tab { + box-sizing: border-box; height: 2.25em; - padding-inline: 1em; + padding: 0.625em 1em; border-radius: 2px; background-color: var(--tab-bg-color); font-weight: 500; - display: flex; - align-items: center; - justify-content: center; + display: inline-block; user-select: none; -webkit-user-select: none; diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index a713d6d1c..8b4041392 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -24,9 +24,9 @@ const initCodeContainer = (codeContainer: HTMLElement): void => { codeContainer.append(codePre, codeLines) } -const addCode = (code: string, filename: string): void => { +const addCode = (code: string, filepath: string): void => { const codeElement = document.createElement('code'); - codeElement.dataset.filename = filename; + codeElement.dataset.filepath = filepath; codeElement.innerHTML = code; const codeContainer = getCodeContainer(); diff --git a/Lara-JS/src-api/visualization/public/js/components.ts b/Lara-JS/src-api/visualization/public/js/components.ts index 0a2c7d770..1a82c56ec 100644 --- a/Lara-JS/src-api/visualization/public/js/components.ts +++ b/Lara-JS/src-api/visualization/public/js/components.ts @@ -42,7 +42,9 @@ const createFileTab = (filepath: string) => { const fileTab = document.createElement('div'); fileTab.classList.add('file-tab'); fileTab.dataset.filepath = filepath; - fileTab.textContent = filepath ?? ''; + + fileTab.title = filepath; + fileTab.textContent = filepath !== '' ? filepath.slice(filepath.lastIndexOf('/') + 1) : ''; return fileTab; }; diff --git a/Lara-JS/src-api/visualization/public/js/files.ts b/Lara-JS/src-api/visualization/public/js/files.ts index c6482ce59..d9bbd94b4 100644 --- a/Lara-JS/src-api/visualization/public/js/files.ts +++ b/Lara-JS/src-api/visualization/public/js/files.ts @@ -31,6 +31,9 @@ const selectFile = (filepath: string): void => { const codeContainer = getCodeContainer(); const selectedTab = fileTabs.querySelector(`.file-tab[data-filepath="${filepath}"]`)!; + if (selectedTab === null) + throw Error(`File "${filepath}" not found`); + fileTabs.querySelector('.file-tab.active')?.classList.remove('active'); selectedTab.classList.add('active'); diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index 0143301a6..ec332c005 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -16,9 +16,9 @@ const initCodeContainer = (codeContainer) => { codeLines.classList.add('code-wrapper'); codeContainer.append(codePre, codeLines); }; -const addCode = (code, filename) => { +const addCode = (code, filepath) => { const codeElement = document.createElement('code'); - codeElement.dataset.filename = filename; + codeElement.dataset.filepath = filepath; codeElement.innerHTML = code; const codeContainer = getCodeContainer(); const codePre = codeContainer.querySelector('pre.code-wrapper'); diff --git a/LaraApi/src-lara/visualization/public/js/components.js b/LaraApi/src-lara/visualization/public/js/components.js index 359fa2c3b..c07cf8e1d 100644 --- a/LaraApi/src-lara/visualization/public/js/components.js +++ b/LaraApi/src-lara/visualization/public/js/components.js @@ -37,7 +37,8 @@ const createFileTab = (filepath) => { const fileTab = document.createElement('div'); fileTab.classList.add('file-tab'); fileTab.dataset.filepath = filepath; - fileTab.textContent = filepath ?? ''; + fileTab.title = filepath; + fileTab.textContent = filepath !== '' ? filepath.slice(filepath.lastIndexOf('/') + 1) : ''; return fileTab; }; export { getAstContainer, getCodeContainer, getContinueButton, getResizer, getFileTabs, createFileTab, }; diff --git a/LaraApi/src-lara/visualization/public/js/files.js b/LaraApi/src-lara/visualization/public/js/files.js index 223c575bd..cd654b9fa 100644 --- a/LaraApi/src-lara/visualization/public/js/files.js +++ b/LaraApi/src-lara/visualization/public/js/files.js @@ -20,9 +20,11 @@ const selectFile = (filepath) => { if (filepath !== selectedFilepath) { const codeContainer = getCodeContainer(); const selectedTab = fileTabs.querySelector(`.file-tab[data-filepath="${filepath}"]`); + if (selectedTab === null) + throw Error(`File "${filepath}" not found`); fileTabs.querySelector('.file-tab.active')?.classList.remove('active'); selectedTab.classList.add('active'); - const fileCode = codeContainer.querySelector(`code[data-filename="${filepath}"]`); + const fileCode = codeContainer.querySelector(`code[data-filepath="${filepath}"]`); const activeCode = codeContainer.querySelector('code.active'); if (activeCode) activeCode.classList.remove('active'); From b23db67927f1758b4865af0b23dbdb8d492c3028 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 16:37:13 +0100 Subject: [PATCH 111/136] Fix accessibility problems --- .../src-api/visualization/public/css/color-scheme.css | 10 +++++----- Lara-JS/src-api/visualization/public/css/styles.css | 1 + Lara-JS/src-api/visualization/public/js/components.ts | 4 ++-- LaraApi/src-lara/visualization/public/js/components.js | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/color-scheme.css b/Lara-JS/src-api/visualization/public/css/color-scheme.css index 4a0b473e0..50fe49391 100644 --- a/Lara-JS/src-api/visualization/public/css/color-scheme.css +++ b/Lara-JS/src-api/visualization/public/css/color-scheme.css @@ -11,12 +11,12 @@ --light-blue: #19d8ff; --strong-translucid-light-blue: #19d8ff66; --weak-translucid-light-blue: #19d8ff33; - --gray-blue: #005cb3; - --strong-translucid-gray-blue: #005cb3aa; - --weak-translucid-gray-blue: #005cb355; + --gray-blue: #004483; + --strong-translucid-gray-blue: #004483bf; + --weak-translucid-gray-blue: #0044837f; --blue: #0000ff; - --violet: #c58bff; - --purple: #8c00c4; + --violet: #dbb8fe; + --purple: #7d00ae; } /* Light color scheme */ diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 6196d9061..9926f429e 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -272,6 +272,7 @@ main { box-sizing: border-box; height: 2.25em; padding: 0.625em 1em; + border: none; border-radius: 2px; background-color: var(--tab-bg-color); diff --git a/Lara-JS/src-api/visualization/public/js/components.ts b/Lara-JS/src-api/visualization/public/js/components.ts index 1a82c56ec..691903115 100644 --- a/Lara-JS/src-api/visualization/public/js/components.ts +++ b/Lara-JS/src-api/visualization/public/js/components.ts @@ -38,8 +38,8 @@ const getFileTabs = (() => { return (): HTMLDivElement => fileTabs; })(); -const createFileTab = (filepath: string) => { - const fileTab = document.createElement('div'); +const createFileTab = (filepath: string): HTMLButtonElement => { + const fileTab = document.createElement('button'); fileTab.classList.add('file-tab'); fileTab.dataset.filepath = filepath; diff --git a/LaraApi/src-lara/visualization/public/js/components.js b/LaraApi/src-lara/visualization/public/js/components.js index c07cf8e1d..61d7c68db 100644 --- a/LaraApi/src-lara/visualization/public/js/components.js +++ b/LaraApi/src-lara/visualization/public/js/components.js @@ -34,7 +34,7 @@ const getFileTabs = (() => { return () => fileTabs; })(); const createFileTab = (filepath) => { - const fileTab = document.createElement('div'); + const fileTab = document.createElement('button'); fileTab.classList.add('file-tab'); fileTab.dataset.filepath = filepath; fileTab.title = filepath; From 565b7615a643b7e7ab37f9cccac66d84ff7e7dfc Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 17:24:08 +0100 Subject: [PATCH 112/136] Refactor dark theme --- .../visualization/public/css/color-scheme.css | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/color-scheme.css b/Lara-JS/src-api/visualization/public/css/color-scheme.css index 50fe49391..9601655a8 100644 --- a/Lara-JS/src-api/visualization/public/css/color-scheme.css +++ b/Lara-JS/src-api/visualization/public/css/color-scheme.css @@ -49,19 +49,19 @@ @media (prefers-color-scheme: dark) { :root { - --bg-color: var(--darker-gray); + --bg-color: var(--black); --text-color: var(--white); - --header-color: var(--black); + --header-color: var(--darker-gray); --border-color: var(--lighter-gray); --icon-color: var(--white); --disabled-icon-color: var(--gray); - --button-bg-color: var(--darker-gray); - --button-hover-bg-color: var(--dark-gray); + --button-bg-color: var(--black); + --button-hover-bg-color: var(--darker-gray); --button-disabled-bg-color: var(--dark-gray); - --tab-bg-color: var(--darker-gray); - --tab-hover-bg-color: var(--dark-gray); + --tab-bg-color: var(--black); + --tab-hover-bg-color: var(--darker-gray); --tab-active-bg-color: var(--dark-gray); --highlight-color: var(--gray-blue); @@ -69,6 +69,6 @@ --tertiary-highlight-color: var(--weak-translucid-gray-blue); --line-num-color: var(--gray); - --secondary-code-bg-color: var(--dark-gray); + --secondary-code-bg-color: var(--darker-gray); } } \ No newline at end of file From c8906077463935cebbc904c050bda2e7b5466026 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 17:24:51 +0100 Subject: [PATCH 113/136] Refactor page scripts to use the main components functions --- .../visualization/public/js/ast-import.ts | 12 ++--- .../visualization/public/js/communication.ts | 44 +++++++++++-------- .../src-api/visualization/public/js/main.ts | 22 +++------- .../visualization/public/js/visualization.ts | 24 +++++++--- .../visualization/public/js/ast-import.js | 10 ++--- .../visualization/public/js/communication.js | 35 +++++++++------ .../src-lara/visualization/public/js/main.js | 18 +++----- .../visualization/public/js/visualization.js | 20 ++++++--- 8 files changed, 99 insertions(+), 86 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index 8b4041392..7ef58facb 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -1,7 +1,6 @@ import { countChar, createIcon, } from './utils.js'; -import { addEventListenersToAstNodes } from './visualization.js'; import JoinPoint from './ToolJoinPoint.js'; -import { getCodeContainer } from './components.js'; +import { getAstContainer, getCodeContainer } from './components.js'; const updateLines = (): void => { const codeContainer = getCodeContainer(); @@ -12,7 +11,8 @@ const updateLines = (): void => { codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); }; -const initCodeContainer = (codeContainer: HTMLElement): void => { +const initCodeContainer = (): void => { + const codeContainer = getCodeContainer(); codeContainer.innerHTML = ''; const codePre = document.createElement('pre'); @@ -107,12 +107,12 @@ const convertAstNodeToHtml = (root: JoinPoint): DocumentFragment => { return fragment; }; -const importAst = (astRoot: JoinPoint, astContainer: HTMLElement, codeContainer: HTMLElement): void => { +const importAst = (astRoot: JoinPoint): void => { + const astContainer = getAstContainer(); + const astFragment = convertAstNodeToHtml(astRoot); astContainer.innerHTML = ''; astContainer.appendChild(astFragment); - - addEventListenersToAstNodes(astRoot, astContainer, codeContainer); } export { importAst, initCodeContainer, addCode, updateLines }; diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts index 10bef34bc..3340caf04 100644 --- a/Lara-JS/src-api/visualization/public/js/communication.ts +++ b/Lara-JS/src-api/visualization/public/js/communication.ts @@ -1,22 +1,10 @@ import { importAst, initCodeContainer } from "./ast-import.js"; +import { getContinueButton } from "./components.js"; import { addFile, clearFiles, selectFile } from "./files.js"; +import { addEventListenersToAstNodes } from "./visualization.js"; -const getWebSocket = (): WebSocket => { - const url = `ẁs://${window.location.host}`; - return new WebSocket(url); -}; - -const sendData = (ws: WebSocket, data: any): void => { - ws.send(JSON.stringify(data)); -}; - -const parseMessage = (message: MessageEvent): any => { - return JSON.parse(message.data); -}; - -const webSocketOnMessage = (message: MessageEvent, continueButton: HTMLButtonElement, - astContainer: HTMLElement, codeContainer: HTMLElement, fileTabs: HTMLElement): void => { - +const webSocketOnMessage = (message: MessageEvent): void => { + const continueButton = getContinueButton(); const data = parseMessage(message); switch (data.message) { @@ -26,13 +14,15 @@ const webSocketOnMessage = (message: MessageEvent, continueButton: HTMLButtonEle continueButton.disabled = true; - initCodeContainer(codeContainer); + initCodeContainer(); clearFiles(); for (const [filename, filecode] of Object.entries(code)) addFile(filename, filecode as string); - importAst(ast, astContainer, codeContainer); + importAst(ast); + addEventListenersToAstNodes(ast); + selectFile(Object.keys(code)[0]); continueButton.disabled = buttonDisabled; @@ -49,7 +39,23 @@ const webSocketOnMessage = (message: MessageEvent, continueButton: HTMLButtonEle } }; -const continueButtonOnClick = (continueButton: HTMLButtonElement, ws: WebSocket): void => { +const getWebSocket = (): WebSocket => { + const url = '/'; + const ws = new WebSocket(url); + ws.addEventListener('message', webSocketOnMessage); + return ws; +}; + +const sendData = (ws: WebSocket, data: any): void => { + ws.send(JSON.stringify(data)); +}; + +const parseMessage = (message: MessageEvent): any => { + return JSON.parse(message.data); +}; + +const continueButtonOnClick = (ws: WebSocket): void => { + const continueButton = getContinueButton(); continueButton.disabled = true; sendData(ws, { message: 'continue' }); }; diff --git a/Lara-JS/src-api/visualization/public/js/main.ts b/Lara-JS/src-api/visualization/public/js/main.ts index 5dc705af8..b70ef3aed 100644 --- a/Lara-JS/src-api/visualization/public/js/main.ts +++ b/Lara-JS/src-api/visualization/public/js/main.ts @@ -1,27 +1,17 @@ -import { continueButtonOnClick, getWebSocket, webSocketOnMessage } from "./communication.js"; +import { continueButtonOnClick, getWebSocket } from "./communication.js"; +import { getContinueButton } from "./components.js"; import { addDividerEventListeners } from "./visualization.js"; (() => { - const astContainer = document.querySelector('#ast-container'); - const codeContainer = document.querySelector('#code-container'); - const continueButton = document.querySelector('#continue-button'); - const resizer = document.querySelector('#resizer'); - const fileTabs = document.querySelector('#file-tabs') - - if (!astContainer || !codeContainer || !continueButton || !resizer || !fileTabs) { - console.error('Required elements not found'); - return; - } - - addDividerEventListeners(resizer, astContainer, codeContainer, continueButton); - let ws: WebSocket; const setupWebSocket = () => { ws = getWebSocket(); - ws.addEventListener('message', event => webSocketOnMessage(event, continueButton, astContainer, codeContainer, fileTabs)); ws.addEventListener('close', () => setupWebSocket()); }; setupWebSocket(); - continueButton.addEventListener('click', () => continueButtonOnClick(continueButton, ws)); + const continueButton = getContinueButton(); + continueButton.addEventListener('click', () => continueButtonOnClick(ws)); + + addDividerEventListeners(); })(); diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index 35ed77c1b..f52e293a2 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -1,5 +1,6 @@ +import { getAstContainer, getCodeContainer, getContinueButton, getResizer } from "./components.js"; import { selectFile } from "./files.js"; -import JoinPoint, { JoinPointInfo } from "./ToolJoinPoint.js"; +import JoinPoint from "./ToolJoinPoint.js"; const getNodeElement = (nodeId: string): HTMLElement | null => { return document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); @@ -100,7 +101,7 @@ const scrollIntoViewIfNeeded = (element: HTMLElement, parent: HTMLElement): void let selectedNodeId: string | null = null; -const addHighlighingEvents = (node: JoinPoint, astContainer: HTMLElement, codeContainer: HTMLElement): void => { +const addHighlighingEvents = (node: JoinPoint): void => { const nodeRelatedElements = getNodeRelatedElements(node.id); for (const nodeRelatedElement of nodeRelatedElements) { nodeRelatedElement.addEventListener('mouseover', event => { @@ -134,11 +135,15 @@ const addHighlighingEvents = (node: JoinPoint, astContainer: HTMLElement, codeCo if (node.filepath) selectFile(node.filepath); + const nodeElement = getNodeElement(node.id)!; + const astContainer = getAstContainer(); scrollIntoViewIfNeeded(nodeElement, astContainer); const firstNodeCodeBlock = document.querySelector(`.node-code[data-node-id="${node.id}"]`); - if (firstNodeCodeBlock) + if (firstNodeCodeBlock) { + const codeContainer = getCodeContainer(); scrollIntoViewIfNeeded(firstNodeCodeBlock!, codeContainer); + } showNodeInfo(node); }); @@ -153,14 +158,19 @@ const addHighlighingEvents = (node: JoinPoint, astContainer: HTMLElement, codeCo } }; -const addEventListenersToAstNodes = (root: JoinPoint, astContainer: HTMLElement, codeContainer: HTMLElement): void => { +const addEventListenersToAstNodes = (root: JoinPoint): void => { selectedNodeId = null; // To prevent invalid references - addHighlighingEvents(root, astContainer, codeContainer); - root.children.forEach(child => addEventListenersToAstNodes(child, astContainer, codeContainer)); + addHighlighingEvents(root); + root.children.forEach(child => addEventListenersToAstNodes(child)); }; -const addDividerEventListeners = (resizer: HTMLElement, astContainer: HTMLElement, codeContainer: HTMLElement, continueButton: HTMLElement): void => { +const addDividerEventListeners = (): void => { + const resizer = getResizer(); + const astContainer = getAstContainer(); + const codeContainer = getCodeContainer(); + const continueButton = getContinueButton(); + let drag = false; let width = astContainer.offsetWidth; diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index ec332c005..b380ffd1b 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -1,6 +1,5 @@ import { countChar, createIcon, } from './utils.js'; -import { addEventListenersToAstNodes } from './visualization.js'; -import { getCodeContainer } from './components.js'; +import { getAstContainer, getCodeContainer } from './components.js'; const updateLines = () => { const codeContainer = getCodeContainer(); const codeLines = codeContainer.querySelector('.lines'); @@ -8,7 +7,8 @@ const updateLines = () => { const numLines = countChar(code, '\n') + 1; codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); }; -const initCodeContainer = (codeContainer) => { +const initCodeContainer = () => { + const codeContainer = getCodeContainer(); codeContainer.innerHTML = ''; const codePre = document.createElement('pre'); codePre.classList.add('lines'); @@ -78,11 +78,11 @@ const convertAstNodeToHtml = (root) => { } return fragment; }; -const importAst = (astRoot, astContainer, codeContainer) => { +const importAst = (astRoot) => { + const astContainer = getAstContainer(); const astFragment = convertAstNodeToHtml(astRoot); astContainer.innerHTML = ''; astContainer.appendChild(astFragment); - addEventListenersToAstNodes(astRoot, astContainer, codeContainer); }; export { importAst, initCodeContainer, addCode, updateLines }; //# sourceMappingURL=ast-import.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js index c8282b3e5..8a59344ee 100644 --- a/LaraApi/src-lara/visualization/public/js/communication.js +++ b/LaraApi/src-lara/visualization/public/js/communication.js @@ -1,27 +1,21 @@ import { importAst, initCodeContainer } from "./ast-import.js"; +import { getContinueButton } from "./components.js"; import { addFile, clearFiles, selectFile } from "./files.js"; -const getWebSocket = () => { - const url = `ẁs://${window.location.host}`; - return new WebSocket(url); -}; -const sendData = (ws, data) => { - ws.send(JSON.stringify(data)); -}; -const parseMessage = (message) => { - return JSON.parse(message.data); -}; -const webSocketOnMessage = (message, continueButton, astContainer, codeContainer, fileTabs) => { +import { addEventListenersToAstNodes } from "./visualization.js"; +const webSocketOnMessage = (message) => { + const continueButton = getContinueButton(); const data = parseMessage(message); switch (data.message) { case 'update': const { code, ast } = data; const buttonDisabled = continueButton.disabled; continueButton.disabled = true; - initCodeContainer(codeContainer); + initCodeContainer(); clearFiles(); for (const [filename, filecode] of Object.entries(code)) addFile(filename, filecode); - importAst(ast, astContainer, codeContainer); + importAst(ast); + addEventListenersToAstNodes(ast); selectFile(Object.keys(code)[0]); continueButton.disabled = buttonDisabled; break; @@ -33,7 +27,20 @@ const webSocketOnMessage = (message, continueButton, astContainer, codeContainer break; } }; -const continueButtonOnClick = (continueButton, ws) => { +const getWebSocket = () => { + const url = '/'; + const ws = new WebSocket(url); + ws.addEventListener('message', webSocketOnMessage); + return ws; +}; +const sendData = (ws, data) => { + ws.send(JSON.stringify(data)); +}; +const parseMessage = (message) => { + return JSON.parse(message.data); +}; +const continueButtonOnClick = (ws) => { + const continueButton = getContinueButton(); continueButton.disabled = true; sendData(ws, { message: 'continue' }); }; diff --git a/LaraApi/src-lara/visualization/public/js/main.js b/LaraApi/src-lara/visualization/public/js/main.js index 376d18a75..4fc35ce5b 100644 --- a/LaraApi/src-lara/visualization/public/js/main.js +++ b/LaraApi/src-lara/visualization/public/js/main.js @@ -1,23 +1,15 @@ -import { continueButtonOnClick, getWebSocket, webSocketOnMessage } from "./communication.js"; +import { continueButtonOnClick, getWebSocket } from "./communication.js"; +import { getContinueButton } from "./components.js"; import { addDividerEventListeners } from "./visualization.js"; (() => { - const astContainer = document.querySelector('#ast-container'); - const codeContainer = document.querySelector('#code-container'); - const continueButton = document.querySelector('#continue-button'); - const resizer = document.querySelector('#resizer'); - const fileTabs = document.querySelector('#file-tabs'); - if (!astContainer || !codeContainer || !continueButton || !resizer || !fileTabs) { - console.error('Required elements not found'); - return; - } - addDividerEventListeners(resizer, astContainer, codeContainer, continueButton); let ws; const setupWebSocket = () => { ws = getWebSocket(); - ws.addEventListener('message', event => webSocketOnMessage(event, continueButton, astContainer, codeContainer, fileTabs)); ws.addEventListener('close', () => setupWebSocket()); }; setupWebSocket(); - continueButton.addEventListener('click', () => continueButtonOnClick(continueButton, ws)); + const continueButton = getContinueButton(); + continueButton.addEventListener('click', () => continueButtonOnClick(ws)); + addDividerEventListeners(); })(); //# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index 2bf49f1f7..b2704b6d3 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -1,3 +1,4 @@ +import { getAstContainer, getCodeContainer, getContinueButton, getResizer } from "./components.js"; import { selectFile } from "./files.js"; const getNodeElement = (nodeId) => { return document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); @@ -77,7 +78,7 @@ const scrollIntoViewIfNeeded = (element, parent) => { } }; let selectedNodeId = null; -const addHighlighingEvents = (node, astContainer, codeContainer) => { +const addHighlighingEvents = (node) => { const nodeRelatedElements = getNodeRelatedElements(node.id); for (const nodeRelatedElement of nodeRelatedElements) { nodeRelatedElement.addEventListener('mouseover', event => { @@ -108,10 +109,13 @@ const addHighlighingEvents = (node, astContainer, codeContainer) => { if (node.filepath) selectFile(node.filepath); const nodeElement = getNodeElement(node.id); + const astContainer = getAstContainer(); scrollIntoViewIfNeeded(nodeElement, astContainer); const firstNodeCodeBlock = document.querySelector(`.node-code[data-node-id="${node.id}"]`); - if (firstNodeCodeBlock) + if (firstNodeCodeBlock) { + const codeContainer = getCodeContainer(); scrollIntoViewIfNeeded(firstNodeCodeBlock, codeContainer); + } showNodeInfo(node); }); // For keyboard accessibility @@ -123,12 +127,16 @@ const addHighlighingEvents = (node, astContainer, codeContainer) => { }); } }; -const addEventListenersToAstNodes = (root, astContainer, codeContainer) => { +const addEventListenersToAstNodes = (root) => { selectedNodeId = null; // To prevent invalid references - addHighlighingEvents(root, astContainer, codeContainer); - root.children.forEach(child => addEventListenersToAstNodes(child, astContainer, codeContainer)); + addHighlighingEvents(root); + root.children.forEach(child => addEventListenersToAstNodes(child)); }; -const addDividerEventListeners = (resizer, astContainer, codeContainer, continueButton) => { +const addDividerEventListeners = () => { + const resizer = getResizer(); + const astContainer = getAstContainer(); + const codeContainer = getCodeContainer(); + const continueButton = getContinueButton(); let drag = false; let width = astContainer.offsetWidth; const rootStyle = document.documentElement.style; From 2617f87d3d8c27066650c82765245f4b8ad87cde Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 17:38:46 +0100 Subject: [PATCH 114/136] Pass AST rebuilding to Clava --- Lara-JS/src-api/visualization/GenericAstConverter.ts | 1 + .../src-api/visualization/GenericVisualizationTool.ts | 10 ++++------ .../visualization/GenericVisualizationTool.js | 11 ++++------- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Lara-JS/src-api/visualization/GenericAstConverter.ts b/Lara-JS/src-api/visualization/GenericAstConverter.ts index b5e76f47a..3495b30ca 100644 --- a/Lara-JS/src-api/visualization/GenericAstConverter.ts +++ b/Lara-JS/src-api/visualization/GenericAstConverter.ts @@ -6,6 +6,7 @@ export type FilesCode = { }; export default interface GenericAstConverter { + updateAst(): void; getToolAst(root: LaraJoinPoint): ToolJoinPoint; getPrettyHtmlCode(root: LaraJoinPoint): FilesCode; } \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/GenericVisualizationTool.ts b/Lara-JS/src-api/visualization/GenericVisualizationTool.ts index a99b3f063..8408c75df 100644 --- a/Lara-JS/src-api/visualization/GenericVisualizationTool.ts +++ b/Lara-JS/src-api/visualization/GenericVisualizationTool.ts @@ -143,15 +143,13 @@ export default abstract class GenericVisualizationTool { } private updateClient(ws: WebSocket): void { - wrapJoinPoint(JoinPoints.root()._javaObject.rebuild()); // TODO: Perform rebuild on AstConverter + const astConverter = this.getAstConverter(); + astConverter.updateAst(); this.sendToClient(ws, { message: 'update', - ast: this.getAstConverter() - .getToolAst(this.astRoot!) - .toJson(), - code: this.getAstConverter() - .getPrettyHtmlCode(this.astRoot!), + ast: astConverter.getToolAst(this.astRoot!).toJson(), + code: astConverter.getPrettyHtmlCode(this.astRoot!), }); } diff --git a/LaraApi/src-lara/visualization/GenericVisualizationTool.js b/LaraApi/src-lara/visualization/GenericVisualizationTool.js index 862fc0039..b965603c4 100644 --- a/LaraApi/src-lara/visualization/GenericVisualizationTool.js +++ b/LaraApi/src-lara/visualization/GenericVisualizationTool.js @@ -3,7 +3,6 @@ import http from 'http'; import path from 'path'; import { fileURLToPath } from 'url'; import { WebSocketServer } from 'ws'; -import { wrapJoinPoint } from '../LaraJoinPoint.js'; import JoinPoints from '../weaver/JoinPoints.js'; export default class GenericVisualizationTool { hostname; @@ -109,14 +108,12 @@ export default class GenericVisualizationTool { }); } updateClient(ws) { - wrapJoinPoint(JoinPoints.root()._javaObject.rebuild()); // TODO: Perform rebuild on AstConverter + const astConverter = this.getAstConverter(); + astConverter.updateAst(); this.sendToClient(ws, { message: 'update', - ast: this.getAstConverter() - .getToolAst(this.astRoot) - .toJson(), - code: this.getAstConverter() - .getPrettyHtmlCode(this.astRoot), + ast: astConverter.getToolAst(this.astRoot).toJson(), + code: astConverter.getPrettyHtmlCode(this.astRoot), }); } update(astRoot = JoinPoints.root()) { From 875f8f27ed6a0c5364133ee002105927d7f7674a Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 20:37:33 +0100 Subject: [PATCH 115/136] Refactor visualization.ts --- .../visualization/public/js/ast-import.ts | 4 +- .../visualization/public/js/communication.ts | 6 +- .../visualization/public/js/components.ts | 75 ++++++ .../visualization/public/js/visualization.ts | 220 +++++++++--------- .../visualization/public/js/ast-import.js | 4 +- .../visualization/public/js/communication.js | 4 +- .../visualization/public/js/components.js | 52 ++++- .../visualization/public/js/visualization.js | 182 +++++++-------- 8 files changed, 334 insertions(+), 213 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index 7ef58facb..49206078a 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -58,13 +58,13 @@ const createDropdownButtonOnClick = (dropdown: HTMLElement) => { }; const createAstNodeElement = (nodeId: string, text: string, dropdownButton: HTMLElement): HTMLSpanElement => { - const nodeElement = document.createElement('span'); + const nodeElement = document.createElement('span'); // TODO: Convert to div nodeElement.classList.add('ast-node'); nodeElement.dataset.nodeId = nodeId; const nodeText = document.createElement('span'); - nodeText.classList.add('ast-node-text'); + nodeText.classList.add('node-text'); nodeText.textContent = text; nodeElement.appendChild(dropdownButton); diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts index 3340caf04..585bbf0da 100644 --- a/Lara-JS/src-api/visualization/public/js/communication.ts +++ b/Lara-JS/src-api/visualization/public/js/communication.ts @@ -1,7 +1,7 @@ import { importAst, initCodeContainer } from "./ast-import.js"; import { getContinueButton } from "./components.js"; import { addFile, clearFiles, selectFile } from "./files.js"; -import { addEventListenersToAstNodes } from "./visualization.js"; +import { addHighlighingEventListeners } from "./visualization.js"; const webSocketOnMessage = (message: MessageEvent): void => { const continueButton = getContinueButton(); @@ -21,14 +21,14 @@ const webSocketOnMessage = (message: MessageEvent): void => { addFile(filename, filecode as string); importAst(ast); - addEventListenersToAstNodes(ast); + addHighlighingEventListeners(ast); selectFile(Object.keys(code)[0]); continueButton.disabled = buttonDisabled; break; - + case 'wait': continueButton.disabled = false; break; diff --git a/Lara-JS/src-api/visualization/public/js/components.ts b/Lara-JS/src-api/visualization/public/js/components.ts index 691903115..e56adcc86 100644 --- a/Lara-JS/src-api/visualization/public/js/components.ts +++ b/Lara-JS/src-api/visualization/public/js/components.ts @@ -14,6 +14,14 @@ const getCodeContainer = (() => { return (): HTMLDivElement => codeContainer; })(); +const getNodeInfoContainer = (() => { + const nodeInfoContainer = document.querySelector('#node-info-container'); + if (!nodeInfoContainer) { + throw new Error('Could not find node info container') + } + return (): HTMLDivElement => nodeInfoContainer; +})(); + const getContinueButton = (() => { const continueButton = document.querySelector('#continue-button'); if (!continueButton) { @@ -38,6 +46,63 @@ const getFileTabs = (() => { return (): HTMLDivElement => fileTabs; })(); + +const getNodeElement = (nodeId: string): HTMLSpanElement | null => { + return document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); +} + +const getNodeText = (nodeId: string): HTMLSpanElement | null => { + const nodeElement = getNodeElement(nodeId); + return nodeElement?.querySelector('.node-text') ?? null; +}; + +const getFirstNodeCodeElement = (nodeId: string): HTMLSpanElement | null => { + return document.querySelector(`.node-code[data-node-id="${nodeId}"]`); +} + +const getNodeCodeElements = (nodeId: string): HTMLSpanElement[] => { + return Array.from(document.querySelectorAll(`.node-code[data-node-id="${nodeId}"]`)); +}; + +const getHighlightableElements = (nodeId: string): HTMLElement[] => { + const nodeText = getNodeText(nodeId); + if (!nodeText) + return []; + + const nodeCodeElements = getNodeCodeElements(nodeId); + return [nodeText, ...nodeCodeElements]; +}; + + +const createCodeElement = (code: string = ''): HTMLElement => { + const codeElement = document.createElement('code'); + codeElement.textContent = code; + return codeElement; +} + +const createCodeWrapper = (): HTMLPreElement => { + return document.createElement('pre'); +} + +const createNodeInfoLine = (name: string, value: string): HTMLParagraphElement => { + const attributeName = document.createElement('span'); + attributeName.textContent = name + ': '; + + const attributeValue = document.createElement('span'); + attributeValue.textContent = value; + + const line = document.createElement('p'); + line.append(attributeName, attributeValue); + return line; +} + +const createNodeInfoAlert = (alert: string): HTMLParagraphElement => { + const codeAlert = document.createElement('p'); + codeAlert.classList.add('alert'); + codeAlert.textContent = alert; + return codeAlert; +} + const createFileTab = (filepath: string): HTMLButtonElement => { const fileTab = document.createElement('button'); fileTab.classList.add('file-tab'); @@ -52,8 +117,18 @@ const createFileTab = (filepath: string): HTMLButtonElement => { export { getAstContainer, getCodeContainer, + getNodeInfoContainer, getContinueButton, getResizer, getFileTabs, + getNodeElement, + getNodeText, + getFirstNodeCodeElement, + getNodeCodeElements, + getHighlightableElements, + createNodeInfoLine, + createNodeInfoAlert, + createCodeElement, + createCodeWrapper, createFileTab, }; \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index f52e293a2..cc953ef29 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -1,26 +1,21 @@ -import { getAstContainer, getCodeContainer, getContinueButton, getResizer } from "./components.js"; +import { createCodeElement, createCodeWrapper, createNodeInfoAlert, createNodeInfoLine, getAstContainer, getCodeContainer, getContinueButton, getFirstNodeCodeElement, getHighlightableElements, getNodeElement, getNodeInfoContainer, getNodeText, getResizer } from "./components.js"; import { selectFile } from "./files.js"; import JoinPoint from "./ToolJoinPoint.js"; -const getNodeElement = (nodeId: string): HTMLElement | null => { - return document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); -} - -const getNodeRelatedElements = (nodeId: string): HTMLElement[] => { - return Array.from(document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"] .ast-node-text, .node-code[data-node-id="${nodeId}"]`)); -}; - const highlightNode = (nodeId: string, strong: boolean): void => { - const nodeCode = document.querySelectorAll(` .node-code[data-node-id="${nodeId}"]`)!; - nodeCode.forEach(elem => elem.style.backgroundColor = strong ? 'var(--highlight-color)' : 'var(--secondary-highlight-color)'); + const nodeElement = getNodeElement(nodeId); + if (!nodeElement) { + console.warn(`There is no node with id ${nodeId}`); + return; + } - const nodeElement = document.querySelector(`.ast-node[data-node-id="${nodeId}"]`)!; - const nodeText = nodeElement.querySelector('.ast-node-text')!; - nodeText.style.backgroundColor = strong ? 'var(--highlight-color)' : 'var(--secondary-highlight-color)'; + const highlightableElements = getHighlightableElements(nodeId); + highlightableElements.forEach(elem => elem.style.backgroundColor = strong ? 'var(--highlight-color)' : 'var(--secondary-highlight-color)'); let parentNode = nodeElement.parentElement?.previousSibling; while (parentNode instanceof HTMLElement && parentNode.classList.contains('ast-node')) { - const parentNodeText = parentNode.querySelector('.ast-node-text')!; + const parentNodeId = parentNode.dataset.nodeId! + const parentNodeText = getNodeText(parentNodeId)!; parentNodeText.style.backgroundColor = strong ? 'var(--secondary-highlight-color)' : 'var(--tertiary-highlight-color)'; parentNode = parentNode.parentElement?.previousSibling; @@ -28,61 +23,52 @@ const highlightNode = (nodeId: string, strong: boolean): void => { }; const unhighlightNode = (nodeId: string): void => { - const nodeCode = document.querySelectorAll(`.node-code[data-node-id="${nodeId}"]`)!; - nodeCode.forEach(elem => elem.style.backgroundColor = ''); + const nodeElement = getNodeElement(nodeId); + if (!nodeElement) { + console.warn(`There is no node with id ${nodeId}`); + return; + } - const nodeElement = document.querySelector(`.ast-node[data-node-id="${nodeId}"]`)!; - const nodeText = nodeElement.querySelector('.ast-node-text')!; - nodeText.style.backgroundColor = ''; + const highlightableElements = getHighlightableElements(nodeId)!; + highlightableElements.forEach(elem => elem.style.backgroundColor = ''); - let parentNode = nodeElement.parentElement?.previousSibling as HTMLElement | null | undefined; + let parentNode = nodeElement.parentElement?.previousSibling; while (parentNode instanceof HTMLElement && parentNode.classList.contains('ast-node')) { - const parentNodeText = parentNode.querySelector('.ast-node-text')!; + const parentNodeId = parentNode.dataset.nodeId! + const parentNodeText = getNodeText(parentNodeId)!; parentNodeText.style.backgroundColor = ''; - parentNode = parentNode.parentElement?.previousSibling as HTMLElement | null | undefined; + parentNode = parentNode.parentElement?.previousSibling; } }; const showNodeInfo = (node: JoinPoint): void => { - const nodeInfoContainer = document.querySelector('#node-info-container')!; + const nodeInfoContainer = getNodeInfoContainer(); nodeInfoContainer.style.display = 'block'; nodeInfoContainer.innerHTML = '' for (const [name, value] of Object.entries(node.info)) { - const attributeName = document.createElement('span'); - attributeName.textContent = name + ': '; - - const attributeValue = document.createElement('span'); - attributeValue.textContent = value; - - const line = document.createElement('p'); - line.append(attributeName, attributeValue); + const line = createNodeInfoLine(name, value); nodeInfoContainer.appendChild(line); } - if (!document.querySelector(`.node-code[data-node-id="${node.id}"]`)) { - const codeAlert = document.createElement('p'); - codeAlert.classList.add('alert'); - - if (node.code !== undefined) { - codeAlert.textContent = 'Node code not found:'; - - const codeWrapper = document.createElement('pre'); - const code = document.createElement('code'); - code.textContent = node.code; - codeWrapper.appendChild(code) - - nodeInfoContainer.append(codeAlert, codeWrapper); + const hasCode = getFirstNodeCodeElement(node.id) !== null; + if (!hasCode) { + if (node.code) { + const alert = createNodeInfoAlert('Node code not found:'); + const codeElement = createCodeElement(node.code); + const codeWrapper = createCodeWrapper(); + codeWrapper.appendChild(codeElement); + nodeInfoContainer.append(alert, codeWrapper); } else { - codeAlert.textContent = 'Node does not have code!' - nodeInfoContainer.appendChild(codeAlert); + const alert = createNodeInfoAlert('Node does not have code!'); + nodeInfoContainer.appendChild(alert); } } } const hideNodeInfo = (): void => { - const nodeInfoContainer = document.querySelector('#node-info-container')!; + const nodeInfoContainer = getNodeInfoContainer(); nodeInfoContainer.style.display = 'none'; nodeInfoContainer.innerHTML = ''; } @@ -93,78 +79,89 @@ const scrollIntoViewIfNeeded = (element: HTMLElement, parent: HTMLElement): void if (rect.bottom < parentRect.top || rect.top > parentRect.bottom) { const scrollPos = rect.height <= parentRect.height - ? (rect.top + rect.bottom - parentRect.top - parentRect.bottom) / 2 - : rect.top - parentRect.top; + ? (rect.top + rect.bottom - parentRect.top - parentRect.bottom) / 2 + : rect.top - parentRect.top; + parent.scrollBy({ top: scrollPos, left: 0, behavior: 'smooth' }); } }; + let selectedNodeId: string | null = null; -const addHighlighingEvents = (node: JoinPoint): void => { - const nodeRelatedElements = getNodeRelatedElements(node.id); - for (const nodeRelatedElement of nodeRelatedElements) { - nodeRelatedElement.addEventListener('mouseover', event => { - highlightNode(node.id, false); - if (selectedNodeId !== null) - highlightNode(selectedNodeId, true); - event.stopPropagation(); - }); - nodeRelatedElement.addEventListener('mouseout', event => { - unhighlightNode(node.id); - if (selectedNodeId !== null) - highlightNode(selectedNodeId, true); - event.stopPropagation(); - }); - - nodeRelatedElement.tabIndex = 0; - nodeRelatedElement.addEventListener('click', event => { - event.stopPropagation(); - - if (selectedNodeId !== null) { - unhighlightNode(selectedNodeId); - if (selectedNodeId === node.id) { - selectedNodeId = null; - hideNodeInfo(); - return; - } - } - - selectedNodeId = node.id; - highlightNode(node.id, true); - if (node.filepath) - selectFile(node.filepath); - - - const nodeElement = getNodeElement(node.id)!; - const astContainer = getAstContainer(); - scrollIntoViewIfNeeded(nodeElement, astContainer); - const firstNodeCodeBlock = document.querySelector(`.node-code[data-node-id="${node.id}"]`); - if (firstNodeCodeBlock) { - const codeContainer = getCodeContainer(); - scrollIntoViewIfNeeded(firstNodeCodeBlock!, codeContainer); - } - - showNodeInfo(node); - }); - - // For keyboard accessibility - nodeRelatedElement.addEventListener('keydown', event => { - if (event.key === 'Enter') { - nodeRelatedElement.click(); - } - event.stopPropagation(); - }); +const highlightableOnMouseOver = (node: JoinPoint, event: Event): void => { + highlightNode(node.id, false); + if (selectedNodeId !== null) + highlightNode(selectedNodeId, true); + event.stopPropagation(); +}; + +const highlightableOnMouseOut = (node: JoinPoint, event: Event): void => { + unhighlightNode(node.id); + if (selectedNodeId !== null) + highlightNode(selectedNodeId, true); + event.stopPropagation(); +}; + +const highlightableOnClick = (node: JoinPoint, event: Event): void => { + event.stopPropagation(); + + if (selectedNodeId !== null) { + unhighlightNode(selectedNodeId); + if (selectedNodeId === node.id) { + selectedNodeId = null; + hideNodeInfo(); + return; + } + } + + selectedNodeId = node.id; + highlightNode(node.id, true); + if (node.filepath) + selectFile(node.filepath); + + + const nodeElement = getNodeElement(node.id)!; + const astContainer = getAstContainer(); + scrollIntoViewIfNeeded(nodeElement, astContainer); + + const firstNodeCodeBlock = getFirstNodeCodeElement(node.id); + if (firstNodeCodeBlock) { + const codeContainer = getCodeContainer(); + scrollIntoViewIfNeeded(firstNodeCodeBlock!, codeContainer); } + + showNodeInfo(node); }; -const addEventListenersToAstNodes = (root: JoinPoint): void => { - selectedNodeId = null; // To prevent invalid references +const addHighlighingEventListeners = (root: JoinPoint): void => { + const addListeners = (node: JoinPoint) => { + const highlightableElements = getHighlightableElements(node.id); + console.log(highlightableElements); + for (const element of highlightableElements) { + element.addEventListener('mouseover', event => highlightableOnMouseOver(node, event)); + element.addEventListener('mouseout', event => highlightableOnMouseOut(node, event)); + + element.tabIndex = 0; + element.addEventListener('click', event => highlightableOnClick(node, event)); + + // For keyboard accessibility + element.addEventListener('keydown', event => { + if (event.key === 'Enter') { + element.click(); + } + event.stopPropagation(); + }); + } - addHighlighingEvents(root); - root.children.forEach(child => addEventListenersToAstNodes(child)); + node.children.forEach(child => addListeners(child)); + } + + selectedNodeId = null; // To prevent invalid references + addListeners(root); }; + const addDividerEventListeners = (): void => { const resizer = getResizer(); const astContainer = getAstContainer(); @@ -187,11 +184,12 @@ const addDividerEventListeners = (): void => { document.addEventListener('mousemove', event => { if (drag) { const astLeft = astContainer.getBoundingClientRect().left; + const minWidth = continueButton.offsetWidth; const maxWidth = codeContainer.getBoundingClientRect().right - astLeft - 160; width = event.x - astLeft; - if (width < continueButton.offsetWidth) - width = continueButton.offsetWidth; + if (width < minWidth) + width = minWidth; else if (width > maxWidth) width = maxWidth; rootStyle.setProperty('--ast-container-width', `${width}px`); @@ -199,4 +197,4 @@ const addDividerEventListeners = (): void => { }); }; -export { addEventListenersToAstNodes, addDividerEventListeners }; +export { addHighlighingEventListeners, addDividerEventListeners }; diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index b380ffd1b..dff9a50e6 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -42,11 +42,11 @@ const createDropdownButtonOnClick = (dropdown) => { }; }; const createAstNodeElement = (nodeId, text, dropdownButton) => { - const nodeElement = document.createElement('span'); + const nodeElement = document.createElement('span'); // TODO: Convert to div nodeElement.classList.add('ast-node'); nodeElement.dataset.nodeId = nodeId; const nodeText = document.createElement('span'); - nodeText.classList.add('ast-node-text'); + nodeText.classList.add('node-text'); nodeText.textContent = text; nodeElement.appendChild(dropdownButton); nodeElement.appendChild(nodeText); diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js index 8a59344ee..d9be93429 100644 --- a/LaraApi/src-lara/visualization/public/js/communication.js +++ b/LaraApi/src-lara/visualization/public/js/communication.js @@ -1,7 +1,7 @@ import { importAst, initCodeContainer } from "./ast-import.js"; import { getContinueButton } from "./components.js"; import { addFile, clearFiles, selectFile } from "./files.js"; -import { addEventListenersToAstNodes } from "./visualization.js"; +import { addHighlighingEventListeners } from "./visualization.js"; const webSocketOnMessage = (message) => { const continueButton = getContinueButton(); const data = parseMessage(message); @@ -15,7 +15,7 @@ const webSocketOnMessage = (message) => { for (const [filename, filecode] of Object.entries(code)) addFile(filename, filecode); importAst(ast); - addEventListenersToAstNodes(ast); + addHighlighingEventListeners(ast); selectFile(Object.keys(code)[0]); continueButton.disabled = buttonDisabled; break; diff --git a/LaraApi/src-lara/visualization/public/js/components.js b/LaraApi/src-lara/visualization/public/js/components.js index 61d7c68db..32f1e1983 100644 --- a/LaraApi/src-lara/visualization/public/js/components.js +++ b/LaraApi/src-lara/visualization/public/js/components.js @@ -12,6 +12,13 @@ const getCodeContainer = (() => { } return () => codeContainer; })(); +const getNodeInfoContainer = (() => { + const nodeInfoContainer = document.querySelector('#node-info-container'); + if (!nodeInfoContainer) { + throw new Error('Could not find node info container'); + } + return () => nodeInfoContainer; +})(); const getContinueButton = (() => { const continueButton = document.querySelector('#continue-button'); if (!continueButton) { @@ -33,6 +40,49 @@ const getFileTabs = (() => { } return () => fileTabs; })(); +const getNodeElement = (nodeId) => { + return document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); +}; +const getNodeText = (nodeId) => { + const nodeElement = getNodeElement(nodeId); + return nodeElement?.querySelector('.node-text') ?? null; +}; +const getFirstNodeCodeElement = (nodeId) => { + return document.querySelector(`.node-code[data-node-id="${nodeId}"]`); +}; +const getNodeCodeElements = (nodeId) => { + return Array.from(document.querySelectorAll(`.node-code[data-node-id="${nodeId}"]`)); +}; +const getHighlightableElements = (nodeId) => { + const nodeText = getNodeText(nodeId); + if (!nodeText) + return []; + const nodeCodeElements = getNodeCodeElements(nodeId); + return [nodeText, ...nodeCodeElements]; +}; +const createCodeElement = (code = '') => { + const codeElement = document.createElement('code'); + codeElement.textContent = code; + return codeElement; +}; +const createCodeWrapper = () => { + return document.createElement('pre'); +}; +const createNodeInfoLine = (name, value) => { + const attributeName = document.createElement('span'); + attributeName.textContent = name + ': '; + const attributeValue = document.createElement('span'); + attributeValue.textContent = value; + const line = document.createElement('p'); + line.append(attributeName, attributeValue); + return line; +}; +const createNodeInfoAlert = (alert) => { + const codeAlert = document.createElement('p'); + codeAlert.classList.add('alert'); + codeAlert.textContent = alert; + return codeAlert; +}; const createFileTab = (filepath) => { const fileTab = document.createElement('button'); fileTab.classList.add('file-tab'); @@ -41,5 +91,5 @@ const createFileTab = (filepath) => { fileTab.textContent = filepath !== '' ? filepath.slice(filepath.lastIndexOf('/') + 1) : ''; return fileTab; }; -export { getAstContainer, getCodeContainer, getContinueButton, getResizer, getFileTabs, createFileTab, }; +export { getAstContainer, getCodeContainer, getNodeInfoContainer, getContinueButton, getResizer, getFileTabs, getNodeElement, getNodeText, getFirstNodeCodeElement, getNodeCodeElements, getHighlightableElements, createNodeInfoLine, createNodeInfoAlert, createCodeElement, createCodeWrapper, createFileTab, }; //# sourceMappingURL=components.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index b2704b6d3..0fea133e6 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -1,69 +1,62 @@ -import { getAstContainer, getCodeContainer, getContinueButton, getResizer } from "./components.js"; +import { createCodeElement, createCodeWrapper, createNodeInfoAlert, createNodeInfoLine, getAstContainer, getCodeContainer, getContinueButton, getFirstNodeCodeElement, getHighlightableElements, getNodeElement, getNodeInfoContainer, getNodeText, getResizer } from "./components.js"; import { selectFile } from "./files.js"; -const getNodeElement = (nodeId) => { - return document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); -}; -const getNodeRelatedElements = (nodeId) => { - return Array.from(document.querySelectorAll(`.ast-node[data-node-id="${nodeId}"] .ast-node-text, .node-code[data-node-id="${nodeId}"]`)); -}; const highlightNode = (nodeId, strong) => { - const nodeCode = document.querySelectorAll(` .node-code[data-node-id="${nodeId}"]`); - nodeCode.forEach(elem => elem.style.backgroundColor = strong ? 'var(--highlight-color)' : 'var(--secondary-highlight-color)'); - const nodeElement = document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); - const nodeText = nodeElement.querySelector('.ast-node-text'); - nodeText.style.backgroundColor = strong ? 'var(--highlight-color)' : 'var(--secondary-highlight-color)'; + const nodeElement = getNodeElement(nodeId); + if (!nodeElement) { + console.warn(`There is no node with id ${nodeId}`); + return; + } + const highlightableElements = getHighlightableElements(nodeId); + highlightableElements.forEach(elem => elem.style.backgroundColor = strong ? 'var(--highlight-color)' : 'var(--secondary-highlight-color)'); let parentNode = nodeElement.parentElement?.previousSibling; while (parentNode instanceof HTMLElement && parentNode.classList.contains('ast-node')) { - const parentNodeText = parentNode.querySelector('.ast-node-text'); + const parentNodeId = parentNode.dataset.nodeId; + const parentNodeText = getNodeText(parentNodeId); parentNodeText.style.backgroundColor = strong ? 'var(--secondary-highlight-color)' : 'var(--tertiary-highlight-color)'; parentNode = parentNode.parentElement?.previousSibling; } }; const unhighlightNode = (nodeId) => { - const nodeCode = document.querySelectorAll(`.node-code[data-node-id="${nodeId}"]`); - nodeCode.forEach(elem => elem.style.backgroundColor = ''); - const nodeElement = document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); - const nodeText = nodeElement.querySelector('.ast-node-text'); - nodeText.style.backgroundColor = ''; + const nodeElement = getNodeElement(nodeId); + if (!nodeElement) { + console.warn(`There is no node with id ${nodeId}`); + return; + } + const highlightableElements = getHighlightableElements(nodeId); + highlightableElements.forEach(elem => elem.style.backgroundColor = ''); let parentNode = nodeElement.parentElement?.previousSibling; while (parentNode instanceof HTMLElement && parentNode.classList.contains('ast-node')) { - const parentNodeText = parentNode.querySelector('.ast-node-text'); + const parentNodeId = parentNode.dataset.nodeId; + const parentNodeText = getNodeText(parentNodeId); parentNodeText.style.backgroundColor = ''; parentNode = parentNode.parentElement?.previousSibling; } }; const showNodeInfo = (node) => { - const nodeInfoContainer = document.querySelector('#node-info-container'); + const nodeInfoContainer = getNodeInfoContainer(); nodeInfoContainer.style.display = 'block'; nodeInfoContainer.innerHTML = ''; for (const [name, value] of Object.entries(node.info)) { - const attributeName = document.createElement('span'); - attributeName.textContent = name + ': '; - const attributeValue = document.createElement('span'); - attributeValue.textContent = value; - const line = document.createElement('p'); - line.append(attributeName, attributeValue); + const line = createNodeInfoLine(name, value); nodeInfoContainer.appendChild(line); } - if (!document.querySelector(`.node-code[data-node-id="${node.id}"]`)) { - const codeAlert = document.createElement('p'); - codeAlert.classList.add('alert'); - if (node.code !== undefined) { - codeAlert.textContent = 'Node code not found:'; - const codeWrapper = document.createElement('pre'); - const code = document.createElement('code'); - code.textContent = node.code; - codeWrapper.appendChild(code); - nodeInfoContainer.append(codeAlert, codeWrapper); + const hasCode = getFirstNodeCodeElement(node.id) !== null; + if (!hasCode) { + if (node.code) { + const alert = createNodeInfoAlert('Node code not found:'); + const codeElement = createCodeElement(node.code); + const codeWrapper = createCodeWrapper(); + codeWrapper.appendChild(codeElement); + nodeInfoContainer.append(alert, codeWrapper); } else { - codeAlert.textContent = 'Node does not have code!'; - nodeInfoContainer.appendChild(codeAlert); + const alert = createNodeInfoAlert('Node does not have code!'); + nodeInfoContainer.appendChild(alert); } } }; const hideNodeInfo = () => { - const nodeInfoContainer = document.querySelector('#node-info-container'); + const nodeInfoContainer = getNodeInfoContainer(); nodeInfoContainer.style.display = 'none'; nodeInfoContainer.innerHTML = ''; }; @@ -78,59 +71,63 @@ const scrollIntoViewIfNeeded = (element, parent) => { } }; let selectedNodeId = null; -const addHighlighingEvents = (node) => { - const nodeRelatedElements = getNodeRelatedElements(node.id); - for (const nodeRelatedElement of nodeRelatedElements) { - nodeRelatedElement.addEventListener('mouseover', event => { - highlightNode(node.id, false); - if (selectedNodeId !== null) - highlightNode(selectedNodeId, true); - event.stopPropagation(); - }); - nodeRelatedElement.addEventListener('mouseout', event => { - unhighlightNode(node.id); - if (selectedNodeId !== null) - highlightNode(selectedNodeId, true); - event.stopPropagation(); - }); - nodeRelatedElement.tabIndex = 0; - nodeRelatedElement.addEventListener('click', event => { - event.stopPropagation(); - if (selectedNodeId !== null) { - unhighlightNode(selectedNodeId); - if (selectedNodeId === node.id) { - selectedNodeId = null; - hideNodeInfo(); - return; - } - } - selectedNodeId = node.id; - highlightNode(node.id, true); - if (node.filepath) - selectFile(node.filepath); - const nodeElement = getNodeElement(node.id); - const astContainer = getAstContainer(); - scrollIntoViewIfNeeded(nodeElement, astContainer); - const firstNodeCodeBlock = document.querySelector(`.node-code[data-node-id="${node.id}"]`); - if (firstNodeCodeBlock) { - const codeContainer = getCodeContainer(); - scrollIntoViewIfNeeded(firstNodeCodeBlock, codeContainer); - } - showNodeInfo(node); - }); - // For keyboard accessibility - nodeRelatedElement.addEventListener('keydown', event => { - if (event.key === 'Enter') { - nodeRelatedElement.click(); - } - event.stopPropagation(); - }); +const highlightableOnMouseOver = (node, event) => { + highlightNode(node.id, false); + if (selectedNodeId !== null) + highlightNode(selectedNodeId, true); + event.stopPropagation(); +}; +const highlightableOnMouseOut = (node, event) => { + unhighlightNode(node.id); + if (selectedNodeId !== null) + highlightNode(selectedNodeId, true); + event.stopPropagation(); +}; +const highlightableOnClick = (node, event) => { + event.stopPropagation(); + if (selectedNodeId !== null) { + unhighlightNode(selectedNodeId); + if (selectedNodeId === node.id) { + selectedNodeId = null; + hideNodeInfo(); + return; + } + } + selectedNodeId = node.id; + highlightNode(node.id, true); + if (node.filepath) + selectFile(node.filepath); + const nodeElement = getNodeElement(node.id); + const astContainer = getAstContainer(); + scrollIntoViewIfNeeded(nodeElement, astContainer); + const firstNodeCodeBlock = getFirstNodeCodeElement(node.id); + if (firstNodeCodeBlock) { + const codeContainer = getCodeContainer(); + scrollIntoViewIfNeeded(firstNodeCodeBlock, codeContainer); } + showNodeInfo(node); }; -const addEventListenersToAstNodes = (root) => { +const addHighlighingEventListeners = (root) => { + const addListeners = (node) => { + const highlightableElements = getHighlightableElements(node.id); + console.log(highlightableElements); + for (const element of highlightableElements) { + element.addEventListener('mouseover', event => highlightableOnMouseOver(node, event)); + element.addEventListener('mouseout', event => highlightableOnMouseOut(node, event)); + element.tabIndex = 0; + element.addEventListener('click', event => highlightableOnClick(node, event)); + // For keyboard accessibility + element.addEventListener('keydown', event => { + if (event.key === 'Enter') { + element.click(); + } + event.stopPropagation(); + }); + } + node.children.forEach(child => addListeners(child)); + }; selectedNodeId = null; // To prevent invalid references - addHighlighingEvents(root); - root.children.forEach(child => addEventListenersToAstNodes(child)); + addListeners(root); }; const addDividerEventListeners = () => { const resizer = getResizer(); @@ -149,15 +146,16 @@ const addDividerEventListeners = () => { document.addEventListener('mousemove', event => { if (drag) { const astLeft = astContainer.getBoundingClientRect().left; + const minWidth = continueButton.offsetWidth; const maxWidth = codeContainer.getBoundingClientRect().right - astLeft - 160; width = event.x - astLeft; - if (width < continueButton.offsetWidth) - width = continueButton.offsetWidth; + if (width < minWidth) + width = minWidth; else if (width > maxWidth) width = maxWidth; rootStyle.setProperty('--ast-container-width', `${width}px`); } }); }; -export { addEventListenersToAstNodes, addDividerEventListeners }; +export { addHighlighingEventListeners, addDividerEventListeners }; //# sourceMappingURL=visualization.js.map \ No newline at end of file From 13d77e47a8302b9dbf2815df8205c6b66f929526 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 21:34:52 +0100 Subject: [PATCH 116/136] Refactor ast-import.ts --- .../visualization/public/js/ast-import.ts | 110 +++++------------- .../visualization/public/js/components.ts | 100 ++++++++++++++-- .../visualization/public/js/visualization.ts | 6 +- .../visualization/public/js/ast-import.js | 91 +++++---------- .../visualization/public/js/components.js | 64 +++++++++- .../visualization/public/js/visualization.js | 1 - 6 files changed, 217 insertions(+), 155 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index 49206078a..af3add2b0 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -1,107 +1,61 @@ import { countChar, createIcon, } from './utils.js'; import JoinPoint from './ToolJoinPoint.js'; -import { getAstContainer, getCodeContainer } from './components.js'; +import { createCodeElement, createCodeLines, createCodeWrapper, createNodeDropdown, createNodeDropdownButton, createNodeElement, getActiveCodeElement, getAstContainer, getCodeContainer, getCodeLines, getMainCodeWrapper } from './components.js'; const updateLines = (): void => { - const codeContainer = getCodeContainer(); - const codeLines = codeContainer.querySelector('.lines')!; - const code = codeContainer.querySelector('code.active')!.textContent!; + const codeLines = getCodeLines(); + const codeWrapper = getMainCodeWrapper(); + if (!codeLines || !codeWrapper) + throw new Error('Code container not initialized'); + + const codeElement = getActiveCodeElement(); + const code = codeElement?.textContent ?? ''; const numLines = countChar(code, '\n') + 1; - codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); + const newCodeLines = createCodeLines(numLines); + codeLines.replaceWith(newCodeLines); }; const initCodeContainer = (): void => { const codeContainer = getCodeContainer(); codeContainer.innerHTML = ''; - const codePre = document.createElement('pre'); - codePre.classList.add('lines'); - - const codeLines = document.createElement('pre'); - codeLines.classList.add('code-wrapper'); - - codeContainer.append(codePre, codeLines) -} - -const addCode = (code: string, filepath: string): void => { - const codeElement = document.createElement('code'); - codeElement.dataset.filepath = filepath; - codeElement.innerHTML = code; - - const codeContainer = getCodeContainer(); - const codePre = codeContainer.querySelector('pre.code-wrapper')!; - codePre.appendChild(codeElement); -} - -const createAstNodeDropdownButton = (): HTMLButtonElement => { - const dropdownButton = document.createElement('button'); + const codeLines = createCodeLines(0); + const codeWrapper = createCodeWrapper(); - const chevronIcon = createIcon("keyboard_arrow_down"); - dropdownButton.appendChild(chevronIcon); - - return dropdownButton; + codeContainer.append(codeLines, codeWrapper); }; -const createDropdownButtonOnClick = (dropdown: HTMLElement) => { - let nodeCollapsed = false; - return (event: Event): void => { - nodeCollapsed = !nodeCollapsed; - dropdown.style.display = nodeCollapsed ? "none" : "block"; - - const dropdownButton = event.currentTarget as HTMLElement; - const chevronIcon = dropdownButton.children[0] as HTMLElement; - chevronIcon.textContent = nodeCollapsed ? "keyboard_arrow_right" : "keyboard_arrow_down"; +const addCode = (code: string, filepath: string): void => { + const codeWrapper = getMainCodeWrapper(); + if (!codeWrapper) + throw new Error('Code container not initialized'); - event.stopPropagation(); - }; + const codeElement = createCodeElement(code); + codeElement.dataset.filepath = filepath; + codeWrapper.appendChild(codeElement); }; -const createAstNodeElement = (nodeId: string, text: string, dropdownButton: HTMLElement): HTMLSpanElement => { - const nodeElement = document.createElement('span'); // TODO: Convert to div - nodeElement.classList.add('ast-node'); - - nodeElement.dataset.nodeId = nodeId; - - const nodeText = document.createElement('span'); - nodeText.classList.add('node-text'); - nodeText.textContent = text; - - nodeElement.appendChild(dropdownButton); - nodeElement.appendChild(nodeText); - - return nodeElement; -} - -const createAstNodeDropdown = (nodeId: string): HTMLDivElement => { - const dropdown = document.createElement('div'); - dropdown.classList.add('ast-node-dropdown'); - dropdown.dataset.nodeId = nodeId; - - return dropdown; -} - -const convertAstNodeToHtml = (root: JoinPoint): DocumentFragment => { +const toNodeElements = (root: JoinPoint): DocumentFragment => { const fragment = new DocumentFragment(); - const dropdownButton = createAstNodeDropdownButton(); - const rootElement = createAstNodeElement(root.id, root.type, dropdownButton); if (root.children.length > 0) { - const rootDropdown = createAstNodeDropdown(root.id); + const dropdown = createNodeDropdown(root.id); for (const node of root.children) { - const descendantNodeElements = convertAstNodeToHtml(node); - rootDropdown.appendChild(descendantNodeElements); + const descendantNodeElements = toNodeElements(node); + dropdown.appendChild(descendantNodeElements); } - dropdownButton.addEventListener('click', createDropdownButtonOnClick(rootDropdown)); + const dropdownButton = createNodeDropdownButton(dropdown); + const nodeElement = createNodeElement(root.id, root.type, dropdownButton); - fragment.appendChild(rootElement); - fragment.appendChild(rootDropdown); + fragment.append(nodeElement, dropdown); } else { - dropdownButton.disabled = true; + const dropdownButton = createNodeDropdownButton(); + const nodeElement = createNodeElement(root.id, root.type, dropdownButton); - fragment.appendChild(rootElement); + fragment.appendChild(nodeElement); } return fragment; @@ -110,9 +64,9 @@ const convertAstNodeToHtml = (root: JoinPoint): DocumentFragment => { const importAst = (astRoot: JoinPoint): void => { const astContainer = getAstContainer(); - const astFragment = convertAstNodeToHtml(astRoot); + const astFragment = toNodeElements(astRoot); astContainer.innerHTML = ''; astContainer.appendChild(astFragment); -} +}; export { importAst, initCodeContainer, addCode, updateLines }; diff --git a/Lara-JS/src-api/visualization/public/js/components.ts b/Lara-JS/src-api/visualization/public/js/components.ts index e56adcc86..b6e61514a 100644 --- a/Lara-JS/src-api/visualization/public/js/components.ts +++ b/Lara-JS/src-api/visualization/public/js/components.ts @@ -1,3 +1,5 @@ +import { createIcon } from "./utils.js"; + const getAstContainer = (() => { const astContainer = document.querySelector('#ast-container'); if (!astContainer) { @@ -49,7 +51,7 @@ const getFileTabs = (() => { const getNodeElement = (nodeId: string): HTMLSpanElement | null => { return document.querySelector(`.ast-node[data-node-id="${nodeId}"]`); -} +}; const getNodeText = (nodeId: string): HTMLSpanElement | null => { const nodeElement = getNodeElement(nodeId); @@ -58,7 +60,7 @@ const getNodeText = (nodeId: string): HTMLSpanElement | null => { const getFirstNodeCodeElement = (nodeId: string): HTMLSpanElement | null => { return document.querySelector(`.node-code[data-node-id="${nodeId}"]`); -} +}; const getNodeCodeElements = (nodeId: string): HTMLSpanElement[] => { return Array.from(document.querySelectorAll(`.node-code[data-node-id="${nodeId}"]`)); @@ -73,16 +75,91 @@ const getHighlightableElements = (nodeId: string): HTMLElement[] => { return [nodeText, ...nodeCodeElements]; }; +const getMainCodeWrapper = (): HTMLPreElement | null => { + return getCodeContainer().querySelector('pre.code-wrapper'); +}; + +const getCodeLines = (): HTMLPreElement | null => { + return getCodeContainer().querySelector('pre.lines'); +}; + +const getActiveCodeElement = (): HTMLElement | null => { + return getMainCodeWrapper()?.querySelector('code.active') ?? null; +}; + + +const createNodeDropdown = (nodeId: string): HTMLDivElement => { + const dropdown = document.createElement('div'); + dropdown.classList.add('ast-node-dropdown'); + dropdown.dataset.nodeId = nodeId; + + return dropdown; +}; + +const createDropdownButtonOnClick = (dropdown: HTMLElement) => { + let nodeCollapsed = false; + return (event: Event): void => { + nodeCollapsed = !nodeCollapsed; + dropdown.style.display = nodeCollapsed ? 'none' : 'block'; + + const dropdownButton = event.currentTarget as HTMLElement; + const chevronIcon = dropdownButton.children[0] as HTMLElement; + chevronIcon.textContent = nodeCollapsed ? 'keyboard_arrow_right' : 'keyboard_arrow_down'; + + event.stopPropagation(); + }; +}; + +const createNodeDropdownButton = (dropdown?: HTMLElement): HTMLButtonElement => { + const dropdownButton = document.createElement('button'); + + const arrowIcon = createIcon('keyboard_arrow_down'); + dropdownButton.appendChild(arrowIcon); + + if (dropdown) { + dropdownButton.addEventListener('click', createDropdownButtonOnClick(dropdown)); + } else { + dropdownButton.disabled = true; + } + + return dropdownButton; +}; + +const createNodeElement = (nodeId: string, text: string, dropdownButton: HTMLElement): HTMLSpanElement => { + const nodeElement = document.createElement('span'); // TODO: Convert to div + nodeElement.classList.add('ast-node'); + + nodeElement.dataset.nodeId = nodeId; + + const nodeText = document.createElement('span'); + nodeText.classList.add('node-text'); + nodeText.textContent = text; + + nodeElement.appendChild(dropdownButton); + nodeElement.appendChild(nodeText); + + return nodeElement; +}; + const createCodeElement = (code: string = ''): HTMLElement => { const codeElement = document.createElement('code'); - codeElement.textContent = code; + codeElement.innerHTML = code; return codeElement; -} +}; + +const createCodeLines = (numLines: number): HTMLPreElement => { + const codeLines = document.createElement('pre'); + codeLines.classList.add('lines'); + codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); + return codeLines; +}; const createCodeWrapper = (): HTMLPreElement => { - return document.createElement('pre'); -} + const codeWrapper = document.createElement('pre'); + codeWrapper.classList.add('code-wrapper'); + return codeWrapper; +}; const createNodeInfoLine = (name: string, value: string): HTMLParagraphElement => { const attributeName = document.createElement('span'); @@ -94,14 +171,14 @@ const createNodeInfoLine = (name: string, value: string): HTMLParagraphElement = const line = document.createElement('p'); line.append(attributeName, attributeValue); return line; -} +}; const createNodeInfoAlert = (alert: string): HTMLParagraphElement => { const codeAlert = document.createElement('p'); codeAlert.classList.add('alert'); codeAlert.textContent = alert; return codeAlert; -} +}; const createFileTab = (filepath: string): HTMLButtonElement => { const fileTab = document.createElement('button'); @@ -126,8 +203,15 @@ export { getFirstNodeCodeElement, getNodeCodeElements, getHighlightableElements, + getMainCodeWrapper, + getCodeLines, + getActiveCodeElement, + createNodeDropdown, + createNodeDropdownButton, + createNodeElement, createNodeInfoLine, createNodeInfoAlert, + createCodeLines, createCodeElement, createCodeWrapper, createFileTab, diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index cc953ef29..b355223c7 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -65,13 +65,13 @@ const showNodeInfo = (node: JoinPoint): void => { nodeInfoContainer.appendChild(alert); } } -} +}; const hideNodeInfo = (): void => { const nodeInfoContainer = getNodeInfoContainer(); nodeInfoContainer.style.display = 'none'; nodeInfoContainer.innerHTML = ''; -} +}; const scrollIntoViewIfNeeded = (element: HTMLElement, parent: HTMLElement): void => { const rect = element.getBoundingClientRect(); @@ -137,7 +137,7 @@ const highlightableOnClick = (node: JoinPoint, event: Event): void => { const addHighlighingEventListeners = (root: JoinPoint): void => { const addListeners = (node: JoinPoint) => { const highlightableElements = getHighlightableElements(node.id); - console.log(highlightableElements); + for (const element of highlightableElements) { element.addEventListener('mouseover', event => highlightableOnMouseOver(node, event)); element.addEventListener('mouseout', event => highlightableOnMouseOut(node, event)); diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index dff9a50e6..a7411b20e 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -1,86 +1,53 @@ -import { countChar, createIcon, } from './utils.js'; -import { getAstContainer, getCodeContainer } from './components.js'; +import { countChar, } from './utils.js'; +import { createCodeElement, createCodeLines, createCodeWrapper, createNodeDropdown, createNodeDropdownButton, createNodeElement, getActiveCodeElement, getAstContainer, getCodeContainer, getCodeLines, getMainCodeWrapper } from './components.js'; const updateLines = () => { - const codeContainer = getCodeContainer(); - const codeLines = codeContainer.querySelector('.lines'); - const code = codeContainer.querySelector('code.active').textContent; + const codeLines = getCodeLines(); + const codeWrapper = getMainCodeWrapper(); + if (!codeLines || !codeWrapper) + throw new Error('Code container not initialized'); + const codeElement = getActiveCodeElement(); + const code = codeElement?.textContent ?? ''; const numLines = countChar(code, '\n') + 1; - codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); + const newCodeLines = createCodeLines(numLines); + codeLines.replaceWith(newCodeLines); }; const initCodeContainer = () => { const codeContainer = getCodeContainer(); codeContainer.innerHTML = ''; - const codePre = document.createElement('pre'); - codePre.classList.add('lines'); - const codeLines = document.createElement('pre'); - codeLines.classList.add('code-wrapper'); - codeContainer.append(codePre, codeLines); + const codeLines = createCodeLines(0); + const codeWrapper = createCodeWrapper(); + codeContainer.append(codeLines, codeWrapper); }; const addCode = (code, filepath) => { - const codeElement = document.createElement('code'); + const codeWrapper = getMainCodeWrapper(); + if (!codeWrapper) + throw new Error('Code container not initialized'); + const codeElement = createCodeElement(code); codeElement.dataset.filepath = filepath; - codeElement.innerHTML = code; - const codeContainer = getCodeContainer(); - const codePre = codeContainer.querySelector('pre.code-wrapper'); - codePre.appendChild(codeElement); -}; -const createAstNodeDropdownButton = () => { - const dropdownButton = document.createElement('button'); - const chevronIcon = createIcon("keyboard_arrow_down"); - dropdownButton.appendChild(chevronIcon); - return dropdownButton; -}; -const createDropdownButtonOnClick = (dropdown) => { - let nodeCollapsed = false; - return (event) => { - nodeCollapsed = !nodeCollapsed; - dropdown.style.display = nodeCollapsed ? "none" : "block"; - const dropdownButton = event.currentTarget; - const chevronIcon = dropdownButton.children[0]; - chevronIcon.textContent = nodeCollapsed ? "keyboard_arrow_right" : "keyboard_arrow_down"; - event.stopPropagation(); - }; -}; -const createAstNodeElement = (nodeId, text, dropdownButton) => { - const nodeElement = document.createElement('span'); // TODO: Convert to div - nodeElement.classList.add('ast-node'); - nodeElement.dataset.nodeId = nodeId; - const nodeText = document.createElement('span'); - nodeText.classList.add('node-text'); - nodeText.textContent = text; - nodeElement.appendChild(dropdownButton); - nodeElement.appendChild(nodeText); - return nodeElement; -}; -const createAstNodeDropdown = (nodeId) => { - const dropdown = document.createElement('div'); - dropdown.classList.add('ast-node-dropdown'); - dropdown.dataset.nodeId = nodeId; - return dropdown; + codeWrapper.appendChild(codeElement); }; -const convertAstNodeToHtml = (root) => { +const toNodeElements = (root) => { const fragment = new DocumentFragment(); - const dropdownButton = createAstNodeDropdownButton(); - const rootElement = createAstNodeElement(root.id, root.type, dropdownButton); if (root.children.length > 0) { - const rootDropdown = createAstNodeDropdown(root.id); + const dropdown = createNodeDropdown(root.id); for (const node of root.children) { - const descendantNodeElements = convertAstNodeToHtml(node); - rootDropdown.appendChild(descendantNodeElements); + const descendantNodeElements = toNodeElements(node); + dropdown.appendChild(descendantNodeElements); } - dropdownButton.addEventListener('click', createDropdownButtonOnClick(rootDropdown)); - fragment.appendChild(rootElement); - fragment.appendChild(rootDropdown); + const dropdownButton = createNodeDropdownButton(dropdown); + const nodeElement = createNodeElement(root.id, root.type, dropdownButton); + fragment.append(nodeElement, dropdown); } else { - dropdownButton.disabled = true; - fragment.appendChild(rootElement); + const dropdownButton = createNodeDropdownButton(); + const nodeElement = createNodeElement(root.id, root.type, dropdownButton); + fragment.appendChild(nodeElement); } return fragment; }; const importAst = (astRoot) => { const astContainer = getAstContainer(); - const astFragment = convertAstNodeToHtml(astRoot); + const astFragment = toNodeElements(astRoot); astContainer.innerHTML = ''; astContainer.appendChild(astFragment); }; diff --git a/LaraApi/src-lara/visualization/public/js/components.js b/LaraApi/src-lara/visualization/public/js/components.js index 32f1e1983..06b94e50a 100644 --- a/LaraApi/src-lara/visualization/public/js/components.js +++ b/LaraApi/src-lara/visualization/public/js/components.js @@ -1,3 +1,4 @@ +import { createIcon } from "./utils.js"; const getAstContainer = (() => { const astContainer = document.querySelector('#ast-container'); if (!astContainer) { @@ -60,13 +61,70 @@ const getHighlightableElements = (nodeId) => { const nodeCodeElements = getNodeCodeElements(nodeId); return [nodeText, ...nodeCodeElements]; }; +const getMainCodeWrapper = () => { + return getCodeContainer().querySelector('pre.code-wrapper'); +}; +const getCodeLines = () => { + return getCodeContainer().querySelector('pre.lines'); +}; +const getActiveCodeElement = () => { + return getMainCodeWrapper()?.querySelector('code.active') ?? null; +}; +const createNodeDropdown = (nodeId) => { + const dropdown = document.createElement('div'); + dropdown.classList.add('ast-node-dropdown'); + dropdown.dataset.nodeId = nodeId; + return dropdown; +}; +const createDropdownButtonOnClick = (dropdown) => { + let nodeCollapsed = false; + return (event) => { + nodeCollapsed = !nodeCollapsed; + dropdown.style.display = nodeCollapsed ? 'none' : 'block'; + const dropdownButton = event.currentTarget; + const chevronIcon = dropdownButton.children[0]; + chevronIcon.textContent = nodeCollapsed ? 'keyboard_arrow_right' : 'keyboard_arrow_down'; + event.stopPropagation(); + }; +}; +const createNodeDropdownButton = (dropdown) => { + const dropdownButton = document.createElement('button'); + const arrowIcon = createIcon('keyboard_arrow_down'); + dropdownButton.appendChild(arrowIcon); + if (dropdown) { + dropdownButton.addEventListener('click', createDropdownButtonOnClick(dropdown)); + } + else { + dropdownButton.disabled = true; + } + return dropdownButton; +}; +const createNodeElement = (nodeId, text, dropdownButton) => { + const nodeElement = document.createElement('span'); // TODO: Convert to div + nodeElement.classList.add('ast-node'); + nodeElement.dataset.nodeId = nodeId; + const nodeText = document.createElement('span'); + nodeText.classList.add('node-text'); + nodeText.textContent = text; + nodeElement.appendChild(dropdownButton); + nodeElement.appendChild(nodeText); + return nodeElement; +}; const createCodeElement = (code = '') => { const codeElement = document.createElement('code'); - codeElement.textContent = code; + codeElement.innerHTML = code; return codeElement; }; +const createCodeLines = (numLines) => { + const codeLines = document.createElement('pre'); + codeLines.classList.add('lines'); + codeLines.textContent = Array.from({ length: numLines }, (_, i) => i + 1).join('\n'); + return codeLines; +}; const createCodeWrapper = () => { - return document.createElement('pre'); + const codeWrapper = document.createElement('pre'); + codeWrapper.classList.add('code-wrapper'); + return codeWrapper; }; const createNodeInfoLine = (name, value) => { const attributeName = document.createElement('span'); @@ -91,5 +149,5 @@ const createFileTab = (filepath) => { fileTab.textContent = filepath !== '' ? filepath.slice(filepath.lastIndexOf('/') + 1) : ''; return fileTab; }; -export { getAstContainer, getCodeContainer, getNodeInfoContainer, getContinueButton, getResizer, getFileTabs, getNodeElement, getNodeText, getFirstNodeCodeElement, getNodeCodeElements, getHighlightableElements, createNodeInfoLine, createNodeInfoAlert, createCodeElement, createCodeWrapper, createFileTab, }; +export { getAstContainer, getCodeContainer, getNodeInfoContainer, getContinueButton, getResizer, getFileTabs, getNodeElement, getNodeText, getFirstNodeCodeElement, getNodeCodeElements, getHighlightableElements, getMainCodeWrapper, getCodeLines, getActiveCodeElement, createNodeDropdown, createNodeDropdownButton, createNodeElement, createNodeInfoLine, createNodeInfoAlert, createCodeLines, createCodeElement, createCodeWrapper, createFileTab, }; //# sourceMappingURL=components.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index 0fea133e6..ad6cbff2e 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -110,7 +110,6 @@ const highlightableOnClick = (node, event) => { const addHighlighingEventListeners = (root) => { const addListeners = (node) => { const highlightableElements = getHighlightableElements(node.id); - console.log(highlightableElements); for (const element of highlightableElements) { element.addEventListener('mouseover', event => highlightableOnMouseOver(node, event)); element.addEventListener('mouseout', event => highlightableOnMouseOut(node, event)); From 5215ce99c96fa8c0bcb8f9be62ee7bae144dfaeb Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 21:36:24 +0100 Subject: [PATCH 117/136] Refactor communication.ts --- .../visualization/public/js/communication.ts | 37 ++++++++++--------- .../visualization/public/js/communication.js | 24 ++++++------ 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts index 585bbf0da..188e4ac57 100644 --- a/Lara-JS/src-api/visualization/public/js/communication.ts +++ b/Lara-JS/src-api/visualization/public/js/communication.ts @@ -3,30 +3,31 @@ import { getContinueButton } from "./components.js"; import { addFile, clearFiles, selectFile } from "./files.js"; import { addHighlighingEventListeners } from "./visualization.js"; -const webSocketOnMessage = (message: MessageEvent): void => { - const continueButton = getContinueButton(); - const data = parseMessage(message); +const onUpdate = (data: any): void => { + const buttonDisabled = getContinueButton().disabled; - switch (data.message) { - case 'update': - const { code, ast } = data; - const buttonDisabled = continueButton.disabled; - - continueButton.disabled = true; + getContinueButton().disabled = true; - initCodeContainer(); + initCodeContainer(); + clearFiles(); + for (const [filename, filecode] of Object.entries(data.code)) + addFile(filename, filecode as string); + + importAst(data.ast); + addHighlighingEventListeners(data.ast); - clearFiles(); - for (const [filename, filecode] of Object.entries(code)) - addFile(filename, filecode as string); - - importAst(ast); - addHighlighingEventListeners(ast); + selectFile(Object.keys(data.code)[0]); - selectFile(Object.keys(code)[0]); + getContinueButton().disabled = buttonDisabled; +} - continueButton.disabled = buttonDisabled; +const webSocketOnMessage = (message: MessageEvent): void => { + const continueButton = getContinueButton(); + const data = parseMessage(message); + switch (data.message) { + case 'update': + onUpdate(data); break; case 'wait': diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js index d9be93429..d806a3576 100644 --- a/LaraApi/src-lara/visualization/public/js/communication.js +++ b/LaraApi/src-lara/visualization/public/js/communication.js @@ -2,22 +2,24 @@ import { importAst, initCodeContainer } from "./ast-import.js"; import { getContinueButton } from "./components.js"; import { addFile, clearFiles, selectFile } from "./files.js"; import { addHighlighingEventListeners } from "./visualization.js"; +const onUpdate = (data) => { + const buttonDisabled = getContinueButton().disabled; + getContinueButton().disabled = true; + initCodeContainer(); + clearFiles(); + for (const [filename, filecode] of Object.entries(data.code)) + addFile(filename, filecode); + importAst(data.ast); + addHighlighingEventListeners(data.ast); + selectFile(Object.keys(data.code)[0]); + getContinueButton().disabled = buttonDisabled; +}; const webSocketOnMessage = (message) => { const continueButton = getContinueButton(); const data = parseMessage(message); switch (data.message) { case 'update': - const { code, ast } = data; - const buttonDisabled = continueButton.disabled; - continueButton.disabled = true; - initCodeContainer(); - clearFiles(); - for (const [filename, filecode] of Object.entries(code)) - addFile(filename, filecode); - importAst(ast); - addHighlighingEventListeners(ast); - selectFile(Object.keys(code)[0]); - continueButton.disabled = buttonDisabled; + onUpdate(data); break; case 'wait': continueButton.disabled = false; From 53c2bfc45c9a3d9be6d5d29ac29d456173b4ea5f Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 21:47:00 +0100 Subject: [PATCH 118/136] Refactor files.ts --- .../visualization/public/js/ast-import.ts | 2 +- .../visualization/public/js/components.ts | 21 +++++++++- .../src-api/visualization/public/js/files.ts | 39 ++++++++++--------- .../visualization/public/js/components.js | 15 +++++-- .../src-lara/visualization/public/js/files.js | 28 ++++++------- 5 files changed, 67 insertions(+), 38 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index af3add2b0..d4eb7f2ea 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -1,4 +1,4 @@ -import { countChar, createIcon, } from './utils.js'; +import { countChar } from './utils.js'; import JoinPoint from './ToolJoinPoint.js'; import { createCodeElement, createCodeLines, createCodeWrapper, createNodeDropdown, createNodeDropdownButton, createNodeElement, getActiveCodeElement, getAstContainer, getCodeContainer, getCodeLines, getMainCodeWrapper } from './components.js'; diff --git a/Lara-JS/src-api/visualization/public/js/components.ts b/Lara-JS/src-api/visualization/public/js/components.ts index b6e61514a..0e5ddfa53 100644 --- a/Lara-JS/src-api/visualization/public/js/components.ts +++ b/Lara-JS/src-api/visualization/public/js/components.ts @@ -75,18 +75,32 @@ const getHighlightableElements = (nodeId: string): HTMLElement[] => { return [nodeText, ...nodeCodeElements]; }; + const getMainCodeWrapper = (): HTMLPreElement | null => { - return getCodeContainer().querySelector('pre.code-wrapper'); + return getCodeContainer().querySelector('.code-wrapper'); }; const getCodeLines = (): HTMLPreElement | null => { - return getCodeContainer().querySelector('pre.lines'); + return getCodeContainer().querySelector('.lines'); }; const getActiveCodeElement = (): HTMLElement | null => { return getMainCodeWrapper()?.querySelector('code.active') ?? null; }; +const getFileCodeElement = (filename: string): HTMLElement | null => { + return getCodeContainer().querySelector(`code[data-filepath="${filename}"]`); +}; + + +const getFileTab = (filepath: string): HTMLButtonElement | null => { + return getFileTabs().querySelector(`.file-tab[data-filepath="${filepath}"]`); +}; + +const getActiveFileTab = (): HTMLButtonElement | null => { + return getFileTabs().querySelector('.file-tab.active'); +}; + const createNodeDropdown = (nodeId: string): HTMLDivElement => { const dropdown = document.createElement('div'); @@ -206,6 +220,9 @@ export { getMainCodeWrapper, getCodeLines, getActiveCodeElement, + getFileCodeElement, + getFileTab, + getActiveFileTab, createNodeDropdown, createNodeDropdownButton, createNodeElement, diff --git a/Lara-JS/src-api/visualization/public/js/files.ts b/Lara-JS/src-api/visualization/public/js/files.ts index d9bbd94b4..2a5818be8 100644 --- a/Lara-JS/src-api/visualization/public/js/files.ts +++ b/Lara-JS/src-api/visualization/public/js/files.ts @@ -1,5 +1,5 @@ import { addCode, updateLines } from "./ast-import.js"; -import { createFileTab, getCodeContainer, getFileTabs } from "./components.js"; +import { createFileTab, getActiveCodeElement, getActiveFileTab, getFileCodeElement, getFileTab, getFileTabs, getMainCodeWrapper } from "./components.js"; let selectedFilepath: string | null = null; @@ -11,41 +11,42 @@ const addFile = (path: string, code: string): void => { const fileTabs = getFileTabs(); fileTabs.appendChild(fileTab); -} +}; const clearFiles = (): void => { + const codeWrapper = getMainCodeWrapper(); + if (!codeWrapper) + throw new Error('Code container not initialized'); + const fileTabs = getFileTabs(); fileTabs.innerHTML = ''; - - const codeContainer = getCodeContainer(); - codeContainer.querySelector('pre')!.innerHTML = ''; + codeWrapper.innerHTML = ''; selectedFilepath = null; -} +}; const selectFile = (filepath: string): void => { - const fileTabs = getFileTabs(); + const fileTab = getFileTab(filepath); + if (!fileTab) + throw new Error(`File "${filepath}" not found`); if (filepath !== selectedFilepath) { - const codeContainer = getCodeContainer(); + const activeFileTab = getActiveFileTab(); + if (activeFileTab) + activeFileTab.classList.remove('active'); + fileTab.classList.add('active'); - const selectedTab = fileTabs.querySelector(`.file-tab[data-filepath="${filepath}"]`)!; - if (selectedTab === null) - throw Error(`File "${filepath}" not found`); - - fileTabs.querySelector('.file-tab.active')?.classList.remove('active'); - selectedTab.classList.add('active'); - - const fileCode = codeContainer.querySelector(`code[data-filepath="${filepath}"]`)!; - const activeCode = codeContainer.querySelector('code.active'); + const activeCode = getActiveCodeElement(); if (activeCode) activeCode.classList.remove('active'); - fileCode.classList.add('active'); + + const fileCodeElement = getFileCodeElement(filepath)!; + fileCodeElement.classList.add('active'); updateLines(); selectedFilepath = filepath; } -} +}; export { addFile, clearFiles, selectFile }; diff --git a/LaraApi/src-lara/visualization/public/js/components.js b/LaraApi/src-lara/visualization/public/js/components.js index 06b94e50a..79f65104f 100644 --- a/LaraApi/src-lara/visualization/public/js/components.js +++ b/LaraApi/src-lara/visualization/public/js/components.js @@ -62,14 +62,23 @@ const getHighlightableElements = (nodeId) => { return [nodeText, ...nodeCodeElements]; }; const getMainCodeWrapper = () => { - return getCodeContainer().querySelector('pre.code-wrapper'); + return getCodeContainer().querySelector('.code-wrapper'); }; const getCodeLines = () => { - return getCodeContainer().querySelector('pre.lines'); + return getCodeContainer().querySelector('.lines'); }; const getActiveCodeElement = () => { return getMainCodeWrapper()?.querySelector('code.active') ?? null; }; +const getFileCodeElement = (filename) => { + return getCodeContainer().querySelector(`code[data-filepath="${filename}"]`); +}; +const getFileTab = (filepath) => { + return getFileTabs().querySelector(`.file-tab[data-filepath="${filepath}"]`); +}; +const getActiveFileTab = () => { + return getFileTabs().querySelector('.file-tab.active'); +}; const createNodeDropdown = (nodeId) => { const dropdown = document.createElement('div'); dropdown.classList.add('ast-node-dropdown'); @@ -149,5 +158,5 @@ const createFileTab = (filepath) => { fileTab.textContent = filepath !== '' ? filepath.slice(filepath.lastIndexOf('/') + 1) : ''; return fileTab; }; -export { getAstContainer, getCodeContainer, getNodeInfoContainer, getContinueButton, getResizer, getFileTabs, getNodeElement, getNodeText, getFirstNodeCodeElement, getNodeCodeElements, getHighlightableElements, getMainCodeWrapper, getCodeLines, getActiveCodeElement, createNodeDropdown, createNodeDropdownButton, createNodeElement, createNodeInfoLine, createNodeInfoAlert, createCodeLines, createCodeElement, createCodeWrapper, createFileTab, }; +export { getAstContainer, getCodeContainer, getNodeInfoContainer, getContinueButton, getResizer, getFileTabs, getNodeElement, getNodeText, getFirstNodeCodeElement, getNodeCodeElements, getHighlightableElements, getMainCodeWrapper, getCodeLines, getActiveCodeElement, getFileCodeElement, getFileTab, getActiveFileTab, createNodeDropdown, createNodeDropdownButton, createNodeElement, createNodeInfoLine, createNodeInfoAlert, createCodeLines, createCodeElement, createCodeWrapper, createFileTab, }; //# sourceMappingURL=components.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/files.js b/LaraApi/src-lara/visualization/public/js/files.js index cd654b9fa..be995ccaa 100644 --- a/LaraApi/src-lara/visualization/public/js/files.js +++ b/LaraApi/src-lara/visualization/public/js/files.js @@ -1,5 +1,5 @@ import { addCode, updateLines } from "./ast-import.js"; -import { createFileTab, getCodeContainer, getFileTabs } from "./components.js"; +import { createFileTab, getActiveCodeElement, getActiveFileTab, getFileCodeElement, getFileTab, getFileTabs, getMainCodeWrapper } from "./components.js"; let selectedFilepath = null; const addFile = (path, code) => { addCode(code, path); @@ -9,26 +9,28 @@ const addFile = (path, code) => { fileTabs.appendChild(fileTab); }; const clearFiles = () => { + const codeWrapper = getMainCodeWrapper(); + if (!codeWrapper) + throw new Error('Code container not initialized'); const fileTabs = getFileTabs(); fileTabs.innerHTML = ''; - const codeContainer = getCodeContainer(); - codeContainer.querySelector('pre').innerHTML = ''; + codeWrapper.innerHTML = ''; selectedFilepath = null; }; const selectFile = (filepath) => { - const fileTabs = getFileTabs(); + const fileTab = getFileTab(filepath); + if (!fileTab) + throw new Error(`File "${filepath}" not found`); if (filepath !== selectedFilepath) { - const codeContainer = getCodeContainer(); - const selectedTab = fileTabs.querySelector(`.file-tab[data-filepath="${filepath}"]`); - if (selectedTab === null) - throw Error(`File "${filepath}" not found`); - fileTabs.querySelector('.file-tab.active')?.classList.remove('active'); - selectedTab.classList.add('active'); - const fileCode = codeContainer.querySelector(`code[data-filepath="${filepath}"]`); - const activeCode = codeContainer.querySelector('code.active'); + const activeFileTab = getActiveFileTab(); + if (activeFileTab) + activeFileTab.classList.remove('active'); + fileTab.classList.add('active'); + const activeCode = getActiveCodeElement(); if (activeCode) activeCode.classList.remove('active'); - fileCode.classList.add('active'); + const fileCodeElement = getFileCodeElement(filepath); + fileCodeElement.classList.add('active'); updateLines(); selectedFilepath = filepath; } From 64bc8c9efb0013ebeda4e45861a83a65ed003ce0 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 21:51:09 +0100 Subject: [PATCH 119/136] Refactor main.ts --- Lara-JS/src-api/visualization/public/js/main.ts | 14 +++++++++----- .../src-lara/visualization/public/js/ast-import.js | 2 +- LaraApi/src-lara/visualization/public/js/main.js | 11 +++++++---- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/main.ts b/Lara-JS/src-api/visualization/public/js/main.ts index b70ef3aed..56c10c62a 100644 --- a/Lara-JS/src-api/visualization/public/js/main.ts +++ b/Lara-JS/src-api/visualization/public/js/main.ts @@ -2,16 +2,20 @@ import { continueButtonOnClick, getWebSocket } from "./communication.js"; import { getContinueButton } from "./components.js"; import { addDividerEventListeners } from "./visualization.js"; +const setupEventListeners = (ws: WebSocket): void => { + const continueButton = getContinueButton(); + continueButton.addEventListener('click', () => continueButtonOnClick(ws)); + + addDividerEventListeners(); +} + (() => { let ws: WebSocket; const setupWebSocket = () => { ws = getWebSocket(); - ws.addEventListener('close', () => setupWebSocket()); + ws.addEventListener('close', () => setTimeout(setupWebSocket, 1000)); }; setupWebSocket(); - const continueButton = getContinueButton(); - continueButton.addEventListener('click', () => continueButtonOnClick(ws)); - - addDividerEventListeners(); + setupEventListeners(ws!); })(); diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index a7411b20e..c33bc3d20 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -1,4 +1,4 @@ -import { countChar, } from './utils.js'; +import { countChar } from './utils.js'; import { createCodeElement, createCodeLines, createCodeWrapper, createNodeDropdown, createNodeDropdownButton, createNodeElement, getActiveCodeElement, getAstContainer, getCodeContainer, getCodeLines, getMainCodeWrapper } from './components.js'; const updateLines = () => { const codeLines = getCodeLines(); diff --git a/LaraApi/src-lara/visualization/public/js/main.js b/LaraApi/src-lara/visualization/public/js/main.js index 4fc35ce5b..6932dbba5 100644 --- a/LaraApi/src-lara/visualization/public/js/main.js +++ b/LaraApi/src-lara/visualization/public/js/main.js @@ -1,15 +1,18 @@ import { continueButtonOnClick, getWebSocket } from "./communication.js"; import { getContinueButton } from "./components.js"; import { addDividerEventListeners } from "./visualization.js"; +const setupEventListeners = (ws) => { + const continueButton = getContinueButton(); + continueButton.addEventListener('click', () => continueButtonOnClick(ws)); + addDividerEventListeners(); +}; (() => { let ws; const setupWebSocket = () => { ws = getWebSocket(); - ws.addEventListener('close', () => setupWebSocket()); + ws.addEventListener('close', () => setTimeout(setupWebSocket)); }; setupWebSocket(); - const continueButton = getContinueButton(); - continueButton.addEventListener('click', () => continueButtonOnClick(ws)); - addDividerEventListeners(); + setupEventListeners(ws); })(); //# sourceMappingURL=main.js.map \ No newline at end of file From 37aae5b7af98bc2a58f73c7c1b0f4074149b0d2f Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 22:44:11 +0100 Subject: [PATCH 120/136] Refactor GenericVisualizationTool --- .../visualization/GenericVisualizationTool.ts | 133 +++++++++--------- .../visualization/GenericVisualizationTool.js | 110 ++++++++------- .../src-lara/visualization/public/js/main.js | 2 +- 3 files changed, 127 insertions(+), 118 deletions(-) diff --git a/Lara-JS/src-api/visualization/GenericVisualizationTool.ts b/Lara-JS/src-api/visualization/GenericVisualizationTool.ts index 8408c75df..89c55e6f0 100644 --- a/Lara-JS/src-api/visualization/GenericVisualizationTool.ts +++ b/Lara-JS/src-api/visualization/GenericVisualizationTool.ts @@ -4,39 +4,46 @@ import path from 'path'; import { fileURLToPath } from 'url'; import WebSocket, { WebSocketServer } from 'ws'; import { AddressInfo } from 'net'; +import child from 'child_process'; -import { LaraJoinPoint, wrapJoinPoint } from '../LaraJoinPoint.js'; +import { LaraJoinPoint } from '../LaraJoinPoint.js'; import JoinPoints from '../weaver/JoinPoints.js'; -import GenericAstConverter from './GenericAstConverter.js'; +import GenericAstConverter, { FilesCode } from './GenericAstConverter.js'; +import ToolJoinPoint from './public/js/ToolJoinPoint.js'; export default abstract class GenericVisualizationTool { - private hostname: string | undefined; - private port: number | undefined; - private wss: WebSocketServer | undefined; - private serverClosed: boolean = false; - private astRoot: LaraJoinPoint | undefined; + #hostname: string | undefined; + #port: number | undefined; + #wss: WebSocketServer | undefined; + #serverClosed: boolean = false; + #toolAst: ToolJoinPoint | undefined; + #prettyHtmlCode: FilesCode | undefined; public isLaunched(): boolean { - return this.wss !== undefined && this.serverClosed === false; + return this.#wss !== undefined && this.#serverClosed === false; } - public getHostname(): string | undefined { - return this.hostname; + get hostname(): string | undefined { + return this.#hostname; } - public getPort(): number | undefined { - return this.port; + get port(): number | undefined { + return this.#port; + } + + get url(): string | undefined { + return this.#hostname && this.#port ? `http://${this.#hostname}:${this.#port}` : undefined; } private onWssError(error: NodeJS.ErrnoException): void { switch (error.code) { case 'EADDRINUSE': - console.error(`[server]: Port ${this.port} is already in use`); + console.error(`[server]: Port ${this.#port} is already in use`); break; case 'EACCES': - console.error(`[server]: Permission denied to use port ${this.port}`); + console.error(`[server]: Permission denied to use port ${this.#port}`); break; default: @@ -44,56 +51,53 @@ export default abstract class GenericVisualizationTool { break; }; - this.wss!.close(); + this.#wss!.close(); + } + + private updateAstAndCode(astRoot: LaraJoinPoint): void { + const astConverter = this.getAstConverter(); + astConverter.updateAst(); + + this.#toolAst = astConverter.getToolAst(astRoot); + this.#prettyHtmlCode = astConverter.getPrettyHtmlCode(astRoot); + } + + private openBrowser(url: string): void { + const command = process.platform == 'darwin' ? 'open' : process.platform == 'win32'? 'start' : 'xdg-open'; + child.exec(`${command} ${url}`); } public async launch(hostname: string = '127.0.0.1', port?: number, astRoot: LaraJoinPoint = JoinPoints.root()): Promise { if (this.isLaunched()) { - console.warn(`Visualization tool is already running at http://${this.hostname}:${this.port}`); + console.warn(`Visualization tool is already running at ${this.url}`); return; } - this.astRoot = astRoot; - const app = express(); const server = http.createServer(app); - this.wss = new WebSocketServer({ server: server }); + this.#wss = new WebSocketServer({ server: server }); const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); app.use(express.static(path.join(dirname, 'public'))); + + this.updateAstAndCode(astRoot); + this.#wss.on('connection', ws => this.updateClient(ws)); - this.wss.on('connection', (ws: WebSocket) => { - console.log('[server]: Client connected'); - - ws.on('message', (message: string) => { - console.log(`[server]: Received message => ${message}`); - }); - - ws.addEventListener('close', () => { - console.log('[server]: Client disconnected'); - }); - }); // TODO: Remove this - - this.wss.on('connection', ws => this.updateClient(ws)); - - this.wss.on('close', () => { - this.serverClosed = true; - }); - - this.wss.on('error', error => this.onWssError(error)); + this.#wss.on('close', () => { this.#serverClosed = true; }); + this.#wss.on('error', error => this.onWssError(error)); return new Promise(res => { server.listen(port ?? 0, hostname, () => { const addressInfo = server.address() as AddressInfo; - this.hostname = addressInfo.address; - this.port = addressInfo.port; - this.serverClosed = false; + this.#hostname = addressInfo.address; + this.#port = addressInfo.port; + this.#serverClosed = false; - console.log(`\nVisualization tool is running at http://${this.hostname}:${this.port}\n`); - // child.exec(`xdg-open http://${this.host}:${this.port}`); - // TODO: See if opening automatically is a good idea + console.log(`\nVisualization tool is running at ${this.url}\n`); + this.openBrowser(this.url!); + res(); }); }); @@ -104,7 +108,7 @@ export default abstract class GenericVisualizationTool { } private sendToAllClients(data: any): void { - this.wss!.clients.forEach(ws => this.sendToClient(ws, data)); + this.#wss!.clients.forEach(ws => this.sendToClient(ws, data)); } private verifyToolIsRunning(): void { @@ -113,17 +117,23 @@ export default abstract class GenericVisualizationTool { } } - public async waitForTool(): Promise { - this.verifyToolIsRunning(); + private updateClient(ws: WebSocket): void { + this.sendToClient(ws, { + message: 'update', + ast: this.#toolAst!, + code: this.#prettyHtmlCode!, + }); + } + private async waitForTool(): Promise { return new Promise(res => { let placeClientOnWait: (ws: WebSocket) => void; const waitOnMessage = (message: string) => { const data = JSON.parse(message); if (data.message === 'continue') { - this.wss!.clients.forEach(ws => { - this.wss!.off('connection', placeClientOnWait); + this.#wss!.clients.forEach(ws => { + this.#wss!.off('connection', placeClientOnWait); ws.off('message', waitOnMessage); }); @@ -137,27 +147,22 @@ export default abstract class GenericVisualizationTool { this.sendToClient(ws, { message: 'wait' }); } - this.wss!.clients.forEach(placeClientOnWait); - this.wss!.on('connection', placeClientOnWait); + this.#wss!.clients.forEach(placeClientOnWait); + this.#wss!.on('connection', placeClientOnWait); }); } - private updateClient(ws: WebSocket): void { - const astConverter = this.getAstConverter(); + public async visualize(astRoot: LaraJoinPoint = JoinPoints.root()): Promise { + this.verifyToolIsRunning(); + const astConverter = this.getAstConverter(); astConverter.updateAst(); - this.sendToClient(ws, { - message: 'update', - ast: astConverter.getToolAst(this.astRoot!).toJson(), - code: astConverter.getPrettyHtmlCode(this.astRoot!), - }); - } - - public update(astRoot: LaraJoinPoint = JoinPoints.root()): void { - this.verifyToolIsRunning(); + + this.updateAstAndCode(astRoot); + this.#wss!.clients.forEach(ws => this.updateClient(ws)); - this.astRoot = astRoot; - this.wss!.clients.forEach(ws => this.updateClient(ws)); + console.log(`\nVisualization tool is still running at${this.url}\n`); + await this.waitForTool(); } protected abstract getAstConverter(): GenericAstConverter; diff --git a/LaraApi/src-lara/visualization/GenericVisualizationTool.js b/LaraApi/src-lara/visualization/GenericVisualizationTool.js index b965603c4..813d2aec4 100644 --- a/LaraApi/src-lara/visualization/GenericVisualizationTool.js +++ b/LaraApi/src-lara/visualization/GenericVisualizationTool.js @@ -3,72 +3,75 @@ import http from 'http'; import path from 'path'; import { fileURLToPath } from 'url'; import { WebSocketServer } from 'ws'; +import child from 'child_process'; import JoinPoints from '../weaver/JoinPoints.js'; export default class GenericVisualizationTool { - hostname; - port; - wss; - serverClosed = false; - astRoot; + #hostname; + #port; + #wss; + #serverClosed = false; + #toolAst; + #prettyHtmlCode; isLaunched() { - return this.wss !== undefined && this.serverClosed === false; + return this.#wss !== undefined && this.#serverClosed === false; } - getHostname() { - return this.hostname; + get hostname() { + return this.#hostname; } - getPort() { - return this.port; + get port() { + return this.#port; + } + get url() { + return this.#hostname && this.#port ? `http://${this.#hostname}:${this.#port}` : undefined; } onWssError(error) { switch (error.code) { case 'EADDRINUSE': - console.error(`[server]: Port ${this.port} is already in use`); + console.error(`[server]: Port ${this.#port} is already in use`); break; case 'EACCES': - console.error(`[server]: Permission denied to use port ${this.port}`); + console.error(`[server]: Permission denied to use port ${this.#port}`); break; default: console.error(`[server]: Unknown error occurred: ${error.message}`); break; } ; - this.wss.close(); + this.#wss.close(); + } + updateAstAndCode(astRoot) { + const astConverter = this.getAstConverter(); + astConverter.updateAst(); + this.#toolAst = astConverter.getToolAst(astRoot); + this.#prettyHtmlCode = astConverter.getPrettyHtmlCode(astRoot); + } + openBrowser(url) { + const command = process.platform == 'darwin' ? 'open' : process.platform == 'win32' ? 'start' : 'xdg-open'; + child.exec(`${command} ${url}`); } async launch(hostname = '127.0.0.1', port, astRoot = JoinPoints.root()) { if (this.isLaunched()) { - console.warn(`Visualization tool is already running at http://${this.hostname}:${this.port}`); + console.warn(`Visualization tool is already running at ${this.url}`); return; } - this.astRoot = astRoot; const app = express(); const server = http.createServer(app); - this.wss = new WebSocketServer({ server: server }); + this.#wss = new WebSocketServer({ server: server }); const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); app.use(express.static(path.join(dirname, 'public'))); - this.wss.on('connection', (ws) => { - console.log('[server]: Client connected'); - ws.on('message', (message) => { - console.log(`[server]: Received message => ${message}`); - }); - ws.addEventListener('close', () => { - console.log('[server]: Client disconnected'); - }); - }); // TODO: Remove this - this.wss.on('connection', ws => this.updateClient(ws)); - this.wss.on('close', () => { - this.serverClosed = true; - }); - this.wss.on('error', error => this.onWssError(error)); + this.updateAstAndCode(astRoot); + this.#wss.on('connection', ws => this.updateClient(ws)); + this.#wss.on('close', () => { this.#serverClosed = true; }); + this.#wss.on('error', error => this.onWssError(error)); return new Promise(res => { server.listen(port ?? 0, hostname, () => { const addressInfo = server.address(); - this.hostname = addressInfo.address; - this.port = addressInfo.port; - this.serverClosed = false; - console.log(`\nVisualization tool is running at http://${this.hostname}:${this.port}\n`); - // child.exec(`xdg-open http://${this.host}:${this.port}`); - // TODO: See if opening automatically is a good idea + this.#hostname = addressInfo.address; + this.#port = addressInfo.port; + this.#serverClosed = false; + console.log(`\nVisualization tool is running at ${this.url}\n`); + this.openBrowser(this.url); res(); }); }); @@ -77,22 +80,28 @@ export default class GenericVisualizationTool { ws.send(JSON.stringify(data)); } sendToAllClients(data) { - this.wss.clients.forEach(ws => this.sendToClient(ws, data)); + this.#wss.clients.forEach(ws => this.sendToClient(ws, data)); } verifyToolIsRunning() { if (!this.isLaunched()) { throw Error('Visualization tool is not running'); } } + updateClient(ws) { + this.sendToClient(ws, { + message: 'update', + ast: this.#toolAst, + code: this.#prettyHtmlCode, + }); + } async waitForTool() { - this.verifyToolIsRunning(); return new Promise(res => { let placeClientOnWait; const waitOnMessage = (message) => { const data = JSON.parse(message); if (data.message === 'continue') { - this.wss.clients.forEach(ws => { - this.wss.off('connection', placeClientOnWait); + this.#wss.clients.forEach(ws => { + this.#wss.off('connection', placeClientOnWait); ws.off('message', waitOnMessage); }); this.sendToAllClients({ message: 'continue' }); @@ -103,23 +112,18 @@ export default class GenericVisualizationTool { ws.on('message', waitOnMessage); this.sendToClient(ws, { message: 'wait' }); }; - this.wss.clients.forEach(placeClientOnWait); - this.wss.on('connection', placeClientOnWait); + this.#wss.clients.forEach(placeClientOnWait); + this.#wss.on('connection', placeClientOnWait); }); } - updateClient(ws) { + async visualize(astRoot = JoinPoints.root()) { + this.verifyToolIsRunning(); const astConverter = this.getAstConverter(); astConverter.updateAst(); - this.sendToClient(ws, { - message: 'update', - ast: astConverter.getToolAst(this.astRoot).toJson(), - code: astConverter.getPrettyHtmlCode(this.astRoot), - }); - } - update(astRoot = JoinPoints.root()) { - this.verifyToolIsRunning(); - this.astRoot = astRoot; - this.wss.clients.forEach(ws => this.updateClient(ws)); + this.updateAstAndCode(astRoot); + this.#wss.clients.forEach(ws => this.updateClient(ws)); + console.log(`\nVisualization tool is still running at${this.url}\n`); + await this.waitForTool(); } } //# sourceMappingURL=GenericVisualizationTool.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/main.js b/LaraApi/src-lara/visualization/public/js/main.js index 6932dbba5..3ed717b0f 100644 --- a/LaraApi/src-lara/visualization/public/js/main.js +++ b/LaraApi/src-lara/visualization/public/js/main.js @@ -10,7 +10,7 @@ const setupEventListeners = (ws) => { let ws; const setupWebSocket = () => { ws = getWebSocket(); - ws.addEventListener('close', () => setTimeout(setupWebSocket)); + ws.addEventListener('close', () => setTimeout(setupWebSocket, 1000)); }; setupWebSocket(); setupEventListeners(ws); From 35aae09642f3e5086a09cae3b513b392218f2b8e Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 23:43:43 +0100 Subject: [PATCH 121/136] Refactor GenericVisualizationTool.ts --- .../visualization/GenericVisualizationTool.ts | 67 +++++++------------ .../visualization/GenericVisualizationTool.js | 51 +++++--------- 2 files changed, 43 insertions(+), 75 deletions(-) diff --git a/Lara-JS/src-api/visualization/GenericVisualizationTool.ts b/Lara-JS/src-api/visualization/GenericVisualizationTool.ts index 89c55e6f0..59b5ef97a 100644 --- a/Lara-JS/src-api/visualization/GenericVisualizationTool.ts +++ b/Lara-JS/src-api/visualization/GenericVisualizationTool.ts @@ -11,6 +11,11 @@ import JoinPoints from '../weaver/JoinPoints.js'; import GenericAstConverter, { FilesCode } from './GenericAstConverter.js'; import ToolJoinPoint from './public/js/ToolJoinPoint.js'; +type VisualizationOptions = { + astRoot: LaraJoinPoint; + hostname: string; + port: number; +} export default abstract class GenericVisualizationTool { #hostname: string | undefined; @@ -20,7 +25,7 @@ export default abstract class GenericVisualizationTool { #toolAst: ToolJoinPoint | undefined; #prettyHtmlCode: FilesCode | undefined; - public isLaunched(): boolean { + get isLaunched(): boolean { return this.#wss !== undefined && this.#serverClosed === false; } @@ -36,6 +41,14 @@ export default abstract class GenericVisualizationTool { return this.#hostname && this.#port ? `http://${this.#hostname}:${this.#port}` : undefined; } + private updateAstAndCode(astRoot: LaraJoinPoint): void { + const astConverter = this.getAstConverter(); + astConverter.updateAst(); + + this.#toolAst = astConverter.getToolAst(astRoot); + this.#prettyHtmlCode = astConverter.getPrettyHtmlCode(astRoot); + } + private onWssError(error: NodeJS.ErrnoException): void { switch (error.code) { case 'EADDRINUSE': @@ -54,49 +67,25 @@ export default abstract class GenericVisualizationTool { this.#wss!.close(); } - private updateAstAndCode(astRoot: LaraJoinPoint): void { - const astConverter = this.getAstConverter(); - astConverter.updateAst(); - - this.#toolAst = astConverter.getToolAst(astRoot); - this.#prettyHtmlCode = astConverter.getPrettyHtmlCode(astRoot); - } - - private openBrowser(url: string): void { - const command = process.platform == 'darwin' ? 'open' : process.platform == 'win32'? 'start' : 'xdg-open'; - child.exec(`${command} ${url}`); - } - - public async launch(hostname: string = '127.0.0.1', port?: number, astRoot: LaraJoinPoint = JoinPoints.root()): Promise { - if (this.isLaunched()) { - console.warn(`Visualization tool is already running at ${this.url}`); - return; - } - + private async launch({ hostname, port }: VisualizationOptions): Promise { const app = express(); const server = http.createServer(app); this.#wss = new WebSocketServer({ server: server }); const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); - app.use(express.static(path.join(dirname, 'public'))); - this.updateAstAndCode(astRoot); this.#wss.on('connection', ws => this.updateClient(ws)); - this.#wss.on('close', () => { this.#serverClosed = true; }); this.#wss.on('error', error => this.onWssError(error)); return new Promise(res => { - server.listen(port ?? 0, hostname, () => { + server.listen(port, hostname, () => { const addressInfo = server.address() as AddressInfo; this.#hostname = addressInfo.address; this.#port = addressInfo.port; this.#serverClosed = false; - - console.log(`\nVisualization tool is running at ${this.url}\n`); - this.openBrowser(this.url!); res(); }); @@ -111,16 +100,10 @@ export default abstract class GenericVisualizationTool { this.#wss!.clients.forEach(ws => this.sendToClient(ws, data)); } - private verifyToolIsRunning(): void { - if (!this.isLaunched()) { - throw Error('Visualization tool is not running'); - } - } - private updateClient(ws: WebSocket): void { this.sendToClient(ws, { message: 'update', - ast: this.#toolAst!, + ast: this.#toolAst!.toJson(), code: this.#prettyHtmlCode!, }); } @@ -152,16 +135,16 @@ export default abstract class GenericVisualizationTool { }); } - public async visualize(astRoot: LaraJoinPoint = JoinPoints.root()): Promise { - this.verifyToolIsRunning(); + public async visualize(astRoot: LaraJoinPoint = JoinPoints.root(), port: number = 3000, hostname: string = '127.0.0.1'): Promise { + this.updateAstAndCode(astRoot!); - const astConverter = this.getAstConverter(); - astConverter.updateAst(); - - this.updateAstAndCode(astRoot); - this.#wss!.clients.forEach(ws => this.updateClient(ws)); + if (!this.isLaunched) { + await this.launch({astRoot, hostname, port}); + } else { + this.#wss!.clients.forEach(ws => this.updateClient(ws)); + } - console.log(`\nVisualization tool is still running at${this.url}\n`); + console.log(`\nVisualization tool is running at ${this.url}\n`); await this.waitForTool(); } diff --git a/LaraApi/src-lara/visualization/GenericVisualizationTool.js b/LaraApi/src-lara/visualization/GenericVisualizationTool.js index 813d2aec4..d66abc366 100644 --- a/LaraApi/src-lara/visualization/GenericVisualizationTool.js +++ b/LaraApi/src-lara/visualization/GenericVisualizationTool.js @@ -3,7 +3,6 @@ import http from 'http'; import path from 'path'; import { fileURLToPath } from 'url'; import { WebSocketServer } from 'ws'; -import child from 'child_process'; import JoinPoints from '../weaver/JoinPoints.js'; export default class GenericVisualizationTool { #hostname; @@ -12,7 +11,7 @@ export default class GenericVisualizationTool { #serverClosed = false; #toolAst; #prettyHtmlCode; - isLaunched() { + get isLaunched() { return this.#wss !== undefined && this.#serverClosed === false; } get hostname() { @@ -24,6 +23,12 @@ export default class GenericVisualizationTool { get url() { return this.#hostname && this.#port ? `http://${this.#hostname}:${this.#port}` : undefined; } + updateAstAndCode(astRoot) { + const astConverter = this.getAstConverter(); + astConverter.updateAst(); + this.#toolAst = astConverter.getToolAst(astRoot); + this.#prettyHtmlCode = astConverter.getPrettyHtmlCode(astRoot); + } onWssError(error) { switch (error.code) { case 'EADDRINUSE': @@ -39,39 +44,22 @@ export default class GenericVisualizationTool { ; this.#wss.close(); } - updateAstAndCode(astRoot) { - const astConverter = this.getAstConverter(); - astConverter.updateAst(); - this.#toolAst = astConverter.getToolAst(astRoot); - this.#prettyHtmlCode = astConverter.getPrettyHtmlCode(astRoot); - } - openBrowser(url) { - const command = process.platform == 'darwin' ? 'open' : process.platform == 'win32' ? 'start' : 'xdg-open'; - child.exec(`${command} ${url}`); - } - async launch(hostname = '127.0.0.1', port, astRoot = JoinPoints.root()) { - if (this.isLaunched()) { - console.warn(`Visualization tool is already running at ${this.url}`); - return; - } + async launch({ hostname, port }) { const app = express(); const server = http.createServer(app); this.#wss = new WebSocketServer({ server: server }); const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); app.use(express.static(path.join(dirname, 'public'))); - this.updateAstAndCode(astRoot); this.#wss.on('connection', ws => this.updateClient(ws)); this.#wss.on('close', () => { this.#serverClosed = true; }); this.#wss.on('error', error => this.onWssError(error)); return new Promise(res => { - server.listen(port ?? 0, hostname, () => { + server.listen(port, hostname, () => { const addressInfo = server.address(); this.#hostname = addressInfo.address; this.#port = addressInfo.port; this.#serverClosed = false; - console.log(`\nVisualization tool is running at ${this.url}\n`); - this.openBrowser(this.url); res(); }); }); @@ -82,15 +70,10 @@ export default class GenericVisualizationTool { sendToAllClients(data) { this.#wss.clients.forEach(ws => this.sendToClient(ws, data)); } - verifyToolIsRunning() { - if (!this.isLaunched()) { - throw Error('Visualization tool is not running'); - } - } updateClient(ws) { this.sendToClient(ws, { message: 'update', - ast: this.#toolAst, + ast: this.#toolAst.toJson(), code: this.#prettyHtmlCode, }); } @@ -116,13 +99,15 @@ export default class GenericVisualizationTool { this.#wss.on('connection', placeClientOnWait); }); } - async visualize(astRoot = JoinPoints.root()) { - this.verifyToolIsRunning(); - const astConverter = this.getAstConverter(); - astConverter.updateAst(); + async visualize(astRoot = JoinPoints.root(), port = 3000, hostname = '127.0.0.1') { this.updateAstAndCode(astRoot); - this.#wss.clients.forEach(ws => this.updateClient(ws)); - console.log(`\nVisualization tool is still running at${this.url}\n`); + if (!this.isLaunched) { + await this.launch({ astRoot, hostname, port }); + } + else { + this.#wss.clients.forEach(ws => this.updateClient(ws)); + } + console.log(`\nVisualization tool is running at ${this.url}\n`); await this.waitForTool(); } } From 140a9b6d28880397d3bca3eca5ee04582de083c7 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 25 Jul 2024 23:55:26 +0100 Subject: [PATCH 122/136] Create AstConverterUtils.ts --- .../visualization/AstConverterUtils.ts | 23 +++++++++++++++++++ .../up/fe/specs/lara/LaraApiJsResource.java | 1 + .../visualization/AstConverterUtils.js | 19 +++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 Lara-JS/src-api/visualization/AstConverterUtils.ts create mode 100644 LaraApi/src-lara/visualization/AstConverterUtils.js diff --git a/Lara-JS/src-api/visualization/AstConverterUtils.ts b/Lara-JS/src-api/visualization/AstConverterUtils.ts new file mode 100644 index 000000000..8c8695522 --- /dev/null +++ b/Lara-JS/src-api/visualization/AstConverterUtils.ts @@ -0,0 +1,23 @@ +const addIdentation = (code: string, indentation: number): string => { + return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); +} + +const escapeHtml = (text: string): string => { + const specialCharMap: { [char: string]: string } = { + '&': '&', + '<': '<', + '>': '>', + }; + + return text.replace(/[&<>]/g, (match) => specialCharMap[match]); +} + +const getSpanTags = (...attrs: string[]): string[] => { + return [``, '']; +} + +const getNodeCodeTags = (nodeId: string): string[] => { + return getSpanTags('class="node-code"', `data-node-id="${nodeId}"`); +} + +export { addIdentation, escapeHtml, getSpanTags, getNodeCodeTags }; \ No newline at end of file diff --git a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java index b0dbf900e..167eb26b0 100644 --- a/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java +++ b/LaraApi/src-java/pt/up/fe/specs/lara/LaraApiJsResource.java @@ -105,6 +105,7 @@ public enum LaraApiJsResource implements LaraResourceProvider { TIMEUNITS_JS("lara/util/TimeUnits.js"), TUPLEID_JS("lara/util/TupleId.js"), CYTOSCAPE_3_26_0_JS("libs/cytoscape-3.26.0.js"), + ASTCONVERTERUTILS_JS("visualization/AstConverterUtils.js"), GENERICASTCONVERTER_JS("visualization/GenericAstConverter.js"), GENERICVISUALIZATIONTOOL_JS("visualization/GenericVisualizationTool.js"), TOOLJOINPOINT_JS("visualization/public/js/ToolJoinPoint.js"), diff --git a/LaraApi/src-lara/visualization/AstConverterUtils.js b/LaraApi/src-lara/visualization/AstConverterUtils.js new file mode 100644 index 000000000..106fafdf5 --- /dev/null +++ b/LaraApi/src-lara/visualization/AstConverterUtils.js @@ -0,0 +1,19 @@ +const addIdentation = (code, indentation) => { + return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); +}; +const escapeHtml = (text) => { + const specialCharMap = { + '&': '&', + '<': '<', + '>': '>', + }; + return text.replace(/[&<>]/g, (match) => specialCharMap[match]); +}; +const getSpanTags = (...attrs) => { + return [``, '']; +}; +const getNodeCodeTags = (nodeId) => { + return getSpanTags('class="node-code"', `data-node-id="${nodeId}"`); +}; +export { addIdentation, escapeHtml, getSpanTags, getNodeCodeTags }; +//# sourceMappingURL=AstConverterUtils.js.map \ No newline at end of file From 66f9975c8bf81d652c4978c089d94682c8a37afa Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 26 Jul 2024 09:49:25 +0100 Subject: [PATCH 123/136] Create getSyntaxHighlightTags --- Lara-JS/src-api/visualization/AstConverterUtils.ts | 14 +++++++++----- .../src-lara/visualization/AstConverterUtils.js | 5 ++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Lara-JS/src-api/visualization/AstConverterUtils.ts b/Lara-JS/src-api/visualization/AstConverterUtils.ts index 8c8695522..955feea0b 100644 --- a/Lara-JS/src-api/visualization/AstConverterUtils.ts +++ b/Lara-JS/src-api/visualization/AstConverterUtils.ts @@ -1,6 +1,6 @@ const addIdentation = (code: string, indentation: number): string => { return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); -} +}; const escapeHtml = (text: string): string => { const specialCharMap: { [char: string]: string } = { @@ -10,14 +10,18 @@ const escapeHtml = (text: string): string => { }; return text.replace(/[&<>]/g, (match) => specialCharMap[match]); -} +}; const getSpanTags = (...attrs: string[]): string[] => { return [``, '']; -} +}; const getNodeCodeTags = (nodeId: string): string[] => { return getSpanTags('class="node-code"', `data-node-id="${nodeId}"`); -} +}; -export { addIdentation, escapeHtml, getSpanTags, getNodeCodeTags }; \ No newline at end of file +const getSyntaxHighlightTags = (type: 'comment' | 'keyword' | 'literal' | 'string' | 'type'): string[] => { + return getSpanTags(`class="${type}"`); +}; + +export { addIdentation, escapeHtml, getSpanTags, getNodeCodeTags, getSyntaxHighlightTags }; \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/AstConverterUtils.js b/LaraApi/src-lara/visualization/AstConverterUtils.js index 106fafdf5..9d4fc6eaf 100644 --- a/LaraApi/src-lara/visualization/AstConverterUtils.js +++ b/LaraApi/src-lara/visualization/AstConverterUtils.js @@ -15,5 +15,8 @@ const getSpanTags = (...attrs) => { const getNodeCodeTags = (nodeId) => { return getSpanTags('class="node-code"', `data-node-id="${nodeId}"`); }; -export { addIdentation, escapeHtml, getSpanTags, getNodeCodeTags }; +const getSyntaxHighlightTags = (type) => { + return getSpanTags(`class="${type}"`); +}; +export { addIdentation, escapeHtml, getSpanTags, getNodeCodeTags, getSyntaxHighlightTags }; //# sourceMappingURL=AstConverterUtils.js.map \ No newline at end of file From 4f45647690a0df2d3013f1bb021a03f5e442e928 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 26 Jul 2024 10:27:38 +0100 Subject: [PATCH 124/136] Write ast-import.ts documentation --- .../visualization/public/js/ast-import.ts | 27 +++++++++++++++++-- .../src-api/visualization/public/js/files.ts | 4 +-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index d4eb7f2ea..651fce8e2 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -2,6 +2,9 @@ import { countChar } from './utils.js'; import JoinPoint from './ToolJoinPoint.js'; import { createCodeElement, createCodeLines, createCodeWrapper, createNodeDropdown, createNodeDropdownButton, createNodeElement, getActiveCodeElement, getAstContainer, getCodeContainer, getCodeLines, getMainCodeWrapper } from './components.js'; +/** + * @brief Updates the line numbering of the code container. + */ const updateLines = (): void => { const codeLines = getCodeLines(); const codeWrapper = getMainCodeWrapper(); @@ -16,6 +19,9 @@ const updateLines = (): void => { codeLines.replaceWith(newCodeLines); }; +/** + * @brief Initializes the code container, by adding the code lines and the code wrapper. + */ const initCodeContainer = (): void => { const codeContainer = getCodeContainer(); codeContainer.innerHTML = ''; @@ -26,7 +32,13 @@ const initCodeContainer = (): void => { codeContainer.append(codeLines, codeWrapper); }; -const addCode = (code: string, filepath: string): void => { +/** + * @brief Adds a new hidden code element, with the given code, to the code container. + * + * @param code Code of the element + * @param filepath Path of the file + */ +const addFileCode = (code: string, filepath: string): void => { const codeWrapper = getMainCodeWrapper(); if (!codeWrapper) throw new Error('Code container not initialized'); @@ -36,6 +48,12 @@ const addCode = (code: string, filepath: string): void => { codeWrapper.appendChild(codeElement); }; +/** + * @brief Converts the AST to node HTML elements and their respective dropdowns. + * + * @param root Root of the AST + * @returns The resulting node HTML elements + */ const toNodeElements = (root: JoinPoint): DocumentFragment => { const fragment = new DocumentFragment(); @@ -61,6 +79,11 @@ const toNodeElements = (root: JoinPoint): DocumentFragment => { return fragment; }; +/** + * @brief Imports the AST to the AST container. + * + * @param astRoot Root of the AST + */ const importAst = (astRoot: JoinPoint): void => { const astContainer = getAstContainer(); @@ -69,4 +92,4 @@ const importAst = (astRoot: JoinPoint): void => { astContainer.appendChild(astFragment); }; -export { importAst, initCodeContainer, addCode, updateLines }; +export { importAst, initCodeContainer, addFileCode, updateLines }; diff --git a/Lara-JS/src-api/visualization/public/js/files.ts b/Lara-JS/src-api/visualization/public/js/files.ts index 2a5818be8..fa6931599 100644 --- a/Lara-JS/src-api/visualization/public/js/files.ts +++ b/Lara-JS/src-api/visualization/public/js/files.ts @@ -1,10 +1,10 @@ -import { addCode, updateLines } from "./ast-import.js"; +import { addFileCode, updateLines } from "./ast-import.js"; import { createFileTab, getActiveCodeElement, getActiveFileTab, getFileCodeElement, getFileTab, getFileTabs, getMainCodeWrapper } from "./components.js"; let selectedFilepath: string | null = null; const addFile = (path: string, code: string): void => { - addCode(code, path); + addFileCode(code, path); const fileTab = createFileTab(path); fileTab.addEventListener('click', () => selectFile(path)); From ee8967645b337dc8f3764c5a5c3af6ccf1c3020b Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 26 Jul 2024 10:35:10 +0100 Subject: [PATCH 125/136] Write documentation for communication.ts --- .../visualization/public/js/ast-import.ts | 7 ++++++- .../visualization/public/js/communication.ts | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index 651fce8e2..e4d5160cc 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -1,3 +1,8 @@ +/** + * @file ast-import.ts + * @brief Functions for importing the AST and code to the visualization. + */ + import { countChar } from './utils.js'; import JoinPoint from './ToolJoinPoint.js'; import { createCodeElement, createCodeLines, createCodeWrapper, createNodeDropdown, createNodeDropdownButton, createNodeElement, getActiveCodeElement, getAstContainer, getCodeContainer, getCodeLines, getMainCodeWrapper } from './components.js'; @@ -52,7 +57,7 @@ const addFileCode = (code: string, filepath: string): void => { * @brief Converts the AST to node HTML elements and their respective dropdowns. * * @param root Root of the AST - * @returns The resulting node HTML elements + * @return The resulting node HTML elements */ const toNodeElements = (root: JoinPoint): DocumentFragment => { const fragment = new DocumentFragment(); diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts index 188e4ac57..1b24f9c25 100644 --- a/Lara-JS/src-api/visualization/public/js/communication.ts +++ b/Lara-JS/src-api/visualization/public/js/communication.ts @@ -1,8 +1,20 @@ +/** + * @file communication.ts + * @brief Functions for communication with the server. + */ + import { importAst, initCodeContainer } from "./ast-import.js"; import { getContinueButton } from "./components.js"; import { addFile, clearFiles, selectFile } from "./files.js"; import { addHighlighingEventListeners } from "./visualization.js"; +/** + * @brief WebSocket message handler for the 'update' message. + * @details When executed, this function updates the code container and the AST + * with the new data. + * + * @param data Message data + */ const onUpdate = (data: any): void => { const buttonDisabled = getContinueButton().disabled; @@ -40,6 +52,12 @@ const webSocketOnMessage = (message: MessageEvent): void => { } }; +/** + * @brief Creates a WebSocket connection to the server, with the message event + * listener. + * + * @return WebSocket object + */ const getWebSocket = (): WebSocket => { const url = '/'; const ws = new WebSocket(url); From 6c8f4099cdfb444f2ddc8eaa5c307edcc814acda Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 26 Jul 2024 10:37:22 +0100 Subject: [PATCH 126/136] Write documentation for files.ts --- .../src-api/visualization/public/js/files.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Lara-JS/src-api/visualization/public/js/files.ts b/Lara-JS/src-api/visualization/public/js/files.ts index fa6931599..0654129e2 100644 --- a/Lara-JS/src-api/visualization/public/js/files.ts +++ b/Lara-JS/src-api/visualization/public/js/files.ts @@ -1,8 +1,19 @@ +/** + * @file files.ts + * @brief Functions for handling files in the visualization. + */ + import { addFileCode, updateLines } from "./ast-import.js"; import { createFileTab, getActiveCodeElement, getActiveFileTab, getFileCodeElement, getFileTab, getFileTabs, getMainCodeWrapper } from "./components.js"; let selectedFilepath: string | null = null; +/** + * @brief Adds a new file, with the respective file tab and (hidden) code, to the visualization. + * + * @param path Path of the file + * @param code File code + */ const addFile = (path: string, code: string): void => { addFileCode(code, path); @@ -13,6 +24,9 @@ const addFile = (path: string, code: string): void => { fileTabs.appendChild(fileTab); }; +/** + * @brief Clears all files from the code container. + */ const clearFiles = (): void => { const codeWrapper = getMainCodeWrapper(); if (!codeWrapper) @@ -25,7 +39,10 @@ const clearFiles = (): void => { selectedFilepath = null; }; - +/** + * @brief Selects a file, by making its code visible in the code container. + * @param filepath + */ const selectFile = (filepath: string): void => { const fileTab = getFileTab(filepath); if (!fileTab) From e06acdb79fe4cbc964c90dd8e516550aa4623ae7 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 26 Jul 2024 10:41:33 +0100 Subject: [PATCH 127/136] Write documentation for ToolJoinPoint.ts --- .../visualization/public/js/ToolJoinPoint.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts index f2c62e654..4a65db582 100644 --- a/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts +++ b/Lara-JS/src-api/visualization/public/js/ToolJoinPoint.ts @@ -17,30 +17,51 @@ export default class ToolJoinPoint { this.#children = children; } + /** + * @brief Returns the join point ID. + */ get id(): string { return this.#id; } + /** + * @brief Returns the type of join point. + */ get type(): string { return this.#type; } + /** + * @brief Returns the code of the join point. + */ get code(): string | undefined { return this.#code; } + /** + * @brief Returns the filepath of the file this join point belongs to. + */ get filepath(): string | undefined { return this.#filepath; } + /** + * @brief Returns extra information about the join point. + */ get info(): JoinPointInfo { return this.#info; } + /** + * @brief Returns the children of the join point. + */ get children(): ToolJoinPoint[] { return this.#children } + /** + * @brief Creates a new ToolJoinPoint object from a JSON object. + */ public static fromJson(json: any): ToolJoinPoint { return new ToolJoinPoint( json.id, @@ -52,6 +73,9 @@ export default class ToolJoinPoint { ); } + /** + * @brief Converts the ToolJoinPoint object to a JSON object. + */ public toJson(): any { return { id: this.#id, @@ -63,6 +87,9 @@ export default class ToolJoinPoint { }; } + /** + * @brief Clones the join point. + */ public clone(): ToolJoinPoint { return new ToolJoinPoint( this.#id, From dd23ba505ad5544d9cd2a49ab59bbc555e886502 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 26 Jul 2024 10:43:54 +0100 Subject: [PATCH 128/136] Write documentation for utils.ts --- .../visualization/public/js/components.ts | 12 ++++++++++-- .../src-api/visualization/public/js/utils.ts | 17 ++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/components.ts b/Lara-JS/src-api/visualization/public/js/components.ts index 0e5ddfa53..23932692d 100644 --- a/Lara-JS/src-api/visualization/public/js/components.ts +++ b/Lara-JS/src-api/visualization/public/js/components.ts @@ -1,5 +1,3 @@ -import { createIcon } from "./utils.js"; - const getAstContainer = (() => { const astContainer = document.querySelector('#ast-container'); if (!astContainer) { @@ -102,6 +100,15 @@ const getActiveFileTab = (): HTMLButtonElement | null => { }; +const createIcon = (name: string): HTMLElement => { + const icon = document.createElement('span'); + icon.classList.add('icon', 'material-symbols-outlined'); + icon.textContent = name; + + return icon; +} + + const createNodeDropdown = (nodeId: string): HTMLDivElement => { const dropdown = document.createElement('div'); dropdown.classList.add('ast-node-dropdown'); @@ -223,6 +230,7 @@ export { getFileCodeElement, getFileTab, getActiveFileTab, + createIcon, createNodeDropdown, createNodeDropdownButton, createNodeElement, diff --git a/Lara-JS/src-api/visualization/public/js/utils.ts b/Lara-JS/src-api/visualization/public/js/utils.ts index d9c2bcb14..ad882f2a4 100644 --- a/Lara-JS/src-api/visualization/public/js/utils.ts +++ b/Lara-JS/src-api/visualization/public/js/utils.ts @@ -1,3 +1,10 @@ +/** + * @brief Counts the number of occurrences of a character in a string. + * + * @param str String to search + * @param char Target character + * @return Number of occurrences of char in str + */ const countChar = (str: string, char: string): number => { let count = 0; for (const c of str) { @@ -7,12 +14,4 @@ const countChar = (str: string, char: string): number => { return count; } -const createIcon = (name: string): HTMLElement => { - const icon = document.createElement('span'); - icon.classList.add('icon', 'material-symbols-outlined'); - icon.textContent = name; - - return icon; -} - -export { countChar, createIcon }; \ No newline at end of file +export { countChar }; \ No newline at end of file From bea7433d55d6360a8c3508084f818f179b2e60dc Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 26 Jul 2024 12:11:03 +0100 Subject: [PATCH 129/136] Add documentation to visualization.ts --- .../visualization/public/js/ast-import.ts | 2 +- .../visualization/public/js/communication.ts | 2 +- .../src-api/visualization/public/js/utils.ts | 7 +++- .../visualization/public/js/visualization.ts | 38 +++++++++++++++++-- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/js/ast-import.ts b/Lara-JS/src-api/visualization/public/js/ast-import.ts index e4d5160cc..e27cf2fd3 100644 --- a/Lara-JS/src-api/visualization/public/js/ast-import.ts +++ b/Lara-JS/src-api/visualization/public/js/ast-import.ts @@ -57,7 +57,7 @@ const addFileCode = (code: string, filepath: string): void => { * @brief Converts the AST to node HTML elements and their respective dropdowns. * * @param root Root of the AST - * @return The resulting node HTML elements + * @returns The resulting node HTML elements */ const toNodeElements = (root: JoinPoint): DocumentFragment => { const fragment = new DocumentFragment(); diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts index 1b24f9c25..9cd9f9165 100644 --- a/Lara-JS/src-api/visualization/public/js/communication.ts +++ b/Lara-JS/src-api/visualization/public/js/communication.ts @@ -56,7 +56,7 @@ const webSocketOnMessage = (message: MessageEvent): void => { * @brief Creates a WebSocket connection to the server, with the message event * listener. * - * @return WebSocket object + * @returns WebSocket object */ const getWebSocket = (): WebSocket => { const url = '/'; diff --git a/Lara-JS/src-api/visualization/public/js/utils.ts b/Lara-JS/src-api/visualization/public/js/utils.ts index ad882f2a4..73ded8e7a 100644 --- a/Lara-JS/src-api/visualization/public/js/utils.ts +++ b/Lara-JS/src-api/visualization/public/js/utils.ts @@ -1,9 +1,14 @@ +/** + * @file utils.ts + * @brief Utility functions for the visualization tool. + */ + /** * @brief Counts the number of occurrences of a character in a string. * * @param str String to search * @param char Target character - * @return Number of occurrences of char in str + * @returns Number of occurrences of char in str */ const countChar = (str: string, char: string): number => { let count = 0; diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index b355223c7..e3bd575c3 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -1,7 +1,18 @@ +/** + * @file visualization.ts + * @brief Functions for handling the visualization behavior and events. + */ + import { createCodeElement, createCodeWrapper, createNodeInfoAlert, createNodeInfoLine, getAstContainer, getCodeContainer, getContinueButton, getFirstNodeCodeElement, getHighlightableElements, getNodeElement, getNodeInfoContainer, getNodeText, getResizer } from "./components.js"; import { selectFile } from "./files.js"; import JoinPoint from "./ToolJoinPoint.js"; +/** + * @brief Highlights the node with the given id. + * + * @param nodeId Node id + * @param strong If the highlight should use a strong color + */ const highlightNode = (nodeId: string, strong: boolean): void => { const nodeElement = getNodeElement(nodeId); if (!nodeElement) { @@ -22,6 +33,11 @@ const highlightNode = (nodeId: string, strong: boolean): void => { } }; +/** + * @brief Unhighlights the node with the given id. + * + * @param nodeId Node id + */ const unhighlightNode = (nodeId: string): void => { const nodeElement = getNodeElement(nodeId); if (!nodeElement) { @@ -42,6 +58,11 @@ const unhighlightNode = (nodeId: string): void => { } }; +/** + * @brief Shows the node info container with the given node information. + * + * @param node The target node + */ const showNodeInfo = (node: JoinPoint): void => { const nodeInfoContainer = getNodeInfoContainer(); nodeInfoContainer.style.display = 'block'; @@ -67,6 +88,9 @@ const showNodeInfo = (node: JoinPoint): void => { } }; +/** + * @brief Hides the node information container. + */ const hideNodeInfo = (): void => { const nodeInfoContainer = getNodeInfoContainer(); nodeInfoContainer.style.display = 'none'; @@ -134,6 +158,12 @@ const highlightableOnClick = (node: JoinPoint, event: Event): void => { showNodeInfo(node); }; +/** + * @brief Adds event listeners to all the highlightable elements relative to + * the nodes in the given AST. + * + * @param root Root of the AST + */ const addHighlighingEventListeners = (root: JoinPoint): void => { const addListeners = (node: JoinPoint) => { const highlightableElements = getHighlightableElements(node.id); @@ -161,8 +191,10 @@ const addHighlighingEventListeners = (root: JoinPoint): void => { addListeners(root); }; - -const addDividerEventListeners = (): void => { +/** + * @brief Adds event listeners to the resizer element. + */ +const addResizerEventListeners = (): void => { const resizer = getResizer(); const astContainer = getAstContainer(); const codeContainer = getCodeContainer(); @@ -197,4 +229,4 @@ const addDividerEventListeners = (): void => { }); }; -export { addHighlighingEventListeners, addDividerEventListeners }; +export { addHighlighingEventListeners, addResizerEventListeners }; From 06450f9510e6a717d44fd8d1ed27aeb0c64f72c1 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 26 Jul 2024 12:33:46 +0100 Subject: [PATCH 130/136] Write documentation for GenericVisualizationTool --- .../visualization/GenericVisualizationTool.ts | 96 ++++++++++++++++--- .../visualization/public/js/communication.ts | 4 +- .../visualization/public/js/components.ts | 2 +- .../src-api/visualization/public/js/main.ts | 4 +- .../src-api/visualization/public/js/utils.ts | 2 +- .../visualization/GenericVisualizationTool.js | 76 ++++++++++++++- .../visualization/public/js/ToolJoinPoint.js | 27 ++++++ .../visualization/public/js/ast-import.js | 31 +++++- .../visualization/public/js/communication.js | 17 ++++ .../visualization/public/js/components.js | 9 +- .../src-lara/visualization/public/js/files.js | 21 +++- .../src-lara/visualization/public/js/main.js | 4 +- .../src-lara/visualization/public/js/utils.js | 19 ++-- .../visualization/public/js/visualization.js | 36 ++++++- 14 files changed, 307 insertions(+), 41 deletions(-) diff --git a/Lara-JS/src-api/visualization/GenericVisualizationTool.ts b/Lara-JS/src-api/visualization/GenericVisualizationTool.ts index 59b5ef97a..6ed093774 100644 --- a/Lara-JS/src-api/visualization/GenericVisualizationTool.ts +++ b/Lara-JS/src-api/visualization/GenericVisualizationTool.ts @@ -4,19 +4,18 @@ import path from 'path'; import { fileURLToPath } from 'url'; import WebSocket, { WebSocketServer } from 'ws'; import { AddressInfo } from 'net'; -import child from 'child_process'; import { LaraJoinPoint } from '../LaraJoinPoint.js'; import JoinPoints from '../weaver/JoinPoints.js'; import GenericAstConverter, { FilesCode } from './GenericAstConverter.js'; import ToolJoinPoint from './public/js/ToolJoinPoint.js'; -type VisualizationOptions = { - astRoot: LaraJoinPoint; - hostname: string; - port: number; -} - +/** + * @brief Abstract class for a the LARA visualization tool. + * @details To use this class in a compiler, this class must be extended and + * the getAstConverter method must be implemented, returning the compiler + * specialization of the GenericAstConverter class. + */ export default abstract class GenericVisualizationTool { #hostname: string | undefined; #port: number | undefined; @@ -25,22 +24,39 @@ export default abstract class GenericVisualizationTool { #toolAst: ToolJoinPoint | undefined; #prettyHtmlCode: FilesCode | undefined; - get isLaunched(): boolean { + /** + * @brief True whether the visualization tool is launched, and false otherwise + */ + public get isLaunched(): boolean { return this.#wss !== undefined && this.#serverClosed === false; } - get hostname(): string | undefined { + /** + * @brief Hostname to which the visualization tool is listening to. + */ + public get hostname(): string | undefined { return this.#hostname; } - get port(): number | undefined { + /** + * @brief Port to which the visualization tool is listening to. + */ + public get port(): number | undefined { return this.#port; } - get url(): string | undefined { + /** + * @brief URL to which the visualization tool is listening to. + */ + public get url(): string | undefined { return this.#hostname && this.#port ? `http://${this.#hostname}:${this.#port}` : undefined; } + /** + * @brief Updates the stored tool AST and code with the info retrieved from the astConverter. + * + * @param astRoot The root of the wanted AST + */ private updateAstAndCode(astRoot: LaraJoinPoint): void { const astConverter = this.getAstConverter(); astConverter.updateAst(); @@ -49,6 +65,11 @@ export default abstract class GenericVisualizationTool { this.#prettyHtmlCode = astConverter.getPrettyHtmlCode(astRoot); } + /** + * @brief WebSocket server error handler. + * + * @param error The error that occurred + */ private onWssError(error: NodeJS.ErrnoException): void { switch (error.code) { case 'EADDRINUSE': @@ -67,7 +88,13 @@ export default abstract class GenericVisualizationTool { this.#wss!.close(); } - private async launch({ hostname, port }: VisualizationOptions): Promise { + /** + * @brief Launches the visualization tool. + * + * @param hostname The hostname to listen to + * @param port The port to listen to + */ + private async launch(hostname: string, port: number): Promise { const app = express(); const server = http.createServer(app); this.#wss = new WebSocketServer({ server: server }); @@ -92,14 +119,30 @@ export default abstract class GenericVisualizationTool { }); } + /** + * @brief Sends a message to a specific client + * + * @param ws Client WebSocket + * @param data Data to be sent + */ private sendToClient(ws: WebSocket, data: any): void { ws.send(JSON.stringify(data)); } + /** + * @brief Sends a message to all the clients + * + * @param data Message data + */ private sendToAllClients(data: any): void { this.#wss!.clients.forEach(ws => this.sendToClient(ws, data)); } + /** + * @brief Updates the client with the current tool AST and code. + * + * @param ws Client WebSocket + */ private updateClient(ws: WebSocket): void { this.sendToClient(ws, { message: 'update', @@ -108,6 +151,16 @@ export default abstract class GenericVisualizationTool { }); } + /** + * @brief Updates all the clients with the current tool AST and code. + */ + private updateAllClients(): void { + this.#wss!.clients.forEach(ws => this.updateClient(ws)); + } + + /** + * @brief Waits for the tool to be ready to receive the AST and code. + */ private async waitForTool(): Promise { return new Promise(res => { let placeClientOnWait: (ws: WebSocket) => void; @@ -135,18 +188,31 @@ export default abstract class GenericVisualizationTool { }); } + /** + * @brief Visualizes the given AST. + * @details This function launches the visualization tool, if it is not + * already launched, and updates the web interface with the AST and code, + * otherwise. This can involve the recompilation of the code. + * + * @param astRoot Root of the AST to be visualized + * @param port The port to listen to + * @param hostname The hostname to listen to + */ public async visualize(astRoot: LaraJoinPoint = JoinPoints.root(), port: number = 3000, hostname: string = '127.0.0.1'): Promise { this.updateAstAndCode(astRoot!); if (!this.isLaunched) { - await this.launch({astRoot, hostname, port}); + await this.launch(hostname, port); } else { - this.#wss!.clients.forEach(ws => this.updateClient(ws)); + this.updateAllClients(); } console.log(`\nVisualization tool is running at ${this.url}\n`); await this.waitForTool(); } + /** + * @brief Returns the compiler AST converter. + */ protected abstract getAstConverter(): GenericAstConverter; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/communication.ts b/Lara-JS/src-api/visualization/public/js/communication.ts index 9cd9f9165..e2d162d5f 100644 --- a/Lara-JS/src-api/visualization/public/js/communication.ts +++ b/Lara-JS/src-api/visualization/public/js/communication.ts @@ -31,7 +31,7 @@ const onUpdate = (data: any): void => { selectFile(Object.keys(data.code)[0]); getContinueButton().disabled = buttonDisabled; -} +}; const webSocketOnMessage = (message: MessageEvent): void => { const continueButton = getContinueButton(); @@ -85,4 +85,4 @@ export { parseMessage, webSocketOnMessage, continueButtonOnClick, -} +}; diff --git a/Lara-JS/src-api/visualization/public/js/components.ts b/Lara-JS/src-api/visualization/public/js/components.ts index 23932692d..607301bc5 100644 --- a/Lara-JS/src-api/visualization/public/js/components.ts +++ b/Lara-JS/src-api/visualization/public/js/components.ts @@ -106,7 +106,7 @@ const createIcon = (name: string): HTMLElement => { icon.textContent = name; return icon; -} +}; const createNodeDropdown = (nodeId: string): HTMLDivElement => { diff --git a/Lara-JS/src-api/visualization/public/js/main.ts b/Lara-JS/src-api/visualization/public/js/main.ts index 56c10c62a..b955b8ac1 100644 --- a/Lara-JS/src-api/visualization/public/js/main.ts +++ b/Lara-JS/src-api/visualization/public/js/main.ts @@ -1,12 +1,12 @@ import { continueButtonOnClick, getWebSocket } from "./communication.js"; import { getContinueButton } from "./components.js"; -import { addDividerEventListeners } from "./visualization.js"; +import { addResizerEventListeners } from "./visualization.js"; const setupEventListeners = (ws: WebSocket): void => { const continueButton = getContinueButton(); continueButton.addEventListener('click', () => continueButtonOnClick(ws)); - addDividerEventListeners(); + addResizerEventListeners(); } (() => { diff --git a/Lara-JS/src-api/visualization/public/js/utils.ts b/Lara-JS/src-api/visualization/public/js/utils.ts index 73ded8e7a..20ed67024 100644 --- a/Lara-JS/src-api/visualization/public/js/utils.ts +++ b/Lara-JS/src-api/visualization/public/js/utils.ts @@ -17,6 +17,6 @@ const countChar = (str: string, char: string): number => { count++; } return count; -} +}; export { countChar }; \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/GenericVisualizationTool.js b/LaraApi/src-lara/visualization/GenericVisualizationTool.js index d66abc366..443da27ce 100644 --- a/LaraApi/src-lara/visualization/GenericVisualizationTool.js +++ b/LaraApi/src-lara/visualization/GenericVisualizationTool.js @@ -4,6 +4,12 @@ import path from 'path'; import { fileURLToPath } from 'url'; import { WebSocketServer } from 'ws'; import JoinPoints from '../weaver/JoinPoints.js'; +/** + * @brief Abstract class for a the LARA visualization tool. + * @details To use this class in a compiler, this class must be extended and + * the getAstConverter method must be implemented, returning the compiler + * specialization of the GenericAstConverter class. + */ export default class GenericVisualizationTool { #hostname; #port; @@ -11,24 +17,46 @@ export default class GenericVisualizationTool { #serverClosed = false; #toolAst; #prettyHtmlCode; + /** + * @brief True whether the visualization tool is launched, and false otherwise + */ get isLaunched() { return this.#wss !== undefined && this.#serverClosed === false; } + /** + * @brief Hostname to which the visualization tool is listening to. + */ get hostname() { return this.#hostname; } + /** + * @brief Port to which the visualization tool is listening to. + */ get port() { return this.#port; } + /** + * @brief URL to which the visualization tool is listening to. + */ get url() { return this.#hostname && this.#port ? `http://${this.#hostname}:${this.#port}` : undefined; } + /** + * @brief Updates the stored tool AST and code with the info retrieved from the astConverter. + * + * @param astRoot The root of the wanted AST + */ updateAstAndCode(astRoot) { const astConverter = this.getAstConverter(); astConverter.updateAst(); this.#toolAst = astConverter.getToolAst(astRoot); this.#prettyHtmlCode = astConverter.getPrettyHtmlCode(astRoot); } + /** + * @brief WebSocket server error handler. + * + * @param error The error that occurred + */ onWssError(error) { switch (error.code) { case 'EADDRINUSE': @@ -44,7 +72,13 @@ export default class GenericVisualizationTool { ; this.#wss.close(); } - async launch({ hostname, port }) { + /** + * @brief Launches the visualization tool. + * + * @param hostname The hostname to listen to + * @param port The port to listen to + */ + async launch(hostname, port) { const app = express(); const server = http.createServer(app); this.#wss = new WebSocketServer({ server: server }); @@ -64,12 +98,28 @@ export default class GenericVisualizationTool { }); }); } + /** + * @brief Sends a message to a specific client + * + * @param ws Client WebSocket + * @param data Data to be sent + */ sendToClient(ws, data) { ws.send(JSON.stringify(data)); } + /** + * @brief Sends a message to all the clients + * + * @param data Message data + */ sendToAllClients(data) { this.#wss.clients.forEach(ws => this.sendToClient(ws, data)); } + /** + * @brief Updates the client with the current tool AST and code. + * + * @param ws Client WebSocket + */ updateClient(ws) { this.sendToClient(ws, { message: 'update', @@ -77,6 +127,15 @@ export default class GenericVisualizationTool { code: this.#prettyHtmlCode, }); } + /** + * @brief Updates all the clients with the current tool AST and code. + */ + updateAllClients() { + this.#wss.clients.forEach(ws => this.updateClient(ws)); + } + /** + * @brief Waits for the tool to be ready to receive the AST and code. + */ async waitForTool() { return new Promise(res => { let placeClientOnWait; @@ -99,16 +158,27 @@ export default class GenericVisualizationTool { this.#wss.on('connection', placeClientOnWait); }); } + /** + * @brief Visualizes the given AST. + * @details This function launches the visualization tool, if it is not + * already launched, and updates the web interface with the AST and code, + * otherwise. This can involve the recompilation of the code. + * + * @param astRoot Root of the AST to be visualized + * @param port The port to listen to + * @param hostname The hostname to listen to + */ async visualize(astRoot = JoinPoints.root(), port = 3000, hostname = '127.0.0.1') { this.updateAstAndCode(astRoot); if (!this.isLaunched) { - await this.launch({ astRoot, hostname, port }); + await this.launch(hostname, port); } else { - this.#wss.clients.forEach(ws => this.updateClient(ws)); + this.updateAllClients(); } console.log(`\nVisualization tool is running at ${this.url}\n`); await this.waitForTool(); } } +; //# sourceMappingURL=GenericVisualizationTool.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js index 1a86cda3e..6a61fd55c 100644 --- a/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js +++ b/LaraApi/src-lara/visualization/public/js/ToolJoinPoint.js @@ -13,27 +13,51 @@ export default class ToolJoinPoint { this.#info = info; this.#children = children; } + /** + * @brief Returns the join point ID. + */ get id() { return this.#id; } + /** + * @brief Returns the type of join point. + */ get type() { return this.#type; } + /** + * @brief Returns the code of the join point. + */ get code() { return this.#code; } + /** + * @brief Returns the filepath of the file this join point belongs to. + */ get filepath() { return this.#filepath; } + /** + * @brief Returns extra information about the join point. + */ get info() { return this.#info; } + /** + * @brief Returns the children of the join point. + */ get children() { return this.#children; } + /** + * @brief Creates a new ToolJoinPoint object from a JSON object. + */ static fromJson(json) { return new ToolJoinPoint(json.id, json.type, json.code, json.filepath, json.info, json.children.map((child) => ToolJoinPoint.fromJson(child))); } + /** + * @brief Converts the ToolJoinPoint object to a JSON object. + */ toJson() { return { id: this.#id, @@ -44,6 +68,9 @@ export default class ToolJoinPoint { children: this.#children.map((child) => child.toJson()), }; } + /** + * @brief Clones the join point. + */ clone() { return new ToolJoinPoint(this.#id, this.#type, this.#code, this.#filepath, this.#info, this.#children.map((child) => child.clone())); } diff --git a/LaraApi/src-lara/visualization/public/js/ast-import.js b/LaraApi/src-lara/visualization/public/js/ast-import.js index c33bc3d20..c63620586 100644 --- a/LaraApi/src-lara/visualization/public/js/ast-import.js +++ b/LaraApi/src-lara/visualization/public/js/ast-import.js @@ -1,5 +1,12 @@ +/** + * @file ast-import.ts + * @brief Functions for importing the AST and code to the visualization. + */ import { countChar } from './utils.js'; import { createCodeElement, createCodeLines, createCodeWrapper, createNodeDropdown, createNodeDropdownButton, createNodeElement, getActiveCodeElement, getAstContainer, getCodeContainer, getCodeLines, getMainCodeWrapper } from './components.js'; +/** + * @brief Updates the line numbering of the code container. + */ const updateLines = () => { const codeLines = getCodeLines(); const codeWrapper = getMainCodeWrapper(); @@ -11,6 +18,9 @@ const updateLines = () => { const newCodeLines = createCodeLines(numLines); codeLines.replaceWith(newCodeLines); }; +/** + * @brief Initializes the code container, by adding the code lines and the code wrapper. + */ const initCodeContainer = () => { const codeContainer = getCodeContainer(); codeContainer.innerHTML = ''; @@ -18,7 +28,13 @@ const initCodeContainer = () => { const codeWrapper = createCodeWrapper(); codeContainer.append(codeLines, codeWrapper); }; -const addCode = (code, filepath) => { +/** + * @brief Adds a new hidden code element, with the given code, to the code container. + * + * @param code Code of the element + * @param filepath Path of the file + */ +const addFileCode = (code, filepath) => { const codeWrapper = getMainCodeWrapper(); if (!codeWrapper) throw new Error('Code container not initialized'); @@ -26,6 +42,12 @@ const addCode = (code, filepath) => { codeElement.dataset.filepath = filepath; codeWrapper.appendChild(codeElement); }; +/** + * @brief Converts the AST to node HTML elements and their respective dropdowns. + * + * @param root Root of the AST + * @returns The resulting node HTML elements + */ const toNodeElements = (root) => { const fragment = new DocumentFragment(); if (root.children.length > 0) { @@ -45,11 +67,16 @@ const toNodeElements = (root) => { } return fragment; }; +/** + * @brief Imports the AST to the AST container. + * + * @param astRoot Root of the AST + */ const importAst = (astRoot) => { const astContainer = getAstContainer(); const astFragment = toNodeElements(astRoot); astContainer.innerHTML = ''; astContainer.appendChild(astFragment); }; -export { importAst, initCodeContainer, addCode, updateLines }; +export { importAst, initCodeContainer, addFileCode, updateLines }; //# sourceMappingURL=ast-import.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/communication.js b/LaraApi/src-lara/visualization/public/js/communication.js index d806a3576..2e2ed58c8 100644 --- a/LaraApi/src-lara/visualization/public/js/communication.js +++ b/LaraApi/src-lara/visualization/public/js/communication.js @@ -1,7 +1,18 @@ +/** + * @file communication.ts + * @brief Functions for communication with the server. + */ import { importAst, initCodeContainer } from "./ast-import.js"; import { getContinueButton } from "./components.js"; import { addFile, clearFiles, selectFile } from "./files.js"; import { addHighlighingEventListeners } from "./visualization.js"; +/** + * @brief WebSocket message handler for the 'update' message. + * @details When executed, this function updates the code container and the AST + * with the new data. + * + * @param data Message data + */ const onUpdate = (data) => { const buttonDisabled = getContinueButton().disabled; getContinueButton().disabled = true; @@ -29,6 +40,12 @@ const webSocketOnMessage = (message) => { break; } }; +/** + * @brief Creates a WebSocket connection to the server, with the message event + * listener. + * + * @returns WebSocket object + */ const getWebSocket = () => { const url = '/'; const ws = new WebSocket(url); diff --git a/LaraApi/src-lara/visualization/public/js/components.js b/LaraApi/src-lara/visualization/public/js/components.js index 79f65104f..dca91ee2f 100644 --- a/LaraApi/src-lara/visualization/public/js/components.js +++ b/LaraApi/src-lara/visualization/public/js/components.js @@ -1,4 +1,3 @@ -import { createIcon } from "./utils.js"; const getAstContainer = (() => { const astContainer = document.querySelector('#ast-container'); if (!astContainer) { @@ -79,6 +78,12 @@ const getFileTab = (filepath) => { const getActiveFileTab = () => { return getFileTabs().querySelector('.file-tab.active'); }; +const createIcon = (name) => { + const icon = document.createElement('span'); + icon.classList.add('icon', 'material-symbols-outlined'); + icon.textContent = name; + return icon; +}; const createNodeDropdown = (nodeId) => { const dropdown = document.createElement('div'); dropdown.classList.add('ast-node-dropdown'); @@ -158,5 +163,5 @@ const createFileTab = (filepath) => { fileTab.textContent = filepath !== '' ? filepath.slice(filepath.lastIndexOf('/') + 1) : ''; return fileTab; }; -export { getAstContainer, getCodeContainer, getNodeInfoContainer, getContinueButton, getResizer, getFileTabs, getNodeElement, getNodeText, getFirstNodeCodeElement, getNodeCodeElements, getHighlightableElements, getMainCodeWrapper, getCodeLines, getActiveCodeElement, getFileCodeElement, getFileTab, getActiveFileTab, createNodeDropdown, createNodeDropdownButton, createNodeElement, createNodeInfoLine, createNodeInfoAlert, createCodeLines, createCodeElement, createCodeWrapper, createFileTab, }; +export { getAstContainer, getCodeContainer, getNodeInfoContainer, getContinueButton, getResizer, getFileTabs, getNodeElement, getNodeText, getFirstNodeCodeElement, getNodeCodeElements, getHighlightableElements, getMainCodeWrapper, getCodeLines, getActiveCodeElement, getFileCodeElement, getFileTab, getActiveFileTab, createIcon, createNodeDropdown, createNodeDropdownButton, createNodeElement, createNodeInfoLine, createNodeInfoAlert, createCodeLines, createCodeElement, createCodeWrapper, createFileTab, }; //# sourceMappingURL=components.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/files.js b/LaraApi/src-lara/visualization/public/js/files.js index be995ccaa..6339628a3 100644 --- a/LaraApi/src-lara/visualization/public/js/files.js +++ b/LaraApi/src-lara/visualization/public/js/files.js @@ -1,13 +1,26 @@ -import { addCode, updateLines } from "./ast-import.js"; +/** + * @file files.ts + * @brief Functions for handling files in the visualization. + */ +import { addFileCode, updateLines } from "./ast-import.js"; import { createFileTab, getActiveCodeElement, getActiveFileTab, getFileCodeElement, getFileTab, getFileTabs, getMainCodeWrapper } from "./components.js"; let selectedFilepath = null; +/** + * @brief Adds a new file, with the respective file tab and (hidden) code, to the visualization. + * + * @param path Path of the file + * @param code File code + */ const addFile = (path, code) => { - addCode(code, path); + addFileCode(code, path); const fileTab = createFileTab(path); fileTab.addEventListener('click', () => selectFile(path)); const fileTabs = getFileTabs(); fileTabs.appendChild(fileTab); }; +/** + * @brief Clears all files from the code container. + */ const clearFiles = () => { const codeWrapper = getMainCodeWrapper(); if (!codeWrapper) @@ -17,6 +30,10 @@ const clearFiles = () => { codeWrapper.innerHTML = ''; selectedFilepath = null; }; +/** + * @brief Selects a file, by making its code visible in the code container. + * @param filepath + */ const selectFile = (filepath) => { const fileTab = getFileTab(filepath); if (!fileTab) diff --git a/LaraApi/src-lara/visualization/public/js/main.js b/LaraApi/src-lara/visualization/public/js/main.js index 3ed717b0f..5c4aa5909 100644 --- a/LaraApi/src-lara/visualization/public/js/main.js +++ b/LaraApi/src-lara/visualization/public/js/main.js @@ -1,10 +1,10 @@ import { continueButtonOnClick, getWebSocket } from "./communication.js"; import { getContinueButton } from "./components.js"; -import { addDividerEventListeners } from "./visualization.js"; +import { addResizerEventListeners } from "./visualization.js"; const setupEventListeners = (ws) => { const continueButton = getContinueButton(); continueButton.addEventListener('click', () => continueButtonOnClick(ws)); - addDividerEventListeners(); + addResizerEventListeners(); }; (() => { let ws; diff --git a/LaraApi/src-lara/visualization/public/js/utils.js b/LaraApi/src-lara/visualization/public/js/utils.js index 067e204a1..921ecd51d 100644 --- a/LaraApi/src-lara/visualization/public/js/utils.js +++ b/LaraApi/src-lara/visualization/public/js/utils.js @@ -1,3 +1,14 @@ +/** + * @file utils.ts + * @brief Utility functions for the visualization tool. + */ +/** + * @brief Counts the number of occurrences of a character in a string. + * + * @param str String to search + * @param char Target character + * @returns Number of occurrences of char in str + */ const countChar = (str, char) => { let count = 0; for (const c of str) { @@ -6,11 +17,5 @@ const countChar = (str, char) => { } return count; }; -const createIcon = (name) => { - const icon = document.createElement('span'); - icon.classList.add('icon', 'material-symbols-outlined'); - icon.textContent = name; - return icon; -}; -export { countChar, createIcon }; +export { countChar }; //# sourceMappingURL=utils.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index ad6cbff2e..07558917a 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -1,5 +1,15 @@ +/** + * @file visualization.ts + * @brief Functions for handling the visualization behavior and events. + */ import { createCodeElement, createCodeWrapper, createNodeInfoAlert, createNodeInfoLine, getAstContainer, getCodeContainer, getContinueButton, getFirstNodeCodeElement, getHighlightableElements, getNodeElement, getNodeInfoContainer, getNodeText, getResizer } from "./components.js"; import { selectFile } from "./files.js"; +/** + * @brief Highlights the node with the given id. + * + * @param nodeId Node id + * @param strong If the highlight should use a strong color + */ const highlightNode = (nodeId, strong) => { const nodeElement = getNodeElement(nodeId); if (!nodeElement) { @@ -16,6 +26,11 @@ const highlightNode = (nodeId, strong) => { parentNode = parentNode.parentElement?.previousSibling; } }; +/** + * @brief Unhighlights the node with the given id. + * + * @param nodeId Node id + */ const unhighlightNode = (nodeId) => { const nodeElement = getNodeElement(nodeId); if (!nodeElement) { @@ -32,6 +47,11 @@ const unhighlightNode = (nodeId) => { parentNode = parentNode.parentElement?.previousSibling; } }; +/** + * @brief Shows the node info container with the given node information. + * + * @param node The target node + */ const showNodeInfo = (node) => { const nodeInfoContainer = getNodeInfoContainer(); nodeInfoContainer.style.display = 'block'; @@ -55,6 +75,9 @@ const showNodeInfo = (node) => { } } }; +/** + * @brief Hides the node information container. + */ const hideNodeInfo = () => { const nodeInfoContainer = getNodeInfoContainer(); nodeInfoContainer.style.display = 'none'; @@ -107,6 +130,12 @@ const highlightableOnClick = (node, event) => { } showNodeInfo(node); }; +/** + * @brief Adds event listeners to all the highlightable elements relative to + * the nodes in the given AST. + * + * @param root Root of the AST + */ const addHighlighingEventListeners = (root) => { const addListeners = (node) => { const highlightableElements = getHighlightableElements(node.id); @@ -128,7 +157,10 @@ const addHighlighingEventListeners = (root) => { selectedNodeId = null; // To prevent invalid references addListeners(root); }; -const addDividerEventListeners = () => { +/** + * @brief Adds event listeners to the resizer element. + */ +const addResizerEventListeners = () => { const resizer = getResizer(); const astContainer = getAstContainer(); const codeContainer = getCodeContainer(); @@ -156,5 +188,5 @@ const addDividerEventListeners = () => { } }); }; -export { addHighlighingEventListeners, addDividerEventListeners }; +export { addHighlighingEventListeners, addResizerEventListeners }; //# sourceMappingURL=visualization.js.map \ No newline at end of file From 5f4a70a0b68ef52c73caf5f756884ff71a7bd13b Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 26 Jul 2024 12:45:14 +0100 Subject: [PATCH 131/136] Write documentation of GenericAstConverter.ts --- .../visualization/GenericAstConverter.ts | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/Lara-JS/src-api/visualization/GenericAstConverter.ts b/Lara-JS/src-api/visualization/GenericAstConverter.ts index 3495b30ca..19916036e 100644 --- a/Lara-JS/src-api/visualization/GenericAstConverter.ts +++ b/Lara-JS/src-api/visualization/GenericAstConverter.ts @@ -1,12 +1,41 @@ import { LaraJoinPoint } from "../LaraJoinPoint.js"; import ToolJoinPoint from "./public/js/ToolJoinPoint.js"; +/** + * @brief Object type for storing the code of each file + */ export type FilesCode = { - [file: string]: string; + [filepath: string]: string; }; +/** + * @brief Interface for the AST converter. + * @details This interface includes all the compiler specific operations that + * are required by the LARA visualization tool to work. + */ export default interface GenericAstConverter { + /** + * @brief Updates/Rebuilds the AST on the compiler. + */ updateAst(): void; + + /** + * @brief Converts the compiler AST joinpoints to ToolJoinPoint. + * + * @param root Root of the AST + * @returns The AST converted to ToolJoinPoint + */ getToolAst(root: LaraJoinPoint): ToolJoinPoint; + + /** + * @brief Converts the compiled code to HTML code. + * @details This method should perform the mapping of each AST joinpoint to + * their respective code, using HTML tags, and optionally its syntax + * highlighting. See the getNodeCodeTags and getSyntaxHighlightTags methods + * in AstConverterUtils.js for more information. + * + * @param root Root of the AST + * @returns The HTML code + */ getPrettyHtmlCode(root: LaraJoinPoint): FilesCode; } \ No newline at end of file From ade699bf91e9023a78177ec9ab3294644bcfe8e3 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 26 Jul 2024 14:54:54 +0100 Subject: [PATCH 132/136] Write documentation for AstConverterUtils.ts --- .../visualization/AstConverterUtils.ts | 37 +++++++++++++++++++ .../visualization/AstConverterUtils.js | 35 ++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/Lara-JS/src-api/visualization/AstConverterUtils.ts b/Lara-JS/src-api/visualization/AstConverterUtils.ts index 955feea0b..7eb2872b1 100644 --- a/Lara-JS/src-api/visualization/AstConverterUtils.ts +++ b/Lara-JS/src-api/visualization/AstConverterUtils.ts @@ -1,7 +1,26 @@ +/** + * @file AstConverterUtils.ts + * @brief Utility functions for the specializations of GenericAstConverter. + */ + +/** + * @brief Adds indentation to the given code, with the exception of the first line. + * + * @param code Code + * @param indentation The indentation to use + * @returns The indented code + */ const addIdentation = (code: string, indentation: number): string => { return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); }; + +/** + * @brief Escapes the HTML special characters in the given text. + * + * @param text Text to escape + * @returns The escaped text + */ const escapeHtml = (text: string): string => { const specialCharMap: { [char: string]: string } = { '&': '&', @@ -12,14 +31,32 @@ const escapeHtml = (text: string): string => { return text.replace(/[&<>]/g, (match) => specialCharMap[match]); }; +/** + * @brief Returns the opening and closing span tags with the given attributes. + * + * @param attrs Attributes to add to the span tag + * @returns An array with the opening and closing span tags + */ const getSpanTags = (...attrs: string[]): string[] => { return [``, '']; }; +/** + * @brief Returns the span tags for the code of the given node. + * + * @param nodeId Node ID + * @returns An array with the opening and closing span tags + */ const getNodeCodeTags = (nodeId: string): string[] => { return getSpanTags('class="node-code"', `data-node-id="${nodeId}"`); }; +/** + * @brief Returns the span tags for syntax highlighting of code of the given type.. + * + * @param type Code type + * @returns An array with the opening and closing span tags + */ const getSyntaxHighlightTags = (type: 'comment' | 'keyword' | 'literal' | 'string' | 'type'): string[] => { return getSpanTags(`class="${type}"`); }; diff --git a/LaraApi/src-lara/visualization/AstConverterUtils.js b/LaraApi/src-lara/visualization/AstConverterUtils.js index 9d4fc6eaf..3749ee94f 100644 --- a/LaraApi/src-lara/visualization/AstConverterUtils.js +++ b/LaraApi/src-lara/visualization/AstConverterUtils.js @@ -1,6 +1,23 @@ +/** + * @file AstConverterUtils.ts + * @brief Utility functions for the specializations of GenericAstConverter. + */ +/** + * @brief Adds indentation to the given code, with the exception of the first line. + * + * @param code Code + * @param indentation The indentation to use + * @returns The indented code + */ const addIdentation = (code, indentation) => { return code.split('\n').map((line, i) => i > 0 ? ' '.repeat(indentation) + line : line).join('\n'); }; +/** + * @brief Escapes the HTML special characters in the given text. + * + * @param text Text to escape + * @returns The escaped text + */ const escapeHtml = (text) => { const specialCharMap = { '&': '&', @@ -9,12 +26,30 @@ const escapeHtml = (text) => { }; return text.replace(/[&<>]/g, (match) => specialCharMap[match]); }; +/** + * @brief Returns the opening and closing span tags with the given attributes. + * + * @param attrs Attributes to add to the span tag + * @returns An array with the opening and closing span tags + */ const getSpanTags = (...attrs) => { return [``, '']; }; +/** + * @brief Returns the span tags for the code of the given node. + * + * @param nodeId Node ID + * @returns An array with the opening and closing span tags + */ const getNodeCodeTags = (nodeId) => { return getSpanTags('class="node-code"', `data-node-id="${nodeId}"`); }; +/** + * @brief Returns the span tags for syntax highlighting of code of the given type.. + * + * @param type Code type + * @returns An array with the opening and closing span tags + */ const getSyntaxHighlightTags = (type) => { return getSpanTags(`class="${type}"`); }; From 615577a99eec34859e775d4faf775bbf6dbc8671 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 29 Jul 2024 01:40:18 +0100 Subject: [PATCH 133/136] Fix file tabs horizontal scroll bar missing --- Lara-JS/src-api/visualization/public/css/styles.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 9926f429e..5fd7e5a1d 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -241,7 +241,7 @@ main { #file-tabs { box-sizing: border-box; - height: 3.5em; + height: 3em; width: fit-content; max-width: 100%; padding: 0.125em; From 5960e80f7004123984e670229e22e8bf04ea0962 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Tue, 30 Jul 2024 20:10:23 +0100 Subject: [PATCH 134/136] Refactor files tabs overflow behavior --- .../visualization/public/css/color-scheme.css | 4 ++ .../visualization/public/css/styles.css | 30 ++++++++++- .../visualization/public/js/components.ts | 52 +++++++++++++++++++ .../src-api/visualization/public/js/files.ts | 15 +++++- .../visualization/public/js/visualization.ts | 5 +- .../visualization/public/js/components.js | 41 ++++++++++++++- .../src-lara/visualization/public/js/files.js | 13 ++++- .../visualization/public/js/visualization.js | 4 +- 8 files changed, 156 insertions(+), 8 deletions(-) diff --git a/Lara-JS/src-api/visualization/public/css/color-scheme.css b/Lara-JS/src-api/visualization/public/css/color-scheme.css index 9601655a8..5ed4761f1 100644 --- a/Lara-JS/src-api/visualization/public/css/color-scheme.css +++ b/Lara-JS/src-api/visualization/public/css/color-scheme.css @@ -2,12 +2,14 @@ :root { --white: #fff; + --translucid-white: #fff6; --lighter-gray: #e4e4e4; --light-gray: #d4d4d4; --gray: #a2a2a2; --dark-gray: #747474; --darker-gray: #313131; --black: #161616; + --translucid-black: #16161666; --light-blue: #19d8ff; --strong-translucid-light-blue: #19d8ff66; --weak-translucid-light-blue: #19d8ff33; @@ -23,6 +25,7 @@ :root { --bg-color: var(--white); + --translucid-bg-color: var(--translucid-white); --text-color: var(--darker-gray); --header-color: var(--lighter-gray); --border-color: var(--gray); @@ -50,6 +53,7 @@ @media (prefers-color-scheme: dark) { :root { --bg-color: var(--black); + --translucid-bg-color: var(--translucid-black); --text-color: var(--white); --header-color: var(--darker-gray); --border-color: var(--lighter-gray); diff --git a/Lara-JS/src-api/visualization/public/css/styles.css b/Lara-JS/src-api/visualization/public/css/styles.css index 5fd7e5a1d..65c9aa66d 100644 --- a/Lara-JS/src-api/visualization/public/css/styles.css +++ b/Lara-JS/src-api/visualization/public/css/styles.css @@ -241,7 +241,7 @@ main { #file-tabs { box-sizing: border-box; - height: 3em; + height: 3.5em; width: fit-content; max-width: 100%; padding: 0.125em; @@ -254,7 +254,10 @@ main { grid-area: file-tabs; position: relative; bottom: calc(-1em + 1px); +} +#file-tabs > div { + height: 100%; display: flex; align-items: start; justify-content: left; @@ -294,6 +297,31 @@ main { background-color: var(--tab-active-bg-color); } +.file-tabs-arrow { + padding: 0.625em; + background-color: var(--translucid-bg-color); + border: none; + + position: absolute; + top: 0; +} + +.file-tabs-arrow:hover { + background-color: var(--translucid-bg-color); +} + +#file-tabs-arrow-left { + left: 0; +} + +#file-tabs-arrow-right { + right: 0; +} + +.file-tabs-arrow:disabled { + display: none; +} + .loading { color: var(--text-color); diff --git a/Lara-JS/src-api/visualization/public/js/components.ts b/Lara-JS/src-api/visualization/public/js/components.ts index 607301bc5..d3f66da75 100644 --- a/Lara-JS/src-api/visualization/public/js/components.ts +++ b/Lara-JS/src-api/visualization/public/js/components.ts @@ -91,6 +91,10 @@ const getFileCodeElement = (filename: string): HTMLElement | null => { }; +const getFileTabsInternalDiv = (): HTMLDivElement | null => { + return getFileTabs().querySelector('div'); +} + const getFileTab = (filepath: string): HTMLButtonElement | null => { return getFileTabs().querySelector(`.file-tab[data-filepath="${filepath}"]`); }; @@ -99,6 +103,10 @@ const getActiveFileTab = (): HTMLButtonElement | null => { return getFileTabs().querySelector('.file-tab.active'); }; +const getFileTabsArrow = (direction: 'left' | 'right'): HTMLButtonElement | null => { + return document.querySelector(`#file-tabs-arrow-${direction}`); +} + const createIcon = (name: string): HTMLElement => { const icon = document.createElement('span'); @@ -212,6 +220,46 @@ const createFileTab = (filepath: string): HTMLButtonElement => { return fileTab; }; +const fileTabsArrowOnClick = (event: Event, direction: 'left' | 'right') => { + const fileTabsInternalDiv = getFileTabsInternalDiv()!; + const activeTabIndex = Array.from(fileTabsInternalDiv.children).findIndex(tab => tab.classList.contains('active')); + + if (direction === 'left' && activeTabIndex > 0) { + (fileTabsInternalDiv.children[activeTabIndex - 1] as HTMLButtonElement).click(); + } else if (direction === 'right' && activeTabIndex < fileTabsInternalDiv.children.length - 1) { + (fileTabsInternalDiv.children[activeTabIndex + 1] as HTMLButtonElement).click(); + } + + event.stopPropagation(); +}; + +const createFileTabsArrow = (direction: 'left' | 'right'): HTMLButtonElement => { + const arrow = document.createElement('button'); + arrow.classList.add('file-tabs-arrow'); + arrow.id = `file-tabs-arrow-${direction}`; + + arrow.appendChild(createIcon(`keyboard_arrow_${direction}`)); + arrow.addEventListener('click', event => fileTabsArrowOnClick(event, direction)); + return arrow; +} + +/** + * @brief Updates the file tabs arrows, enabling or disabling them based on the + * number of tabs and selected tab. + */ +const updateFileTabsArrows = (): void => { + const fileTabs = getFileTabs(); + const fileTabsInternalDiv = getFileTabsInternalDiv()!; + const activeFileTab = getActiveFileTab(); + + const fileTabsLeftArrow = getFileTabsArrow('left')!; + const fileTabsRightArrow = getFileTabsArrow('right')!; + + const fileTabsOverflow = fileTabs.scrollWidth < fileTabsInternalDiv.scrollWidth; + fileTabsLeftArrow.disabled = !fileTabsOverflow || fileTabsInternalDiv.children[0] === activeFileTab; + fileTabsRightArrow.disabled = !fileTabsOverflow || fileTabsInternalDiv.children[fileTabsInternalDiv.children.length - 1] === activeFileTab; +} + export { getAstContainer, getCodeContainer, @@ -229,7 +277,9 @@ export { getActiveCodeElement, getFileCodeElement, getFileTab, + getFileTabsInternalDiv, getActiveFileTab, + getFileTabsArrow, createIcon, createNodeDropdown, createNodeDropdownButton, @@ -240,4 +290,6 @@ export { createCodeElement, createCodeWrapper, createFileTab, + createFileTabsArrow, + updateFileTabsArrows, }; \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/public/js/files.ts b/Lara-JS/src-api/visualization/public/js/files.ts index 0654129e2..d5901e91e 100644 --- a/Lara-JS/src-api/visualization/public/js/files.ts +++ b/Lara-JS/src-api/visualization/public/js/files.ts @@ -4,7 +4,7 @@ */ import { addFileCode, updateLines } from "./ast-import.js"; -import { createFileTab, getActiveCodeElement, getActiveFileTab, getFileCodeElement, getFileTab, getFileTabs, getMainCodeWrapper } from "./components.js"; +import { createFileTab, createFileTabsArrow, getActiveCodeElement, getActiveFileTab, getFileCodeElement, getFileTab, getFileTabsInternalDiv, getFileTabs, getMainCodeWrapper, updateFileTabsArrows } from "./components.js"; let selectedFilepath: string | null = null; @@ -21,7 +21,14 @@ const addFile = (path: string, code: string): void => { fileTab.addEventListener('click', () => selectFile(path)); const fileTabs = getFileTabs(); - fileTabs.appendChild(fileTab); + const fileTabsInternalDiv = getFileTabsInternalDiv()!; + if (fileTabsInternalDiv.children.length === 0) { + const fileTabsLeftArrow = createFileTabsArrow('left'); + const fileTabsRightArrow = createFileTabsArrow('right'); + fileTabs.append(fileTabsLeftArrow, fileTabsRightArrow); + } + + fileTabsInternalDiv.appendChild(fileTab); }; /** @@ -34,6 +41,7 @@ const clearFiles = (): void => { const fileTabs = getFileTabs(); fileTabs.innerHTML = ''; + fileTabs.appendChild(document.createElement('div')); codeWrapper.innerHTML = ''; selectedFilepath = null; @@ -62,6 +70,9 @@ const selectFile = (filepath: string): void => { fileCodeElement.classList.add('active'); updateLines(); + fileTab.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + updateFileTabsArrows(); + selectedFilepath = filepath; } }; diff --git a/Lara-JS/src-api/visualization/public/js/visualization.ts b/Lara-JS/src-api/visualization/public/js/visualization.ts index e3bd575c3..0a1d8ad80 100644 --- a/Lara-JS/src-api/visualization/public/js/visualization.ts +++ b/Lara-JS/src-api/visualization/public/js/visualization.ts @@ -3,7 +3,7 @@ * @brief Functions for handling the visualization behavior and events. */ -import { createCodeElement, createCodeWrapper, createNodeInfoAlert, createNodeInfoLine, getAstContainer, getCodeContainer, getContinueButton, getFirstNodeCodeElement, getHighlightableElements, getNodeElement, getNodeInfoContainer, getNodeText, getResizer } from "./components.js"; +import { createCodeElement, createCodeWrapper, createNodeInfoAlert, createNodeInfoLine, getAstContainer, getCodeContainer, getContinueButton, getFileTabsInternalDiv, getFirstNodeCodeElement, getHighlightableElements, getNodeElement, getNodeInfoContainer, getNodeText, getResizer, updateFileTabsArrows } from "./components.js"; import { selectFile } from "./files.js"; import JoinPoint from "./ToolJoinPoint.js"; @@ -225,6 +225,9 @@ const addResizerEventListeners = (): void => { else if (width > maxWidth) width = maxWidth; rootStyle.setProperty('--ast-container-width', `${width}px`); + + if (getFileTabsInternalDiv()) + updateFileTabsArrows(); } }); }; diff --git a/LaraApi/src-lara/visualization/public/js/components.js b/LaraApi/src-lara/visualization/public/js/components.js index dca91ee2f..16fb916da 100644 --- a/LaraApi/src-lara/visualization/public/js/components.js +++ b/LaraApi/src-lara/visualization/public/js/components.js @@ -72,12 +72,18 @@ const getActiveCodeElement = () => { const getFileCodeElement = (filename) => { return getCodeContainer().querySelector(`code[data-filepath="${filename}"]`); }; +const getFileTabsInternalDiv = () => { + return getFileTabs().querySelector('div'); +}; const getFileTab = (filepath) => { return getFileTabs().querySelector(`.file-tab[data-filepath="${filepath}"]`); }; const getActiveFileTab = () => { return getFileTabs().querySelector('.file-tab.active'); }; +const getFileTabsArrow = (direction) => { + return document.querySelector(`#file-tabs-arrow-${direction}`); +}; const createIcon = (name) => { const icon = document.createElement('span'); icon.classList.add('icon', 'material-symbols-outlined'); @@ -163,5 +169,38 @@ const createFileTab = (filepath) => { fileTab.textContent = filepath !== '' ? filepath.slice(filepath.lastIndexOf('/') + 1) : ''; return fileTab; }; -export { getAstContainer, getCodeContainer, getNodeInfoContainer, getContinueButton, getResizer, getFileTabs, getNodeElement, getNodeText, getFirstNodeCodeElement, getNodeCodeElements, getHighlightableElements, getMainCodeWrapper, getCodeLines, getActiveCodeElement, getFileCodeElement, getFileTab, getActiveFileTab, createIcon, createNodeDropdown, createNodeDropdownButton, createNodeElement, createNodeInfoLine, createNodeInfoAlert, createCodeLines, createCodeElement, createCodeWrapper, createFileTab, }; +const fileTabsArrowOnClick = (event, direction) => { + const fileTabsInternalDiv = getFileTabsInternalDiv(); + const activeTabIndex = Array.from(fileTabsInternalDiv.children).findIndex(tab => tab.classList.contains('active')); + if (direction === 'left' && activeTabIndex > 0) { + fileTabsInternalDiv.children[activeTabIndex - 1].click(); + } + else if (direction === 'right' && activeTabIndex < fileTabsInternalDiv.children.length - 1) { + fileTabsInternalDiv.children[activeTabIndex + 1].click(); + } + event.stopPropagation(); +}; +const createFileTabsArrow = (direction) => { + const arrow = document.createElement('button'); + arrow.classList.add('file-tabs-arrow'); + arrow.id = `file-tabs-arrow-${direction}`; + arrow.appendChild(createIcon(`keyboard_arrow_${direction}`)); + arrow.addEventListener('click', event => fileTabsArrowOnClick(event, direction)); + return arrow; +}; +/** + * @brief Updates the file tabs arrows, enabling or disabling them based on the + * number of tabs and selected tab. + */ +const updateFileTabsArrows = () => { + const fileTabs = getFileTabs(); + const fileTabsInternalDiv = getFileTabsInternalDiv(); + const activeFileTab = getActiveFileTab(); + const fileTabsLeftArrow = getFileTabsArrow('left'); + const fileTabsRightArrow = getFileTabsArrow('right'); + const fileTabsOverflow = fileTabs.scrollWidth < fileTabsInternalDiv.scrollWidth; + fileTabsLeftArrow.disabled = !fileTabsOverflow || fileTabsInternalDiv.children[0] === activeFileTab; + fileTabsRightArrow.disabled = !fileTabsOverflow || fileTabsInternalDiv.children[fileTabsInternalDiv.children.length - 1] === activeFileTab; +}; +export { getAstContainer, getCodeContainer, getNodeInfoContainer, getContinueButton, getResizer, getFileTabs, getNodeElement, getNodeText, getFirstNodeCodeElement, getNodeCodeElements, getHighlightableElements, getMainCodeWrapper, getCodeLines, getActiveCodeElement, getFileCodeElement, getFileTab, getFileTabsInternalDiv, getActiveFileTab, getFileTabsArrow, createIcon, createNodeDropdown, createNodeDropdownButton, createNodeElement, createNodeInfoLine, createNodeInfoAlert, createCodeLines, createCodeElement, createCodeWrapper, createFileTab, createFileTabsArrow, updateFileTabsArrows, }; //# sourceMappingURL=components.js.map \ No newline at end of file diff --git a/LaraApi/src-lara/visualization/public/js/files.js b/LaraApi/src-lara/visualization/public/js/files.js index 6339628a3..90610de92 100644 --- a/LaraApi/src-lara/visualization/public/js/files.js +++ b/LaraApi/src-lara/visualization/public/js/files.js @@ -3,7 +3,7 @@ * @brief Functions for handling files in the visualization. */ import { addFileCode, updateLines } from "./ast-import.js"; -import { createFileTab, getActiveCodeElement, getActiveFileTab, getFileCodeElement, getFileTab, getFileTabs, getMainCodeWrapper } from "./components.js"; +import { createFileTab, createFileTabsArrow, getActiveCodeElement, getActiveFileTab, getFileCodeElement, getFileTab, getFileTabsInternalDiv, getFileTabs, getMainCodeWrapper, updateFileTabsArrows } from "./components.js"; let selectedFilepath = null; /** * @brief Adds a new file, with the respective file tab and (hidden) code, to the visualization. @@ -16,7 +16,13 @@ const addFile = (path, code) => { const fileTab = createFileTab(path); fileTab.addEventListener('click', () => selectFile(path)); const fileTabs = getFileTabs(); - fileTabs.appendChild(fileTab); + const fileTabsInternalDiv = getFileTabsInternalDiv(); + if (fileTabsInternalDiv.children.length === 0) { + const fileTabsLeftArrow = createFileTabsArrow('left'); + const fileTabsRightArrow = createFileTabsArrow('right'); + fileTabs.append(fileTabsLeftArrow, fileTabsRightArrow); + } + fileTabsInternalDiv.appendChild(fileTab); }; /** * @brief Clears all files from the code container. @@ -27,6 +33,7 @@ const clearFiles = () => { throw new Error('Code container not initialized'); const fileTabs = getFileTabs(); fileTabs.innerHTML = ''; + fileTabs.appendChild(document.createElement('div')); codeWrapper.innerHTML = ''; selectedFilepath = null; }; @@ -49,6 +56,8 @@ const selectFile = (filepath) => { const fileCodeElement = getFileCodeElement(filepath); fileCodeElement.classList.add('active'); updateLines(); + fileTab.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + updateFileTabsArrows(); selectedFilepath = filepath; } }; diff --git a/LaraApi/src-lara/visualization/public/js/visualization.js b/LaraApi/src-lara/visualization/public/js/visualization.js index 07558917a..90c9ce295 100644 --- a/LaraApi/src-lara/visualization/public/js/visualization.js +++ b/LaraApi/src-lara/visualization/public/js/visualization.js @@ -2,7 +2,7 @@ * @file visualization.ts * @brief Functions for handling the visualization behavior and events. */ -import { createCodeElement, createCodeWrapper, createNodeInfoAlert, createNodeInfoLine, getAstContainer, getCodeContainer, getContinueButton, getFirstNodeCodeElement, getHighlightableElements, getNodeElement, getNodeInfoContainer, getNodeText, getResizer } from "./components.js"; +import { createCodeElement, createCodeWrapper, createNodeInfoAlert, createNodeInfoLine, getAstContainer, getCodeContainer, getContinueButton, getFileTabsInternalDiv, getFirstNodeCodeElement, getHighlightableElements, getNodeElement, getNodeInfoContainer, getNodeText, getResizer, updateFileTabsArrows } from "./components.js"; import { selectFile } from "./files.js"; /** * @brief Highlights the node with the given id. @@ -185,6 +185,8 @@ const addResizerEventListeners = () => { else if (width > maxWidth) width = maxWidth; rootStyle.setProperty('--ast-container-width', `${width}px`); + if (getFileTabsInternalDiv()) + updateFileTabsArrows(); } }); }; From 03a08b8523ca2587828a3e9e7be5bf1381f8bb96 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 14 Aug 2024 17:32:20 +0100 Subject: [PATCH 135/136] Add README --- Lara-JS/src-api/visualization/README.md | 36 +++++++++++++++++++ .../compiler-abstracted-system.svg | 4 +++ 2 files changed, 40 insertions(+) create mode 100644 Lara-JS/src-api/visualization/README.md create mode 100644 Lara-JS/src-api/visualization/compiler-abstracted-system.svg diff --git a/Lara-JS/src-api/visualization/README.md b/Lara-JS/src-api/visualization/README.md new file mode 100644 index 000000000..55da94b03 --- /dev/null +++ b/Lara-JS/src-api/visualization/README.md @@ -0,0 +1,36 @@ +# LARA Visualization Tool + +Web tool for visualization and analysis of the AST and its source code. + +## Integration + +Internally, the tool follows a system for interaction with the compiler, to be able to apply code linkage and correction, among others, while still being independent of the specific compiler. This system is an implementation of the Factory Method pattern, and the integration with Clava is illustrated in the following diagram: + +![Compiler Abstracted System](./compiler-abstracted-system.svg) + +To integrate the tool in another compiler: + +1. Implement the `GenericAstConverter` interface, with its functions properly implemented. More information can be found in their documentation. +2. Create a class that extends `GenericVisualizationTool`, and override `getAstConverter` so that it returns an instance of the class declared in the previous step. +3. Use an instance of the previous class as the entry point of the visualization tool API, for the compiler in question. + +## Usage + +After integration, and being `VisualizationTool` the extended derived class of `GenericVisualizationTool`, to launch or update the visualization tool, execute the following statement: + +```js +await VisualizationTool.visualize(); +``` + +Once ready, Clava will provide the URL that should be opened in the browser to access the web interface. The function can also change the AST root and URL domain and port. + +Other properties will allow the user to know other important information from the server: + +```js +VisualizationTool.isLaunched; // true if the server is running +VisualizationTool.url; // URL where the server is running +VisualizationTool.port; // port to which the server is listening +VisualizationTool.hostname; // hostname to which the server is listening +``` + +For more details, refer to the `GenericVisualizationTool` documentation. \ No newline at end of file diff --git a/Lara-JS/src-api/visualization/compiler-abstracted-system.svg b/Lara-JS/src-api/visualization/compiler-abstracted-system.svg new file mode 100644 index 000000000..caaf5f448 --- /dev/null +++ b/Lara-JS/src-api/visualization/compiler-abstracted-system.svg @@ -0,0 +1,4 @@ + + + +
Compiler-Abstracted System Class Diagram
GenericVisualizationTool
+ visualize(): Promise<void>
+ get isLaunched(): bool
+ get hostname(): string | undefined
+ get port(): number | undefined
+ get url(): string | undefined
# getAstConverter(): GenericAstConverter
«interface»
GenericAstConverter
+ updateAst(): void
+ getToolAst(root: LaraJoinPoint): ToolJoinPoint
+ getPrettyHtmlCode(root: LaraJoinPoint): FilesCode
VisualizationTool
- astConverter: ClavaAstConverter
# getAstConverter(): GenericAstConverter
ClavaAstConverter
1
1
LARA
Clava
return this.astConverter;
\ No newline at end of file From a79ecb791fc144fa22125e7f9e9cae2f070dab62 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 16 Aug 2024 22:02:27 +0100 Subject: [PATCH 136/136] Add report --- .../visualization/capstone-project-report.pdf | Bin 0 -> 1322084 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Lara-JS/src-api/visualization/capstone-project-report.pdf diff --git a/Lara-JS/src-api/visualization/capstone-project-report.pdf b/Lara-JS/src-api/visualization/capstone-project-report.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f204be5faa4b883d0b84eb858f81c2945a4f0efe GIT binary patch literal 1322084 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

e?>|QukZ2ORx1=SK22Rs;y`}F`)bgo*qttH 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%0D5szm99G~nTV(;TO6}uNKgp3yhSUj35p_) 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

1`&+*hL_T$30{{EefQz8M+B4J<1rseR4mD1z% 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ξL zK0qvxy{&#ul`cO;9(tMtLRqWr`R}K!{rZ{;_?jmakSql;mmSp?Q%(w6zpiPsGs80?)K`ImXM z|M=_B_+dhwO~y~R!wR1SK?>lx!e-8@6mx*NA2LRSAPmy7i19})tb6|^h?EEG0KHQF zr%PgX{jZX0f8C)j#CY#N&lLL~%9{R#M&j-NbAm-#Iq}=RUaSnRdN4k@W-Dj!@GA3f z#e4sH0c`GU0e}x5yl&r$ne(W&)7(0_jJ?Nv`*)b++>Z$XM9B8%gn-wCGHCt)S)dYM z03^qum}J6h)=|%-=SD<3HT}no(FNOjN6a#+2b5XwOjvR+N`Ry$|!9$e0|c@U^aZ zHdSCqvRx#HqD**HAC{qzhH;8imOe=lku{)e0!|kM1H8im2va;e06X#-=4#iHJO`Nn zu}+=6vrS{G_#2I_5WXmXh!J3$?c#WT5Cha+AhRl@s6etN)nx>n(IJRawm(M#lmRN+ zKxAXeW1+ZN&-@HI@vJ-MuI_JaZVJ?IEVc!?>Fx3J&sBKvVOa*XASm*)h@huL2Brt% zLs|#hywp6+KNX>i{?sC#9ig2_B0@a^=<^^Ms_KmlNYbFDA4(aix^@gin^Eh(E%1ih z6*Djqr0$oDG@B)7A>;b04ygb3q(lvps}>s#0BR3OBnVY1q##`aXW&$VHv>)OZ%G!v zHl$~OYB>hFL3xE>Dv%{}PZ|RuZwfO@<68DT6hkzD#!G%)9!vm>AR+X_2WWZ1@2n6> z_>oW)lg3;XnFInZFz)ft5&AJjBKsG-e=E=~oI?`Ak`j056&RTUr2zRFJE$6dq;cxV z00mM@0J=0WfdX0m(-#Z=V6f*q+ZtvYX5P`3@C?Wk z7}CUb%!cZ*b)_BOKDj_n0ILc#G1th@9ov@8n~Y!4-vudbFW@3@$1(dT`f@*LkPz9- zqP`1JKaEil$=Lt9K6isw$UD||2jKRa+TC_?Mw=ShB7d?sw%RKwBzp4vN#swGl6!g8 z+#O6$@@ic-wlXzw-pi}%V(h&A|H#`KnVatA)wI0rY;lrLjQ{*z-m9jT<`&K;#l(d6 z@?NrZcDiATzHD#nU~gw?=X_FhFYjf08+){>gOLdw@QUdjOA}K$w2}K>UL{jIbL1eR zB7!HkKYMv)Ep41l(Ik)s~)&?iju$$mQ6;cR@j;(r0t$N3jAq+jPM{ z7=mS8ldt#L`4A4d)2yCbW-4#ptZ&(DX_>7TTxm>f#DDoZQ~oN*H-n?%bfE8Ph4Plp z&3^LR++-`w=`+ei-1p)DqDi~dLiyn0S~j!tYe&XS=8X>VY~pJr|Ad-Ql}#9TlSJ)(sn9HPPew6w;Qs^Lb7(|JB9-+^(UE zk;Q3C>iU#)J{ZwLs#m?kw{eQqOjE3Sty~}fN|4rgk#SRy|+_4lP%Fy8KG3TY8vvlau8m2y0Mrxg2_M(XrOI;(gbgNwz z#xc6D{SF6qsi`X_*Q9G%KO%8|E}ohhi|dnS-$!|3>G;lg30ZX_pT{xfdXoSh${UG+ z0Yu-q2Znlnml^7Phc7?N+;{){y#QXbXs^wUl%0li#53~VZ=x=iT^`hu6?ObrlYZpQ zy!?fha89OO1xFc8-1f&kRZUGku{QV8;6`l^;XxanOl5iQ4tX^9?NYTTJ4q&T!mB~~ zR#PWM^{@ICkDl0*gZm~cpr_r$=|Wk$L;up1N7{erF}da1yT_jho_fE?m7^jX8!{(x zD&(HOg;RoJv^&qo>ck`)C3Fvq#Vr5v?C{h3GK$63KhVVEP&>xh@GhC=UoTQL{Y9C1 zv&8aD*WJVvga^A*m56xID?Q<6JAGu}KwREb*=|AUXV2YH_pQP>bz}Ih zjecO*dcgDGPS9WPWP})tJVdh6I}ZNfYa4V?I^DCpr{8c6UdL zrFK5GptU!rT6g8G-1%NQkx~YO9JYrQ!<9$nE`JCej&znUZa5hGg0{IV_2gv(!ADI2 za>Bd04Xiw0w4wJru|BQoJU8g{p!5*K6>GIH{A2XHl>$e>jr1L+JAy?o5An_Ur;tg0 zr=s67Tq@Celpk(oxSnsKV{2r#=*)(VG@J9G)b5o_nWSITYBf5Xf$TlkFXq`^GrSHZ^mIA%( zE=SXkEn$1@NOcFIF&yl#o^Z17j<<7s^hVE7-t%nto4uLF^c#)}E_&+C(@mMDmUH_{ zBk%1FcOSm(Pk-0nsnMWSB98R_RrT>ERn8Nwe{rgDzKl3{@RKRwtY(U!BAt*D@6jN| zAxfIW{-Hm|^3c3fx-HTzPduU+(i(4C=V~G^FtHHqNdj$TRbm4hb!0OJX^RC&e7=G^6)J$-94?a{8Cpah^L!`4NhM=l(5pZ<{QYF2V$b)Nrdkp9 z@)4U6o#{%o^0=)Bj+yN|6}OJ0wr1bX9@Z1)I9%y+Np_Stsws4Iy;CK9e@#k_+gpb9 z)&T!YhjR>g1AInEQch3BGtXQ;`D)k1Tl{y^MS4G(8)vguy^?lkjptb7__U9WPCamM ztsW2>ypo{yL>_Ye3UhwV4)SZ)RBJALJN+)EwUu}eG zIq~{pRG4e=cKTTPNUx+Kfi`(qv>&T z(CO^F-^B7?A1yT3xl$`%#2VLDPAGcw2h^l0JW2%jv+Yj~QB&;>wxMtBYfxNu-o`_- zO6FDknaDLdf!x~5!{|nZCWR?V1W`ghZetax#|To&;*xS>Yr))6snW)KaX!3flI^w3 zu#cXRPsBdgS6T;i(CyE9Y-+OYgNezSQLO|V{TM&Q#201vmdDyu*?1)9RkF!7j{O#6E z3&C|+RnoI(gWcqkf-I%)Q{_{#CM4p;E1mN!XcA+js#fQnO1(?FeqDFP!|^R_I6hFZ zu`!^jaj)WW&x`i*+%uk)Sr|7BxNrKuQ^UzI?-%k3Mgi=CmKYQFwx=->nMLT5 z-e2GASrCh=VkC>5-mK%T$K0v9**$T|ot)acwzGW~m)O7{sp0OQ(%eea!DJF|Jxhqe zXLr9^J*S_)JxL|VfMF3#zU~$>Rr2gJ9a`G5?mt8@%E9g~oiy-~1zW z#n9o^V3zmR#EiWo!}+y$cf~eQaTtfY)lXZSri{Vn=yvO`YYDeJCcQ@0}8ThHG2?O!Q?IWg@Stm3Q0o5x_%m`?G8@J4x;vJ`%QQpcOSvz~A+&uY=~2qavl*GP>|V8BPL(Adn3jfDj$3$BC6Xpzf) zpfKs=0Qo_SV|i(bUr6Zfn>Tx?spo9zMq5NZSF^l&!o$M}x<3x~u59deaMfoL(H$5$ zXxixb_{<@lxw*L|9iRhR51bSi*U6-pl9B=`WMEH#+EOv7kGu^A>JU8xgF>dLnAltQ z^Np*ZOT=9mD`Xwzb!>5Q@$uuwOT(ZlGT1rsvX&P+H?=W$s z@Pgk!BOOL$1@rU7bsAP$h-o&Ictqlm1* zv**`sZ{6X2Nnp@(izs&TE!J1FN_%s|C7&n;l3lS>)XdW51qEs;!TcCtC%#cX46XXH z0H6a8Us+p=ww98VeEH$QgFpB{d}Oey7AGG_aFUg+ z1JVObe!v7?*HH;uK8z11-EEje(%6e;y#s2?nTDl~BeJY>(Zrwc?tDSl6p?6fRTi6% zTEqB|n=1NWlaa{+wbHbQetzi%H^*@#5(({o>C$}|M$wCmiFuW0j7H}**3Hh$^uj58 zxFpt>AZQt;@EYa{%iqGxq%(H=WHhb0F+S6yxJYQ>M9)3?`!`Cz&_*Zp6+IuqH_3BV zekXH{HB*i=9ccH*Q~QX{-$idEZ@mwtJ>e=w%ekuCw?Y73f5OwJPrnSncg1Pv-F~c) z-??Od>iqc|*;dkDd?oV=${mMb?#-5|8ybBnb8L1dJhg3)n))AdKVUvOsm-D`_=02=#|+QZq(9y&6n&r=zgvm)V>iF++KZ7fT?=lWW{x8Gz)tg~ z8Jm`xEA$f6q5QsRAh5v)x28~)`Uy9W>p|W6M4}m=n@8cS?^b`_u`h&5>l@4Ut=S5c z7Q=eS&}5!S8@cD}S6wR`8yhcZes;=QYJ{VJ2$S;mRenK%GstzRM2fzFX1F{aT8*|$ zT34?=D%W_X=Y-AmHAI0fu zrp5Nk)G>cwI|w?WAF{%l@oHUmqE}(bCNE=a(wO${iboq75&4Tph{;u|DsR?6Sdp?j*?G#4s4d|Yuqby8KJG>rQ9)(UU8f6ZwnG(IZ8_-;T9v8=P zR5(;O^79%H7Hdqkt9&XXQ|qCZmvU4HhaEHG0r8E=nVE9X6FhwQu)DrvfhofTZushq zkGw%sL&MeYH@uN`Xo!dw12wn;=;-jCrjJ4^m&1DRtJ;nyIbovgxiDDuzBA+sX?~xI z4Nq@V*#V#;oDx!f>{Z*b3Cnp@X3SJG=PgkTg)$T~3v@Z`ep3*vqV2kNgd<+D@cE|i zI;!zvtoB1y%_wR9)eJePc?0)q1|>J)<-DqaeYaeBZ_0aTyH3o`nuy+d)QZ#~r+3%0 zIT4f4pqr(~ zRRqFx6H~ewy%8?HuJW*prD}9a#xUWQV~Gz=?ZN< z#?I$Vqa+5#0fEwOs_c8KVXCOO0XR;k0Yq&Wal_%ROEbCqqF;Et9>W2DAzI|@+@o^u zT!}G>vqGp_OW=l%e9GHLD@(P{rf8ov)viqu*%z3vsBog(WVh(^b&IF2MVZhVbIVSw3+o@fKv zn>@o+QT33vtY)n4OK|QvqzG;6rV91ike8QA(yXnGvQ(6mg37m^JUMVP3|3XNo4fl* z?)duGpdb1~v1VZ3zT?Rx5U0x0e96=_%Rttyg}%BvKY@A_H_Ql{Vkx_Tu&9E$2!w$F z;zl?vd6GwU6sMt~ffjI_8XbYjysC4|wVg{75Lgt)41EYG zd0wFZ*f=m1$9(=*)EaQ%rmmS8Ju39>{Y0|+?v0k-uHG+IQ+~d|LwFn94kw0sFVeK8 zoYTGl1sEw0x6{d@Sw|TXh^CL%FcooEW(}h@UrV@GW=#*G*wWsEE=05fuzFXPm*Ya) zvZOIge8vwNIfoj3=uHjF-EMN|fyt5eWu;8i2bjwWtOXXdLg4U8*igpIxOY{7?n>A` zh(gflF9BsG7_40`~3vN}ZH*^wehVRO-YHrj-xh!t(uCA^givlKd zcoSSzk5`15DM=FiV=~VzIn)4&!KQKxXfXucz^!Cm>n9s)i3&TDs)*TT1p?sxZ;G_6^~ohbe2Seh4@a~pUnjs2D)Qx2qZ z@69`el|T-Ou*)P+X^C+0CUM!D!yH88638t!)^mjz;wjG|zO3+hSC;)UWo?XG>WEO{ z3bBN+{JETRi;gjVp8iy4g@?eJ;feBH%~I>{`zR@ov@_3CNIOW*p|kjcXWB?O7@mmK z(0zH6w^4_l><;WBw2_xrIeLU0HeWj;%m7;Bok8;QC8LRU2+x?K?^$@rVUR&QvgQX4 zImkdZ+JY7Ox6lX1 z;rWB0-V+li$Aw03Dh9!;12?t1CU8ryK0$ujh|bk6Av=}*J*-H*BM{pX``Qv`h7)JP z-enDcD{sR|vTU}%FnBp7!#Ivvcm!cZhB6b)B;9R>-ZLl@Y zcYZc4j$USf4`bA4-J{q3rO6v z;w+D0r&EX@cg!sBg!p*n^%td{c%AQWxYtUK8k?BJ8#1z6!@y^b9(tsXDm^xj(y44| z0%)5cEZ5MJ^`%I=7L;vMb_oj$tIQiN=AcCnieaKm+)Gql1Lah-wAkzl)c7si&RDuQ zI*k?W0HHy}wbD$y`Iu1jW-ds>>G)K=ST0m(q4_O^=#eLQAD$;mpSjE_uFESdCn7Az z%Av?x`E5|deFz0 z!V2*7D=8_Bl0K@u$eXYV{44+2H6Z#$6$-w2tO6Ob0*Ud!fyA~{_rAEWu+wWsg>T;i zaWfy5gQ$0U4dk+WQ$|Kc#*WHt!Zc3MF}_PByi;fpp}oe#CnDnN?EDOp>bxEh+iMd1 zgw@y68#No=+r1UGtWgPhVS}w~C=>T8o1x%G2m>jvVGmQ{Ib!?rrm~J?*w*G+lizS< z=y8#2J-ZDUnv$}n@e?33Ggk7*eeq%>UG3|G%o+c%C@_y2zGGf^Rj=It(3Y2!w=~?A zcYnAoERVn5_vsehx`i|efBX|xcXVzpoAH@D45qh;fWQE2S|g~-vRg}Gcs9pw90x99 z(rqA{ewXV;W}5J)Qaf%utN>lv3cWq(nsCiVcou7xVh!xg0an!I0{Hnz)Oz51jOB;~}$Jpc0FPp{Is zakm?Ebu1mzhG#~}2U z&|nPr{41H@tGR^@{5tqIm}h}{iY}^ywe)1MHmSUZ^E;*7VCYPRI3I@OThRp|LZo8T&QnOr{d1U~Qvyv75Mc&{Ua#ui};@zAyJ> zuE?_0)3-^hs#qiIe-D3dSYwK~f5PW3)AdGd$|CmgK85o9rqJ88o~BU`$D$tSR}!L4 z>vwZ4&(1qI-;JSN2RowNwdEdNt&WJ(-kHMZ&)7Cu9KOgNNC6JTF=VhmaRfL>lNSYz7PR8!fA6H1R%%(zOe9|L&u5OrqtTes`LVWa2W=)fZL zDEsT2HqZE*DC2igu$$hZ{&jSAWI3%_5rc);|HPEKO$?sSC%a`g410y-WukAk-bSF%89v5uGg!UUf0OQZ8RV9 zYnFYKUbyRB)0Y3M=x?A~5DiP-=5t;thy1JIb@=e5O?}Vx(k*O!=ke!H+C{Pp47D^& zcF)9ocrh_W{so{)H8!l5OViDhau%AW9uow@m#T`1=r~6)Oseq)KRf0_Z6C=<+dG`8 zxZlEye9KYZzkbj{&rywtBjD#v_U``tw>QZnN|A8(+ys8ZptMVb;5Zn;`aRW}W;1~| z-i@6%r+Cz*ltZy(B{~`pZ(^*saUkk3&igr^)X#fN(SPHj{M6H|w)wVzsfBoMc8Fc^kgZwuf z;qF`pOj4aYHx7d+a;;R4jvqXJTvt`Kn)UFHgTIq_U0T|U!_C7Kw(-Nv&&qR;icf&z zp2zA8z2?tb^RA3jT3VWg#do^3|7A+b*>mU6Cw`tSKUHsYND6EYHuG4tS|2}FJfU8^&HJ8gTF=y6;d z5#R_IgycFPj`?+mEa1eu#f({ouJKy>9Z;FcM zdy1fH5Q6(;Vr+cvnW*C*twHMrHFb61L$xr0+@?e!`;(X`E}a5oc9rsYU!G~8RzLXP zfN+O#Zg%#y=DukPoFBk6L-^nLn zQn&x6CqUMGRZxF|N($&{feN>pja)z~$S25j0Q8(Po0ggyAR{LyXPFBk_AsFc5H(x| z9Q^Rx{@G3niq^RGb+>`uyCL$RjsOXy_}|XoP>{-j&vVtr(h=zHai}2;HMOfOZm%Lx z7?NINE@oqj73$c}vNjl)ZZ)p^@?{xBdwF4x00l~@jq{3%=3eO@f zK>-2$@^Ui{XP1Q1BLx*MYlVtIb;p0%zOS1rc z^av)84KM&N6Q^%O5*;gK8JcUJ8okPlJ?F6pMN1)``gVx>e)iTksS9 zG|WCQS=Uw`8L4h#lQpgzUN?A*&-jWp6+oz0D8~Z3K&t^3qJdx&76b*xifFA8P=lo| ziqx@?QzqTWj0J0lLlDG0Cg$%k-L;9`1~@N>cn#PjpkR3U3WHO1e}4t2HR$VR=H$SD zGTpSoWpTx@@Nk*O%cS=pW}sxF=Ogjr3mFpS?=p0wIB-`m_5lo4C6XUAQ0ZM31NE|Z z)+H5{LpR}_b0slN?r4bm(f4q!o@f~-oQoobgc^(tBa{{K&71Y~K;#Ab3F;MWQCDHs8KYw0n zX&f7@ZwU;|GR5ttABaim5I$9b1nM>yn3{G+j%)0(E)znYwF06BZ%$t z)RdUGIEY%6*XxBB9hwNReg*c(^YhpL@rNJy`&(OD+IGKr;jfSUSZZ3DVcDJFMG@qs zSSbGsP7+T0D5r)6J4nZ9ACZPV42ERk#=*u11KXcJe@1%>sdGTdFGKm+2AT|D^hV1W zfb_3&qd`CmP68e&OuBe_cqq(vsJSS4hIfPV$74$HF@6l1wxCr3z#q&c0pJUmChY2> zw;K-7(M5wAT)N|p8?68+zcd5c7Hw!wsDV#8W9~r|3T4Sf%~u5zvVfrlOy9D!L?j<+ zUAcM6M5kdGiJqQbIYCm}MvsG8#O_)5htAi!@M`lEo(DbCVK`EL7*58l`kw4V~CM>6}VUhcqNk>!Hl7&#~t)t&}1w2 z+q8^~jD>dnpl!=OPPudE7flzX`XzHC7{o*1SCBl80Uwt)Z{MzDY3BpNRg+YFB`HNz z0#vMSo~CvI!O2~_c7ei833^N+kdP1d$uJ{aGZr~sTgtIn;W+Zp`7vzqtQvH-HBk_b z#-6~X*;s*0oUwr(?Pmk;qd(q)YiF1oAVQZ=_XiiV4|h61rej|>J?anRY#nWF;FBF* z5EBt`ruH+qk<$gj40PCO?G<$n5coKm(bnEx>t$>E8XiVM0*Hj;UOsUL&{!oO0%)!% z2zt=vEKSg6il>?o@3nQba&z^D7g1|2?E%Y~1h=^R63hx*^K6Z0y z=o17I@Zion`r<|HFyMY_tDb;;58RT=Oj>P#S7gdrhdg;sOWnQ~b6k^e;!kQ}}#f3MkY%^UNv zv0b&hWp2(2LQ@J~)YwJVM?W6EX<=f*5-kaHeb7As(VLdbW}xcXxq)g@KOvEm-Gl|D zdaiLmK3--Zwka-4=IOD5c?D14r3nj5VjR_`j;-aX83vovFPcw(wx}p3uRI3~LTmwy zWVAIkS!qFN541I)%hsV;VX=Xs=&$h9jEk~hKwkM8@wIzU%VKq z&D@a!@-wXV<+T|hQ>8?A=n)wk5IHNPAOS3_AUMH*95h*gvgbJ5mQ++!q@`EtoX*Iv zh#0)b1Ky@}WVYj<&Ie}P5m&t=a6glWF7lIyq9KnS-7q{0iuy2H2!d#j2{AFpZVG|< z3dok|FvyyKG$^luU-Jjpl0dxE(%AU4>njb_4nF;8R3K+60HW|L?Cr^5_oU!oVcJ9s zol^LS(KO-*!O8Ur^BZ)>;$Bf&(>A^q>bL`0E$UDW@#qz|LgX#YfF{Mw5Hc47 zV4{#Q7^=z0$`)$>SmF>05)W5!5{&Q{6NKQn@$=G=@TaWb&ba{_&w}~YSfE(g_+S1` z6JPe%uVx%YPB8I}*sr(;N6!iUum8OyD~oH`bOm})gzbzD4toO*TLSw&c&HxWFFFU~ z!jPSK`M`fcKCk3__DG|C{F$P3C*jnOOmOlvDDw01@xgGu=uQfX0qx;TNnzna5aTf+ zoT}PJpb~dW4!Tq?^!%zk*_bHqx^775d%XZ*@$Vq#Fsr`?sSuF~Irv?s1+T9Dn)-V4 z+qWIA@1U5Z2V>4dhYx=((*SD0ZwC|s>1Rm9#_V=dvqu4K zk6ba)Tyk(_N%bTwyTs!@B~_PEmU9DM#Y3*|D@}OT#&u`Df44F=hIVCR8d5KRBZR2l zK%O`9!$l0BX_yrz8_~0Hs*~S z+TV!vgKhs)|2IgH&Vj!r7MYP<$VUBs!I_3*AhHc?kWvhp1V(s+7f9u}1-%UW^cZ*a z+Ehl>3TE|Xc4m&2vgz#f1nxzMei)*|4!g<}m6uKFbrNFM2b$e@e*{D-pY$d;f!qd& zjp}9|Rj0CDk^OPANYXc}6rgYZ+9>_(IqM>+TaJy)jBzkHk0xKEbL88k^#iFwXJ{xuCx@Jya|Dr3#_0(j?B0gq>(_d zP$q+$m$%DG9?GJEVQ}eUJaGb2F78gCR!ky3e-Jv{^PxaBfV2+OQ*(0{!2}fIN<{tP zBS!=QseoL^Q4*q8#HLN)2-vghS9+B^ehlb;1B9_!TH@ALiVmcGu!XsMc!XU-R(=IH z24u@#yD^D@?gF0UCR+w1a%n#M;?gPyP+uy1oV{Qu9NAT>u8UhoW`DwBQLn2!@k8IM z_Prvcul9^g&CASBH%`}Q5K&$nzp=hzI~ zh^~%~ex&g|HZsCe2qwz9qp(?8!{{r^E+rr&;wG6mn?5LL8e!Pr@vkw2%w?N)NTdxQr%VHWqagOllatge&|uancbW*% z+D}6hO}n+V6^Z3EH%B}p74QIM4*!wDeRVgxtwL;pB6|S{TmkhAa)}6acw7`XSE=pttRQs zcx!grN>4yoByYal9L33(Ern`T?RTM}QM|fpWu!C?w3V0rf1*E1`#KF?FF)Oaykoi& z8ZFo0iD1@@EV_pUOLEb~U}ik_QIwd`QS@x?^+Oe}5A{lJ0VuSXZv_=G{G*CtiJQOy z8oCCVGRf>PDtS}}I_`%LhXeHlqK=fu@+*QcfHvTWw?)`>_AJt86_-rkeSZmm3-rRq z#yW=iKqnGXFugc?ZVbo|sZ>B=VeC761th@N+mY7;=UOp(%b6BRy02dy>v zuV*X=>>E3z#GhkPWtamDg`*QhZN|c9C{%-OGgcpSc)bPn{AsE>W*XBVS=rK_la=)h zKoO`20eG+*tb9E#D~Q8_&N$F$&hUD8dQMMHHUxtE9|^pPt{hKic@IVM*g@elR?u)Q zvO0^wkn09r_BIYa0QwflwgQkPV4DP1iUaVq`Q34`v9ZCy%Al8wLCD+R2-$1TJ^<16 zc>eG?V_7b%;-n+|@l-`o{-59IPW6g9I4gxIM0P!A$hF{aRIo<96jM+9L>&pI!)JA;AHwfZ zXSoP+3>i%v>dg?(b+g61DCjmLPfQQg(x9;o?aAn1*(Vkj7LP(g5cFj|as~7QUj>Do z8ho_6iirZ1K*+`c5h*P$W^0mof>PYVLMK`A2$Qr8WA{s7vi9|PK^qBC;=Kg&L1w^(QD>u)pHLLY&o_L%DMORJRvJ3gp znNTuUVGchDpU)cnen}m7De}Z);Ur1U$&doAJ7aRWG0gb9uWuUHal@~Cn8*bA{tUxS zR%W7DE06NV&60Xy@V&SqBLg}^*T75zUfmY^D}f^@sM-w;)I!L5hK3ku|C{<-IWj}G znXRW^fEBbM1=F2-H0_a+)J5Ai<2TyaCe-?Q19O_C?s?O z1T}$ael-CS%y)QwduhKF<=(dnc%pk)``(**mD=P9-=a))9iPJqCtdZU9$F8OtX@(J z5%A;IV#bcmjQ-K~+>)-55LVvJhu?XP2IPPF9B`+>y8~!Fba?dl?#DhH=>}VaI}^IT z1QHZ=@m@eZv39EFyVb}ho-tKsBg z{hR*YT&k6-?v+mRqrTTi!%5z;1wHjggXzmGSnizZO=IM_w!(dMvwt!ARjD~|SCDH{ zlz~Ug2{bD)bNM+YI11cXm7iz#?|xvg#=|3tK@F8LIvR9QA1PeB54Pi1WMxas%9=z@ z7d{p|76**X0=9~H94==Ol^0kCwGM#!U{w#9h;mPFZwUCvojG&n)Ttq;VPLHy#Pqpo z@Hz&FYR8TpZNg*O4OUANkYNLH7~CSc!3Tz&{UdlEU%#G{n7GIz>Amr#xfyYH0iFcV z_kdUgmR@b=G0@W}5)A@fU3?LeG%qg^OJ2yAX*|7TcKLAoR!V4yMrF6Anx%#KD@Wo) z`DRP<)WPsshUBSk&bHw{>T^bm*T-60m?Tm-HuZM{OQ)*BL7%I)Ifk2C;Vptl`(Q09 z6(CDd6#&-V^fF78OQ{|c+ek!z2Yxy~;_V#sgHmFnp{cmkY%kIxIfWWen0P1Jh1pMB z-8xXkuE`vgPVILyL67}T#rG~=k@%y;JRE*RLMUmgPC9tx9wW0SA#K>oJ+id-p+lN; z?t7}LeIa^2s$wL3VCsAEOD9zOH~fl8=N#${lOpOnjoRFrT0WXML&n|R-90%q1v!~9 z!o>Sj4dUa})YNSUT4-r$p?{Z|ovmS4o{@oA-KtPt40vkzss(#IN7o%O15^f719*|m zF^*%6M@zl~3W&6ujx&lozbY%+gxpPHfpigW=G_U2YcKR)J}KcdXX0DRAIofcs2fON z|CGtF+siHrl@lu8*7^Cf0DoL@jr+@f=bP4IpH>$tdh1R-cbcCPkz)4dclyLCU}kM; zT@;1#>`&*T?_AG#p6f&xlwiU;tMBpgBtVRJ2U(@YS+|FE7NGRJdMZCXrK6AAv!-f; za`!cZjg@)VUO0gUSPXsm`=OUo1BLyhM?>~g`_o3;l zAUIUd-}kQ98<*W~dO?j1Kh_VU8ZGMl0s=+0U&n!84*F>TCU$)J;@toLqR^gZ?f}x* z+k%4fii%nyF!cbajw^zG0;v6WTEXDVSC_TnsrEV%xlID$ZRbfzNj<@0muU?d89%RX z&s%8dWJTUjk@u2URdwis`!&-{b8cNTev@;WdlW^GIw< zoYVHce3^CTs?8aeZ-p^->k-aaA^|iPb#!z<%$SAaA@l`C801Zka?oZ3Y&!?| zlrC#YEkv=?r!NUMG&h&K&L{jwrvjrjGebk47dY_BSFbvt(H`pyMsz30($!Asl_|#K zAN$iXNuA`L@Y3u`W1+~mIarjx_?>GcJyq4@loXeZWlKI3C|pD7pDB1%S@|dxWb1@o z(u%e~tgSxDaDH7FF9oXOPp~U3^ z>&6{?qwJjPA+LgUK-v;!U`zFq@v|{B-E~?TMo_TEAIpe{6irS}0@V`! z7v$Nl8+1XC>h4~Lnj2byz&(#VG$R~(r4bAGbgdDzy?SbEWw;DVo#pLr=;?7$Utplo zleU4oKw-ZKudEv-^s+&{^A>!}%!HvqR^l7wrq#J%xet1UD`VLN4gg!xj*d44PF7nt zZWpA6cUq_tT&2+N`;wJrL_)0jFdT0+m5DB@5EjlO>&gk zz>WAjb72NDF>?)%0?{M?n}U=SM>hc|$*_4d5KL3}__Bo$`l-Br{u~&p_|dvyVHlcF z#KZFk_-E-=KVa<$?}MthFF690V_0RN1Ej?KH*XxAoJMkxAU#G%-S49i`L#KF|V7lk}_3Lq9D04KkrndIu zTUf|RGHt2?KAt30&PMVdw?)PYZm<78%)Mt=RN1;N zO1ItGwjyl+5fltyAc$l^pv;N^6v-&4fMk#?vFx^uq!uKHQbAG?lqeveA{i->BpJy; zVo5Hl&O5Qw+WYSNoO}PAvwrOLJZodstU2cx=(Ps9Fq zM|4h(%yn0^pT=RwOE+ok_`#E|Y7%2s-!acG;Wzf^be%5K5|kxy8J8OPv!Eym6Wg?6(RCJEsGy;V%X9phfn`M zx}V7($(b6CS0|d8hs|ERXVMz;z4&{S9>$&Mm5#J~((cYOSpxezx47b5tXoez)g^vH zPwxP8(xUliQB^>tH|oS0bZ@S42;sWl&9((-u~@5wNk5H^MSx1yfla={cLH-Go4B{==2^C)%#(^^Q8-Uayn z8x6SV0aE+vBHE7_IC4z(3Rh5DcQ^uJ=Vz~U&ktA-Ycct5c>HT?~5HZO1;-@fpUKu^DR7DujrO@ z?Ui2E*N;VC{y6a2;Mo|%A|rxSXgcIDqO|pEuCwjuxrLP-z4z$3PLH2oNKvzxOMiLj zDg3mG*z)1##U|1v8+Z|_YRjuvyGBMtJulv$>)YYKW&ibq^M!tBb}Mc#Rb{Ua%%EU2 zy^7YU>&l7L`yp$+!cwVMmV*o7(O;?CDt|}=iMyg7)J>KY<^Y2QQ`1|zh3)5ETB}_kTUAAl) zMoz=xMSr-Q5Kis-r_kOHt<#6Lg4Vfj5?00TKWo^L%E&*tD^%nE{LwfUUWuIo0%cy9 z(*{I)_>m|`mFq63lW(?9|IKE#w|R2>P<=*chV^6MMb{6D7-Fp-1)P(Zio#uOsHm6^Z*Ls{Sj%vL2lgP^!|Q8mKGX1-Yy(#R)G0$+ISzyVwPM8ZrPHdP zXJ1yLM%hY=^=P|E)5_#{ZsT$QL&o0E(X4P_YS!4{M?-5lun(Z zh4Wa)iX7F8p1l-K_kMN*V$u`Bd8bnx;gbQb2EdHVLhRzpt>$12OQU6xp!3HPX+|4|A$e2%|m4O6A&n(bU{L@k;O2I|VxDm3K^Ca*4`(?Ili3B!~J;MWIQ%y<9 z9TJhU@!0vF+S?+HXz$@arBW?Uo;HT>Rkw6_42{qEl>-z`mK$%OI zLI5M$j0sB__zle&<;~4u{|~>R0s!sZ)4*fv&WpRn1K1l zAX!7*4gxS##!QQuWNBt%5{Gx{(xv2e=VY3NpX37YdX!%F6eBeKiLyi2m%c zmLh7pv0SJptKbz?&~mbLocOXQN)Y`|b4upLix=PNcHIvd=3pCmKpeP08_~U%dEs*$ z?Cj<-bUt@ zrJ$gIQM%>)VB4|XQpWb*&OWIyeE)7~gL%FB9v>KmK%Sz`-g00$N}+?Y3_GpD2XS$6 zcDuWgJAoUP;`ZZbFW){IiAx>khC`7-B(K_x^z;B;Ep>H(7b1|MK%8;z{flISLIfl{ zU8aSZD?UpTuCg135)LPB;|%VK`LOW^Je8cz>}^RD%4!5%m~E>3Lzxu1!HH0C>5 zcMp%+j~`>+Q1S*AZv`IHSR%@+rE7b&nD<*IV~W8QZ{+ErIcBn3uu4M>DFWAKgS|2< zQnK3YwLO^b=&P~Kp^HQwH@YA*!fvWgU{f#70^^gkYL+sCz`pzTwYhYpQ+&FCg;N>) zH0>iczM`V&Wx?kemQcR^YYW4bhlaYuU^Z)1+C>HRQ@zBc!y30y^^urim;97TBLX3kBVKDDlxYsQV`3MS=)=W5?tcZucWVDZ*r|c*sBDojEw> zj?%b21@P*r^B7e6tNQk9G3(AhE9V0715q4+?1`g$_;icAh#-q=UA7t`zMJLjP_zSl z;iMxrLk#Z0;^_PkS>!V~IBW*l@b#oHV1!MZpv~+$q-}XBaos|f-&(eDd4thc99_w~ z_w0dwUR*|I`s>$6s}>oIJF7QcH8V>nCv(|6oV{{+N$(-_Yyek>DhzwVc*FftaoYyK zHKNwruf2P^yY9MUAfpMKrR#SAQagL%D4s%nD25+{kiK$dVs^rCa8ON8ZvhYR#uHnn zrG*7ZhlztwQ(@%b&VCx&va3pa_l_RBP$=@^1eO{;d1B@#wt?~B19S8iwM&;gumwXk zS}us&(hkZKGp*}BTRrI524ljyLM{gfhnH`On`h&Oo0og*(W4EvU4S;!W<|K94YULy zU^cmrTLS??$bB!b^%^*+?doy-##HG9Fy_!>;JE$B+M>PPk02IOaov`d7xAXG_h2*? zmqu7Wj&V=QxZ7jd-RSykyO%?D_-7e$Sk`uGNW<)t&ve1h=K>gV`}b?BTXhuV&kcq3 z&hXjUFJ`qTRXXe9@g@qWi7Pmlf}lqhWQmE4{;mh3s)F*B8C;mGP>wX4Wq=qOMW zh;$JDN$~K|U%nSQF7@W#{&z>aHzx@x4y&<3|L%H2zB2fu-p%_9uoo!WjL znXt6_&(cTR@}3RAnJvks(ICN)U@E}!(4G5XdB7}crp)|&59}O1^*$5*4<7v7g#N!} z?sTx2K_C4mHdEVvm2V$uY3O-keY5=OE@HBu{q^hPlaslis!ur$eE1yc+%ArfP5QnX z$sd`CN^tLX&(qACj&}zO_~JZziBZ7k{;-4~j`|cJOXk)GYuGNkzr8`cZETh{e|_+s z6&4ooD8QNWrnK}#7I;RZS!2c=xQMioo*scvoSvG(X7RIh93;kiLYPjk8dsaJos-;- z*n?ZQZb9WtWOS6hf1iwR5&8fD^RKf@0ZAF%b6d`u4vTn`hNtGCdBc$n9Xk4CYG?ZA z;0co#=Ji5Naadja80-u;yEsz>PLc*l?Bp(Azkc1pAp`%5oxMFws$Cd;0p&5w%-sAy zkzfCH$0u;d@oOxGTU&~5Jf7EoAED*+t{`J-^u3VvI^uU`$rnbi1Pj$H?_5cBM}ow* zZQCGNzYv}$qTxJcpb#uS<-Er^rNctAxU2Q!*ZaE?g{N9|iGP_y#vMW(m+3glrJ3V2 zqN}a_2~26rKwynl;{eq>OmDdJt>i;m5>=!>!HoD9R+ssVPS%r_tIt8b%cie|c^~<4e{A6x4w= zVZHP_lBNH;12a3RtuJkNY~OwrUu-q;8^VlSw$D9fB)J}ldoxSlNd9DZ_0mKpzxA_Y zQGXcKWK|}r%fvLqKD;YPFRS3MKeTsCj#;_a@e?QVa&y7x8ygvc=Q5WH16LY0`W$%z zkAOE49jZyqmp*lI`JI z7b4j`C%Pw^BYdZHAl))67uix7rk&`BBy}x}G6Ml|Ft9UyQX1{ym$Y+uu*B zk1BHU<#ptvIP(G1%6>6RXZ~1tkCeKmOuQ}5c%mP$<=3%}j<&Y8%%J>y#R-U?*w|G6 zdD49ZoG>(G=B3qlznDT8vjHLHSObI*PT~CJmv1lX>-8aj-HW_AA!vpfZ{FdQY1UxK zIoIek^;_#mcG2L$t}&J3bOd=J^PY~7&z};9M<;(xu9ZgTn$eE&eaRf!3EZ9w= zwpZV0cJS6a;h`HBl{!4rL8ieFtWZ$hmeozO z_@3jaQhG`?&3j`Ow}=HJxDt*Kt9glw4 z_OC~%aLhGd3FXuawLNJ)9G8J-TcJ)H7~tN#Swyl_lT?m&tK9M{qNs#6adH~l*pN{g zDOt1KEN#r8vuaeMRO0MyZDF8U6HHWvH`!Gin(N&qp|gvwNz%{H^?Yb_>Y*S_(xxp} zV>&{@xWZ7r{M&uS3Zd&PV^e6Tj+~_jG(y?6Yy7S{aSNJT@NOv_8{)WB41xJsI=UYX z&j6a_-P^a#&H%l(!IMoB$EfhkBc7fG0@whJVFuS+yfkue?G9Lea>1VXPmUk(LysU} zK&`L%dSQ(lNE`GHwYR8pW-wJ)LcDdb{@V!Tdv!ytIds6f=)s;$YShUCO&rPY00sss zUBQ#FpK@+#(|#=<2^GEkD4O5vkf5@$eVR5(&VMh9cS)pH5oiHwguorV{*S6KT~7E@prfYt_U^yF za9R>y*l-!Xr6Dt;D?Hn?j@ozAggQlnM0B>7BNnO26I=h;TUFTlYk}IRr@ffPP}|#< zNCw(O6A%`ze0k<>qVeke)Y4?k{Vren=nVSAYx0J1`UX$pek&wn^M1Y#^1ySIB9+I} zg|hZDUpWk_Depz(SQZ-Fy)76LHRac#&A?2V%CxoI+@`G%X$JU9Vuag~{8>6wC4xHV zUVr7leae`bV0&`8iwMFwm`CiB!{Rxb_7?T`Nl@*bw8Lm~A+swCo!eeu=bk=*#RNpQ zFpG!BkJJGRgA5uBAColf_2nx$IAni;Ojtn|B||9x(kI@usv0x>Z7M#YzE#(;3%7v) z+5=nRMiHDa$z-NCa~Zf#+!m;z5kBkhx2bR!io{1{;%9Sf6nfo!?6HD5lTubZ<519){Bm2)k z`#0J-J7f8x8og7o3*ML;kE+|;1Mx419pnece3j0rtJi(Is(y*;(}i<~KUxppCv~vo zbj#;?iFbv5APoPv*|p!g;1qf;l{hNKWP52vOLx;;dR3*Et%)S^NxAtizkXca9Oao? zzM}B{5_V{1`mbXNqeIWd)xxOJcy3{2rPZ)G<5k3@R=B~)C@c3?_pnMe+0CSrUuMhs zIWph+goOV3?C<&RSjqB}dq*CV_gff@)GXYN@m}6dqj_pEoN2PG_WGHoFO?jU>0+mS zdrq4zj(?$B*!M{F80p*3Fe*>1Eu43N;jm_<)LRHSe3(M|Gg&F9xwQQRgcM!OVUW6Inht(Dc=KW8(; z+H9ccFf8p%D|DVAo7FUI+-T+T%=z-IiT?h^zWD1CbY43DHERWiee8s3$qC==jrpUU zv)QjXdP2l#3)9kK9a?THmD9_u7oR+0CP)ERUv3^;CV>=-Mw zKA$wJU0-S6SYeSr+B%^caE;~3D%$;Zm2zJ152GiZlpa+Iwr!v6Opi#vxOvV++_u*1 zwDYUUYPOu`g|wV&JY+tZFP{4svCsG2LUGEb5c+pv7@nGD)1efpBI_~#^&0}YtTT#AcY%{yKgt&NSz36@0ZZt_K9aKFdG zOv+632bP@e^q74mU)0EhDlPNq4|u_vIwf1!6&SB(D#*9bc1BDUjiRX1H@ul}o$hY!zdYR3AT%dP2l?$pg$xbm@$#4M_r zu(@@-;OkUS|3?*wCZxGiSd7WvjLKPlN3uQjS#z&_G<8^WV*JNmb+N41`*F$gk!4EU zSlwl93;mMLXVYmM+bx!vAthd3+4;@Uv|-(iP0z0{`miGWXU&~i zjq*A9qg_IzrMmxSoYLQSBixU~OIh%+cKb{@@jJ7qdbFvHr1ZX@;x^^+^F)7ipjc_@ zJbs3Sjl*2GH<@Q6+uY3h`QU=~$(d1Rs>LOyl$ozo?b~Hc+iM@O1BmTC>n1~go8G}m z%BHVgDor~RXk(@*hMs2;dln`J-d?p4i2?CODk{FHlEuVq)@vO}KFxUByty`&mB(+T zpFZ|kzQlZ6Z_H}?FzIw)1I83P)=p8hJR6gyD0*Dvpxji)i9{a-G3f$_k^0^4^)t!y z2B%EVZ}l)55Vl#N_{v)+c&LrG-CwZY_1k zLv4KJZZJWf=};XQ3+5}>v1=%yE1F%X%%d#k82U5k{RKXALgiM(ZAd2yBwvx|T|>;V7ul}DSkfs^xnsN^mY z5fOQLS2zh#)LZqMb89`A&C|*ze`?Af?O`O63pZ+-G_HJBFiZQoaG>tEH%nssyAamh zN71RTwtU4&OU++KQLVPx2PM37F?q(FK6c52^1gtvPjVrx=VZYnnQ^Jf=7_?4$#W`w znbmw`KF(`N@tWT!sOFmvC$U;xNi5H#p~CG~4Tie}*S2T4qJExO4LIIJU??QWeBsjKAx1mcpXe)*~tmO6gj%lH<^$x=;%6E;Q-8juWx_^61VTzyj8h5cK`m{x071@gOWIrHi(G>sF#D zg?aMAuf|74y7;iIKQsck0ap)bHH^kZ6Y=?wx(;p^5btp~bIy+LIpDqJKm#b8EXcIn zriPx?4gyja2!%In;iv!ly)t4Xl0nK4xP@w>&`un;2~cOCC8`Dn5M+cJ7ZCBSTSb86 z0HcGFz#wF+c%Ne^Gs~jT7Tj5Jz89$zO$PpFN991I!iA=sqLY5yA0S_17vLBHbY8{b z|Lto>4xzBQ_=!++b*2l5CdGHrV7e}eh>zcmjNo$dx|G7T>(?Wd&CtdsmCsPq?>IHy z&d4a{MbK@xiu=kE0s=<`VcOYQ?x!?=&-oX&X1)G)zLI72Gvi9xTpbVrU3E?@(Q+;dWiPy9 z!9A$CT^86L=Mxx?oNd?dDoX>E^yKZ=r}erK#c9c|AT7c)52HCOKrK$G-UYeG4eGyye0g)*WIIJiOQ+pCE|zkN0S2+C zkm2>Y+mD^9xk+ihY`lXRzD=fKxy{(`zb||@&8N7!aAC{RZclu(DWCKupiFS@1{eWb z4i68%b?XSy7trd(n8G7tPJ<%0mW4BJ#D;yx z38gdBqgJzYO1Se692|ZaNvlnF1Lw8Kz3^zy4F8Ddw9Zmv^unQJm7X`J4=KD3cLS1-$RUGv)u8-SQ0+F1PZ>zlIeHwa685XA^xaV)A@D(Q#yxITsqe(b>m z5jX*)Wr{Hb#bM^ zwj`M_9rBJHJC3Lv*?XXn;2}S(>BrG@Rz-_A6r!#Z?F3qHNBOqw4o^407ic~`zJ&q8 z0A@8>A;m?m4kb5-1axK$hZiiIoiVdz%;DLK_aS>}2*24~;=PDaE@%%wzMxS94UJ5A zRe=qeXEx(1EfLybOm){Q7|MJV6uWI`K}_V`Jkh}~Es3=IyjYU2*N?Wo*Se2<4XiG4 zpcGGQ2LT#4_NAOLoakR%<++xuzvU0ZTve(&s}q}(sLt`|;$adm=SE(A*K$Mym^QFD zTJxN3pcR4ceE8!tuii?{fhG8yOeQ10rcsxxgo2p!B)$<}FRuWhl3n?La70Naf3HqF z((nOXMW}@FDTHU5shFC^3s#C~hws`1td6@HB*evKNRj}Ac zL`EVQDGo~?BLNo2i#USn;3xIh#hZeBncaK$D%tmW-<~juE`7ly0R9iv#O+_I3~=X> z_HwN{LI-MJfBf}_K?fq=)LttV7;5V=6JtuA@q1sAYM7L=AO(5Z+18sPBH6_X-C1h~ zt|5Xr7UFuGVRb8K7Z!N6Qe?!SncKa)ECdjXf8aX`K$$&_$vRXQd@Y;Izq<<{x01ll zUh)3CdKZAO0t)ibC5+=zkxUQ{VZ!0 z!o{Vn!?TUb*8x#Oy7ER~lO`tQ$?{$AeMpsCq)~TN&IE^7f6C}!Hhf6$r?q5TE4H3c zDZr@=Djq~zPc~8XG=KgZ#tu(H1}id%BY=h=!&E>}Ff+NVJ?!-|f?uFZcHb5yVBPJa zUCdjxiBAWsea=tx!^C>@=uwBEx^nO~ikBGLa&nuZTw+IQHPL<(oRCYw1B>b2c&hkq z$Z&3Lowc_Q43hb9d`{fs@WoLLIiKzTo|KY z`;M)9eaW($>TnxV+%#K0PV-+ho)tQPblFkd)QgaG-IP%zYDp_l{t^z^>FKA> zp4E^`+ooK8G0jLhpk9NagIioSYoJ)NnUBxfbv)%#9>h$EPtjm$;3w};hTJEicW^La z&Gr)k33n~Any78pFwm)7+M;0`_K5EFmEQ=91UeV z7gLYw2CQs2-<3D+sh{>?LrSA)rv@Q-uXQg zw~_LWxvo=*LaxXWdq#++38oahwWDJo`yT%-sGPlrZGQOi$MR{g(Z&a0s7}0C=oy*4 za;X+N=KA90%a_p{$TA~ZRLCV|?2BW99~IbP2kCRRSU+L1Ne-!dy5;4Jsnq49!Uf(V zcX>SN;dkd$g_*6N<`j+r628&xBa1m0dFc(Pvvqr)?>O!~P)}_fX~{k-8SYK;LTYR| z(nVaYpPXQQ(zRmbAW1EM5S05)B6>&H8SATg*;L32L(!%`Vs3K|r?Qe79^toDS5)}> zAaqYYHO+Cj0qlbOvDjf!s`iV+SxBiiI=+J!IbYjseqr0EWTqMTPPe27DlZ55$oW*Y zmdjVRQt{3&V)JZFq);9{ymFCARo)`cJwKIKDDIT>d8UvYlG;T>^&BInV%Sg5Nb%!V za-W4|MK?Rt?y6n1bxT&VN&z4yrL}FJ`Y%!IHOs<{er)WoA!Wh9mX}-A+%Mjf=y3F3T?@%MZPqd2*5)Tat*$*jTmgCtNUW{l#gtLQ_6W{*po%ng`o@gUt zqtLV%7Gt&n$OyB{W-Lo|G*a93(K~3yE%SwW+~MnDrkJ-nl7k$M`0?E#FKQ93L07k^{N=MgPhPzm_3Qoh zV@Y*?xd8?0)ihl&VVjU{*R;&PqsMA1Rv1D8#(H|{a_miV?jktn>}joJT|pa<$h#DF zjxL&AJkp7Cp7kmr-I%;+FT8vAA8kA$uKqKA`l%4A5$x!ZoZQ?Y2l*-4n?dfDXzxId z#Q1^W<;@8sUQuEWXMJzEBAqVIYaS@bwb%?VV5 zxFb4i@+sIlD&4WCk5=l=(D4i?OI_>iJcAFY$@`A&H6HO}-}1~bjc1+=9nmilfq~Cr zSFhiBW*=91$i?CS^sifaYa{|)UrRAXj7&wN7a@Q>$IN%3@3dhj-cZ{>B(b2S$lNb8 zr5+(?Us7GY7svdL9fsi4K8hAhl<+17E1*m3L1@?U`S<8z6G?YvvqqsjKnMd2pn4WR zE?+Lgy;F^RPXLlWpHPI&XogOuYM}Z5S_T=xubb&DHid!udcKz4&gCxf{GuSf0ed2m z+r0UZysL)MPNqBTt&T))B}{?sb2r7K%s$`#dDW@_3&G&sVq&xqN@(aFlTYZM@QOfx zRX(}QbMpQnMX&s;w?DTz^Y>9_>0_QIvkdzqrygwEJ>Y3q8ym}^t?odk$&>i_{9Kh( zne$&zw7+4A?Mq=<_Q{-^p&?8K_OdL*jUf#vC-@8p5v&Gd%)D6Q@&G%MP3uc>QPE_S zo8X(13cC=!Bx{tt!NoADeQGV%OF%93m$R^Z%hb3j_dwSGa6E;&@@4jkfY#bxcj z%AvIz_Y5^8hktPgfmHr*TRu)qXaUUo9XKThfeKT_q&8v-LZ1h*u>I7GA(|#wm%XuP z!L-YIa{l&Yy8I)-i|&+~zRM8Ocm6tXK2ZJF$IFY3c6iKOf=VdzO!-5``~g~+bS0F1 z+?{HvYU=7S^Uh$FpfyX_p06*~=7H_tRM>qIZ>HrUd(QWhSrw=Kpnj2hu=ngwt5-i- zTZYP3*TU>7!B^UkY?zmStyfXAM;g@-5cR3T&X z^wjm+XAWZ38@YirOf-e@%763z>z5%CF7#ZiJLD}n3|JOie7vK=%QCjtvoXaQi0*>* zZrq-%fV3!ssJ9Zy6#M@U75ZRKT`PKO5oHC#HrRdCI2|lUux@tz=s|wB^JXz zXAQfOblay=83l#buk^s6pa!8~Qsqwy?M9&?z@91xn!g6j;#KB8ee1X1yqC@Hq}`oD z&3BMWpx{;KinK!SS4Ao2q3}kTQj!uYESsYJ;w@&_?5x377clqR#39UEcmUK2a#zU9 zL%(0Wc|T_Ci7a2u-HFAcuU~zPfNn%nCNUdq*dcdkVPVzJsP>6$Q4g`}grNIf1J5d> z1Wq>Z;ejm8kli98NtAh>SFS^pax{28r|XmoX6;X%df+WjNCP4wEOR%%0pDL&CV=J7 z*|CO`tpB|8ib}RMmuU3FyRT1B3up&lsIjjNQQcT(rSKiVCN3b>A}l4(p3tPg%cdhSQ_Okjb?iq!CO-DFmRD=pl~`W}n;eRXF7&)Y>@a0dAd zrR;fDW}i13Tgz~(Xo8x)J`?e9h}q1}S!D+(HX>rPVOo6r$=qVtGB?>1ir-zWadkQ@ zokRuSDT*<_+UEAzP(NL97KhSb<nBHUWGGqdS zPJMb0C%em6M^g>PT>i{zjxe*4VG^lexb`xmiDC7)fh@xe4R8HpG8e3elx9b!MQ~4@ z!8`3XT1ovU9zB)|c1Hh>G?Kk(@o)fzB9`f0jym#`W9?lk@4}}B9UB+Sqwz^*y}}w@ z-bj}5xD*XF6Z|iRlh~9??&iKPrGzW+yW|P>aE4(Eg1+UMx)Xx0 zUse=44hKQKTUf|y(EZ3*-tCC#ZszydYNu?_Tqa3Ck!fJU!Y~eK{5X_m5Blai~aFFsq8EvHJd%g4ZcNzi6uoT7m4%s1H7hD>W2 zcnF4~spp*1bW~PRak_q8JHxaFXDzz?FTK5-e0(XyTPiBYZ*~cYVSTCQ#eCQLuUiIN zbJ8Q!^cZc3k&hyZXLO@zqD2!X=lUt+#i>TCuVFv|Q1!n@8_?90gA`(zk{q3IbOvux zfA@)dzeL?$>t8l(h^aTnbs|OzxEwaI!nwY1tUf47Wv*3Z^_=v)MV_Su1V|z^SZ))5 z-RIBu)jj|>Knz&yK`TqhNSm^(!p(}2=FL5ySnw|oNbX25j`^ZyuC$G$XxIM>t(mB%*rM# zp^G*-_Q$p(JwZW5VNA}OPh_4RfP{T6gxj!9b!ulqh=NO6>yTYFG#SLPlHy#`Q|bil z3+a5Qg&5eJjPsyQM0Z8Ba#V9(v4^i3+{W@7JC8J@+aGO8k8^+7W50!$*TVZe_*FeS zB}54hv}C6uFxuQ}@acABHu)QRI|8rx#$;s_R|Dm^Y>3YHagFKs%a$RY-K3leyoxA{ z5wCXn;^Sov2SpV=<{GfUhHmNkFZ_e81uK+QzKhb++pCjC#9*TS7|dS(mdBe{HU%xs z#T84^7$_e<#2#=iS;ltxHmnPHze^m^-+#kEtaAT@JNm|y<4^IQ%e;RRr~jsx{x|-_ zv>yylZ;Bhn1!d|c=jFMXnhv0ZeksB>?yz(re|`E*uG--#!~^*Ro;r_sZQ+88V55#l z!y#tYrs<71$~D8iz0$yrAr@>xu8YPt+)bO)X{b9y_gkW@h#3Va`cjpx)Ak*ar301+ z$yab>KX;unpYwU|xD@jsOu)(++EDRA^`}E#++P0ZX-G)OwJpyvL*R@VHq$;oOt7=% zypj?he@<@Y%9Z|-D2|o^+qvONNPv*r1+gyL(_WO~h-W}iv{6*|-M>Hm69H6ZBDKdR zj1m!%G-H}lM35J&nvdV{5n1uEFojbX3-@kl0;2^JjD)x~%-wix% z0TYD2rsW%!u5pY&rm-4$2Baa)gTUrXaC}uf zWLG}b2V33}yZ9D?$PjP=pT&YEXo4O*Jp#LbSCOM>zf!eHuV&JR&gBc4JU=y`wK=sT{%Hd33& za9XDesZE$@X#smo64QJo7Sy+>W_mppyvd?M~puZ%FgrhkPN_SgWE39#l**W+6}!(rA!m-#Cx{l@2qlOqVIDs(NSrzF zf}!ViyJ${a`tvO8a|bOF66%wLrn+g!VU88Pp{}Wk^`T)!)ledGw)}-U8B>i0)E*SW zy1y*RgEzp%1)Ss*r&a6A^L9|AUQOn51uaJBwX+C&@51`U*C2ut--p3okcw)~K@jo{I+f6< zMp$=@H4Y!8+0JECoerxS$U#NW%g!$>R8~3;el*6lARSa{G{>TGuZ_(Z-V#U+qID+v zA*2Dfq@}-GMkeUb%TYUb?s4s4(B4a76iOk=SODFXVlBX|_97u4ObsD!#($&I2v4%Z z2Lt;AwUymf-k=H-}&FfU(ApRNKk`0B}ksL?8alGMAWd0U~{2%<2vzmK$ z?+(G>RGf5pi-<|YK$52I6}{|F*Y1jnPpQ)@DhCuj(AF|eX8L_LVAXeySnYRxv_M4c z{;A>Da31ZWZs6h~9y>6791+OKgD{zBWQM0kdR+M~-q<-A>KNFop}Kg+x=rHM_-~)Z z$l@Xf*4pVModc$`ZDXsoQm$7Ldp3l(-#6dJBQ}@fLG8QzGy(8oR_}sBXaHe`gM3<( ziZKa@ngsNs0OS_ECDk7r@zSK7Xn!I(e$WBh1)E)swXR(>sunMl2IsVA?I$ZZi&8y} zLlndH2>N7+bX*Q*Fexdi7reXGyv=nsjt9CVV%WRM=eTarALJ&KkZWy*M50Zs0VXN0nq{h`unqBgh^q zUq9Z`!Q9>-sI}U#gt6TOB8`u|`HRyn?n}n;X=2}hu8WeTu1*(2kj*@aA>o9y=}Sx%!mI~o0Cb4P5HeYwrP(4qpy|d~{tQOxjgE|rbak!UyxDzm-Z4`TY7i_B zBjSj?dGjVF!UPtgOi4gr@JvN0-mid&n(c$9eAX z`^~*S-W|1`xlpvMZ?C*?X`y}cAnU1g3B|*MJ+;u29PP=NLo4~wHhl;oU~C_(i5H~7 z608}I4}mH};L=-mM?laX32$dLwI)oUI~TWZ-H&RAQ(W^I)Iq2|04ZZ%HkX~c?uaTF zGcE--A*+j*SNPcE(A?Y%>!VIF9(^^iYmuWn5I&41&`g<w?UT-s5ziVoIf858^q%~y; zxm1=5jkR!H)x}ZHQ~8if0K0ezWpRqdZtLoKFbL{%$Gcsq-m|IBO$$Rxrg?;lJ4zkzt@1qp;qHFdyK=`Js{elVi zloX!USQ?$&Evw4u5FBd2MX zgmaAGtf`p8Z7@1L0GF!`6nA=>?Kf-(U)n|6hEhi|V-U}dlvp+^A_ZDqgl z-5G{X61)7q#hHY=K~V?N^NeU4Z?8w}(VNujy3_&>#*6jk8++rvR#^q6c9rnn>Gdf5 z+|^)v!dNePPqKpVti4OlXAbhq!oHHB`a3;63#SjVrnB!fOQ-PZzPNhZ!5qhZ*DWj{ zwhS6kyXu|4K-JLzTtux;JkbsKt$Sny<}~NQkBeiDX#z`@s69NOE-SQz6~gb6P@Eo8 zj2r3egT7KRFf=?|7_+pMg88e;2}VzD*@`vl%2@E%JDL1v;f4ihXyA{&?bYDY)AAQW z7X&x6nO8@Hxf0WT%*oOGIx~aZu^A^W&BeHs`D5JhSOAx;J8`sZoixln!hFEr&r}>q zXPF#f7&vdfc%vkvqWd1n>q6XWMK=)CyhW*GtAXbF>pdsiwv_m85sqxeu{yVv_=!t` zj>|>(b^n!75WPIPn70LXu;-hg`2ru=E5G!`PUx(NFLuG1hw>Ho zHC>a6p(mok3v<8jT~$ba{`m1_SknD#(b2-v)|3u{;Z8|6ZK!2kz1HS1Ox!a>4Ym1% zK9LcFVo#DQAtb$(@hu#dNH!U^gQIHco7sm}t!?h&S#rx+I;NGlueWN(;(=GK7sqE( z;fmCB$e8viT~WBqi@_?qgX?`(sS(T5^ezFxWCW!a%vW@D7(~|~omGmu9l!*kK_-63 z&9j7F=7TPF0)SE=Q^FUFwb9BEA4dHD*<^?_{C@+p`SvHmn>c{8PqZzmn=V@gWIDA2 zJ{rFlnHT820_100jVZw$1O=5q|BL?+vX-0d4x?u^Ylf##w$|0>0`xFDZ*2T*!!byH z@7#eZitw9>D$&shVI0Q~D^{SQ(l2li&0exaf5VuT97+|KdW;*%%g0w>RPK+~3D^!; za-(b4_(-1Ubd;Uo%y)f6M|fk*@#yl3imqf_+!7m%8n~wwI{B`Se}ZV-^b`B|iCmb(q$JD03-!ZO*M9qqJTp5UM1v9( zgQu^6$%n^>URAO+oAz644BI>k4tBe719t6-N=P9?`8E?DPROqwA3~j}u9`+4?xJD$!aZPS9V0AUTylh_#>f9qYkG!Jr%%dc)4 znwkfyak>wWTVNC5?Cm2!esR0)b|Sy1Z#moL_mOTW1N^!dR^W4cvj<=xaSdczAuA}rz3)Z zDgJpS-=pb!{96$qu%gLKMGZy{2w$O{7xnc=dwU6|GZfQA83O|X6o!H>gC7Nz3#Jr`M36!cTD!_={5WGhMG>4yJ0U=9lTggY@;coI zID*iY2qlRs&GoETM#g-kGylzcCv?FV5kzX549)>ADZ+L>?MGt9pTg~22V-vGu~k3+ zj44ce4xm}a!lWAeA`!OgpVb^@0eTJf_lqFQlONwcm}(?zTAV`9!8=q;(Ri&>vt+w8 z&x0Fx=gyMS(o#%&Qyfo2Yll2}GMi4Y zsdb8gKL-!7 zwf}6oYpgXC=t3-X6n)*aRU7v{szRi9c_9D?A^W$#uXHRRKaY(qK~Q-03erv(*@1|DOwzqp?X6Y+G;(GwcnZ5|a*MM@@O`gU+pI5s4;`I-~_32|Y!RpKP2 z){(iEEnocy18{5THN*`i+8o}jyH2``+5fB+*f>cEm{5IlJd}Pgzp!kBW z4|R}Pf-LLZKbebbB{`2qyf|4L(Oc9zM#!+FFb7dn{Kw0^k^doCZT`wnz0c!v6;9gxea;ncB!H)61_`C5Rz8(Gw>uq{8qzHH!J4+4JE3eV5k$$Bn~!T)o&3z)}v&39^kO{s&L? z+At06Oy;$Jk{JrVTxR}+Gc>RbG#<_iY@YT103*6;Y7$7r;o#u-ACV7qv8S(jdNM&! zX6Pn=LF%NQhK67$+TS2W1OlS8Lo`&L!{ByARkUB0VM3mEbdN5wGc2{1Msh+Hv4!M$ zF@F@1&WI)MHJv<+QvRCC$|bq)((R!WyMcld$_MC^p(TVKNa%!&z@E_4K_X^pxu8}6 z%HU8VcA;ao&*JgkqUiNsy=ILsvJwt;0?{hEbvgCAvoriemu+n)Fyj>@W#^YKo7b(I zoNlo>{ZL&TE%yNonRSnyPz~d!WuS&3Tq&nv8e>Z1AQ`P zUX&xM*6vo^yZnD@*O4nI(r=auVKh?ljaqZiA3hw*!{fTZk~(X4%xj%L|GBqU@We|z z3Lt5xPE}wdVb6TAG%Z*l{qv#cve4L0Yn5)2Hh3Uz>M5_K{(*SRY~OC%uFG(@2Ar(1}Np zmtV#nv~=w^h)2FFI#x415z2SOJ?iL$KABd&2Ez$-*eL8#4k*_=zfx2e1v1B{m~sq& zU=I*QY7gEjun;CU#usmi-XlTzec{ylb?cB&0X+b2@n!cmqS=*}MBIZvc4M>gNB`xX zu~DCO(Vn8q$Gt`nXy6D85Zrw38=nY}<_C66P5pK_u67g8&(otFg&nBXYHDoZU`_K2 z57&eG3LRvv5kp5`yeAleDtJYA-JOIi*8ZGYlydg_IFT%t5KN!8}T(_)?Rz=)%N!GViQ0gdKr%I=q@{LXDwI(wj`nU&v*8vfu-gNwxg-(zpGwq zMAO9kc|1JAn@?WGcn0VOML;3NsG_kcF%b@jkDc$wquxU~8~6rRrhB(wr0lL5JXbUa z5cYR3cSLT{Tgc3|Q6EopORwsxaI5O=E6?2O!Nqr#^wa9^w#-}q+eNPLE?(pkTexJy zVN;*QPq;45HqpLxapS$NYYUEDJLs|K`*~hs8-6mnxk+x3lt`mniDj10FUkjR`QC4| zI-S~J*neN)hkN5rA*1*25BS5Sq||`4cdU7nFkao>-EQl?PjQ#fOWWr85X961ewmlz zh6}?CZP5E4n5o!Do{d@tS23!u%s7O)scc&BJK zIy+L@LtJu4qLLei!g=N@)wL11Rne3F%cQDww7RbMv<<%e)19Qjg4_KRpT-TDsM3yl zvFz%seW1?-HSw|)87;W5(T;a(;6TR8v182bN@2tA``L&dmJp9P93AJi23w{*h1!)t zG87P6T3O!Dyq4HQCOYRDJPq1@+%&Lf`ghNTty^x&IR7k*?8wvXiKpQq^Tbh=>`RPd zL-c(GIyaAcoZ9vPknlxwThZ+ko)yCu80oE*t(yubGoMa)DQP>g3*hz`V)~Ba&SsA1 zox?_D)vbErk?x9nT)BMo{=${ZKS`%P)X^TJ;;hYm4Ykx^i*7P|(3ircgZuKW0vy=j z%$`VX51H?Untndp4WHDSKU>JL$*+-&HG`@4PDTB~`^TvWwiQxtT~EUq50p7y_|7fc zWbhp=EIV|7AxX>~fu%L-b~smT$Jp!$3ub#Gn>B7|29$cxnQ^sUbCO0ZHimpJ`WCRp z*6P~)BDoQaJ(DhA_~c@LEk~kItoY&ZgsE1<~^A}E66|X9pKo9XOmmt zUUKLWf~RFl^HrGZ0_{Re%k~C~)_&nSJW(_a3u)qjOhGkeeHy~9!{j=O4@KuBL!3L< z1tLU3JrS}vs~_5DHc+b2fJ)VML@42kFpkjN4jakv35uo_V3`>E+nHGj)y>6Sg z1%uzA#{uM#+}i?lOLkmDjqTY9Yn|H)Tgc=-Y7H4NX7hnI${3$x8~&bx7ZFkK&> zw9vP+9@J|lML&Q7f4wKnizm=iJGJ<7_aw&$4`f33acQ961|_gWa_-3w^LTYDgnx(o zu3rF3^#ofcLfxbFLq*1W3pnnp$pukpJbx|vT17zx$>nwQZ*M{ww1;4F5RZ@oU(Ds7G8QFm za+yLY@@T#D$h$qQov=a68v!$_1S>SOfE+sX4qyokD|ri$l`5h+tm`COp6Hl^>u4+C zLv|3x`LPZSPVI;rL|;(eYh-3P`~_zL<5!V(6PbEmC^Wwu4;Jkh6nqDZ-jv1}G!stZ z)Mz$ngI9o(*`is7E0)#T_M^xGhDG>bs7aQC)+ADVOnXOD8PFtQhgdjI!%+ekjo3m& zW!NrVr!E|)<|zZdT@r%WY;@FHZ|GtNa|SZiJvmEa4_NR0`AzrEbxX??)!Y!uvtvzGJ|E~ zRGg4yJQ?sXYdtgCycRG%cR6JFk4LDhj&m&j3&k~AZZ3?P8&W9``5V!PxZV>u4ou4z z&qBxK94~ZsfX&?ilf|egW}Yxh3T%GtC|+Y&O(~@hO0W01YkU>_$|zTjI2V6Si-TJ9 zNHj7G$XwBjWC@ueZpiZN**RX3no*t)&!Q9>l%^>R@2{Pr0bQCejw@tYY#oe|d{4P5 z!ueUz5$XL|J5X?VK1B7AFHTb&*S&L<9TwCu-wj&Tc0i0KK8+ukFE;poZ?zJLEd1`p%qU<_+Px@0!PHevy@9o54*$QKFI<}Gy%*q%}> zP`gJaX*Zv7Z1NlpOrq+f2=RrA}91!&h+!?es_P}{7B{4)Te* zer7M<@{wZ~{9SMxTZLQ<8Gm-qX^VccETze}n_y2L1n4o1U)|( z(3OSENCgQ~-B7f;(u4@U0kTa6_H7-@xjMKYB4b=>j!d}+N_?k}xAx=`Q@|1W30#y< zQZH@9uYG3K7{^mB^+eRXLJ1Jpd`5q!!gtx^n0ae2E@Pi6p_=_M3~|zemD-k+O?l)* zq5GvH5c7sX7^EN3DJ|IC42Tp+na>o>5Wb!d=&yl;UrBEncSJ|#+P+=Mu1eMmIMQTN zR4uww=^hXgyiS=A2(!o@_hZ?mH4A_R_?&i98z=}1uz$vZJ;@lR@yOnlw>$Hp9yZQ( z6iB=Gl0U*?!>RcSZ!Qm{^TL#EX{hxjs64uPoG<${3{@1}dw28czPpiqj6H$ggLmUj zhz~krRNjrp7HT3t%0dTaZ*}}SZ%9~ll!Q~Rat6XAx}LvyW;bs)8DqDv5OYecvb&2V zv(2k1eKqG$ zit6Y5YuxF-&A(PI=Ex*-G~y6sc!LR%-5#mLTUYQeNBdwoq}6ifL87Z z`YY9?GuNL2WQEyZXlX!0ER54Bt|g1r6@H9%f@#PUddd)^z_wl?FcO4D;b`6XGioWH zux!x?14THt*1X2m`}0~NW3pCAFAix^UpQ^?H+5k&ibI{L5*q9>7R?$|{gBqvi{s4u zVF$RE8{t@S+~pwi=m>;B3ZW5z+$BiXN}mtweiX&-=lU9&Hlo&=qCutn0$3l?ulqQ8 zCkyzkxWtKmR7K}=d|b=7h*XT+>M0a**|;D35OgyG-!aiTbNXYA0E54jr3tTsmUyDT zHheBIj$W=LG_FBIR57yU21NHjj%8(JgiuJ}9I=_5{@}rbw6yJ>4^bqM&p&4?AX%Z$ zwPDLEe8h4ee2ro`?=^%(t(|BlScsMLw)P^Ir8k+rdp5W+kbc5J`z zP*Bp9r=}UBFBk^IUZs;f^X@s0T;F{LvM67J^dDzwoomH@|8dtlD7sN}<(|MJGaSSt z$7=^tj*q9%^Mv6`v^Ys0HR@6RNgDJLH?$oQ^bb$ccCO{fnl2a=^ats7>fs3#6&K@G z)B41@6fNEL4pv94W+6_W?k0^>Jr?jU)5Ik*YtOV87(8Kf8NKCL(uO!DH;@ukLY#o}113 z=t-kXEbsJHhmWr;00y{-<4#a`csmd+I>gNoZC9|FI)lgu_RuzwYNu z=a%;YH-*fa=yZ>XQI`aG6AWF>CNZ%UxJ~r7JX{}y>mYKvqvhR?K$~NNDfH}Q)LJ;J z^1JbPcoO&oEm)Y5I>#q8$(S`ebzmRIs)mu^bwQSR#QM(RVUF0R4h&B_t3 zqGZ9oUp#%1mUG_b>)QU+-VDt#k}?w`uWH!UvA5xmD^|(Sn zPIxgR%ES)i)puOQkb+2PaL=Q3RIWo4q~$JOU-gWOFsmtysh@L6JZ%s;6m|P~u}LMX zY^HrNYxv?1^K)Ojjn@+n7SLu~wgGWi+&m$UQoFmla#Io#Mv@yx z0g)y3kYVPw*|nRVym%I|z8pBd)ZLRvn-D6Yq%mtXK`s_pCyi|`1z65Z0VZ*0>!h+D zvk~-e!ruaezO=M>!(cxF0zNvOBR&lrNqElI)zRTkz6K;RIPC*PXp7OS_h;aHcjLXo zR9;}+D27f!2ZuItv=ulZDaVvAI0tP9gIG)>H`>t@4NpTc1yD+I{rWkrpau51pSZA} z6H*}W!u0{IL!rqfj$=ekuZL{#5a-osJ!?SJ;RnJd3XuK(AIBJ9%%`2KRFDr^8p z2r+q$#ee@tvA$J}+0J@ZSxmd3;raV^X!;0@er(7aoXl|v+kI$cfNX(J-s$ewL_od2 z*H=3He0CQqD*(LiXJyT$Wn_|AmzRbg*66qNdbnPEN4gICr?zCSjT?LEJ=6mo?!hg?N{Bt)tANY5@L%*R85y=ocD<&6f-c!Bd#HhAztEX)=tJcBFz_-2s4R^B> zS6>;WohW7+hg{@SH_qQd=ak<^*ZnrmzfgKOw>ie8QGP8~L~_4bHO2i?&huaW!VN2yYx`{5&TmO62raJu`<7bIAsof0+58gVn=9fPVw-G#Yr)u*kpt=b}GxrLW+ z%4+K?I%T#@a)&-!nc2?EO>tAszhUS+{N%YT)AStQIbpe9*vl`B-KFBjzCf9|rzPO? z==l(pfkP1ReRyT3NQ_6z1*}LMD6(e0*NDj%wq-qF4b#K3EzbGgOP0#&shkq#Do?et z7)@Sr!QL^Y`_Iq%SqGE1emV48tq50c!VTG^Ot!9k%Uh|C%x31gaUpI4|3J4X_s&6e ziNSCGA-@DbEq4z)z6548l8N-XI$6`^BoCp#{97&S$Gp zn4caoS(-CTxp^Fz#(#jgXVVMTYMt8$bjV?o<7)4I-Ghq_l17S$x9V4SjPra~sx-X# zAHSOtfpf@xOnsea`nd(=`gwb}v#ib(3Mu9}zNqwNM$P&i=5p)<^_mloe9r zAo6SpUf0L=aXy8S2MdB-S>HTcjis`;DX)1+2`DtNroS)^(PH@8Qew5AO$PDsU;pvH zJj(w)8#dY@xxv}&^yxMTMcwOq30myoS+EOQ=YYw^=jtK)jb)QbTCHDm-CXJSA3ivO z8WXOuI31aab`Iz(?;}tbGP&^BjGCIG*`WD9^AJ^~vxQw3w$x+jL2*>rwB*W*f0_j32TE_-rod(lLji^(C%MkH7~PyL zqo0`V=-a$J2$u>$D~aQABKz~oTRMT!g1d+P6~+^9h9eGs3y0SY0r@z{fEZF=KpWQP z?*@)=e_dZGN#8=wHA9#8P14Yygr@Af+s~V#%DJPdp^;DI93Cc!y+A#n0|=LLN8k5h zi@wjMOWweRkheM(OCGP9}+-ixl4 z*k{`|CJm0^Tan@8Q|6Qt)rbjlraU?zZ{)bR6nLlZzk`X{>&3;*FwOR<%s6#67vI8y z!Ks~)M@V7X-#NAu<}w*FKq7_)>Cak}bqe(tm^7x#_f30*zrKx|b78Z${5wpuI9=z; zU7plPu&0)Ch48Kd^po3^Io>c|2X*lQD>r(xhYc-Ng4vGAbl_oulaKUvj+14#ZNmw; zZ0S-r^u~Jx6Ej226Ei8?7Z|^MW~$zUsaj6V3?4L-jULgOkrc|#01^@lJh^;mENO&_ zA(KL%A^66CzCUt=P7l(B&p|Rhs{is$dH1T1s{N+>*iNWi4b7lK?~EE&JKWeyn7T+Y z^*fHnvxebG4~?)@T4xxpihwlq_|X2$8cT;lW*=K<2K}8!`aj~XZhuV~F{cO)_6MAt zCV=6$nxK?HrF;aRTDYK{$ZWC*rc5=P?SwPj@E%L_I!tC&@J9e#h8f6q9J|4LkRN(+ z@0==9Z(&oY#nJRem|U&Q>)x>{VYUS_s!drskARH+I$H?8x`!`DCwUVN_F?3rxpHJO zd1Hh7RPSK$HmnigX?b#W^!G>9)j-xYcuvuT-RXw7JWI?F zyW?y?dNv?Ti65Z`2UoFd$r9M8tKX4Y#oTN{+KW5_XGy}JwAeqM+h?xpABV#i&g}o8 zzx>(78ru;4h9Ria3+v%2|9-t^l2xPoE{BpJY4N&5r{Oc~shQTH9819{1&UEHuIy4v zah(##IU=7u>j8;N?lVS2Z{k&|Bg~&Zb9<}uh`MK010Hf>-8?Ttab4?b4-73v)x+Au zCc+{kciJbT0xeiNjsc4`u1x52ktsCA@xZ0UZpkjgfq#lO51vMmf~4CO!3HWqiR3z> z7s4^qiR}cDt|0@}b)gPWgSq7c365!arPzrelQAE2f6%t4_^q#{5qUpyOE-n96@$}EDo53bjX=oKiOdd zdjle@O`g0a+7J#OUNnnpIPR|$U|Q({DRxqgl5l+MWjTOxDo}W#qH$eP_`?iTT*;m zjP_1OpZf!gR>5fDuW9j{!SuCS&RXE_2`v-m^Sx2Ax^Lw-X~}lN&32Ce93C`EMaa8( z|Grzfnrx_*`uOL<==XmVMw8=na(p>fY_oIKZPqZrVXz5=lr2P&Umb=KsFA7oRK)r0 zf!z6ny*!E|2Pc^{_`jQz#X8N_#MAvNW^uNMl`IKxvlVzi**FnUT9TzZHfl8iL2GR^@zHnH0WeU@jfyI z=Lrs01T6wd)cI07dGgnjZkBz{qzZ{l%E6+CIA90|@`n#ssn9PD^Tee}3$TKMu%JcL;}ctFR3Ae*9XxUG5G-V7;#Jm9n(fE2qp_d5Z&G zk2bS8;xHMs;J(XvQ4vj;wX>qaawWU#R^vzBz>N&+7<_Xi z_xTFe2uE|bPU%PVy0zcA$6)Y_S!S@aW7I*5$G7f0Mw8gsbFdO)U+&HB%ThmPgwxus? z>z^+tns_-n(AeBSDfyQwjbhCt6NQ~_on40osx)%y_`vBYTD81y(|0W!7W10-M?BvF_ut zC6}oWrgBypW)?O*C{~wwJVkTbwJX^>dtF19Yk3JynxVy?MQ7;|C|*kbNxhebC&N?a z>zcCNQqCZWtzi0-+N9cB!|kmBHw;~;?(NB=_Zi>L*HI38J2_VVzTeVpf^`FVa)`1O zdrbMQnp-DtwkcPmC|q@qhLhO>Rsr?LmWR2HkrAX zq%2x*kRP%IWbfrwp?02Mu0qRRQcyq1W7=TkZraF;oAe1Mzo+OA?b730;vk`({o>Ty zV4bp<&TIY7pUPVYH5oSDjjjoe-+aSDP?z2Y@dA*Hbp!idr;x6&f1OhD7H_L3DVx1v z#>R@vrbyLO+$VhvuKqOst1^qm|Cn4OtZ6AEophR=O6wRj)XsFHQXc>L;HI7uiRGUH zF=6M{Osgpxmh3KOJG*|9lJ&+0ZTFU3zenx46rE0Tw)IDIzH>eLgA^rphK=2^<*xl) z2qEq*oM-Bz=S55F%i@T;cWJcIQn;VwtcO@WI7Mx>p|3bJ+AZbQH0qF-a`IKI6w5R?8rJD=4k)zRwC0liUT+mi=54z6x(pk;oNUE6;<@7Z|iomJ1vo-kfj zyiW!&kstC!u|1RP6jS@}%r(rk>sKoFaa)`;t8vE~ciL`7SO?yxQ@`c>zLb#}_0F{npmE5C8TT~&#91(M8uwu98A(d3I1o(Izl_jpH;*s+&RUHV(4L2v8O;ft zX+L0d z12ktyIo&W4?SP}}fYD6*Vv!F>1|O~0MjPhm${8V4n{p9PtcpYN@bksL1Dswm+siB! zcT2d&9J49vtM!(C`q~a1cyP=`&_Az`@JTwV%^}-m*iHDX5<`WEQj57ibCv>WFE=Bvhz(JO$IAtgeKH7q#3_P zNJ(^R3-yV0)rq?Ab*v%xOqlVV>#JXv)${fTX~XTOCst4n|G6555N$;o9_|8e_qwRa zDI7wCPV^Sm+FI37-gcKZ-&OaGkqdBU#Tcum-8MdLOlB5JQrQdx+xhc`yu7H)^6QR~ z2|DAkqwllU(1aB3-VY8o(1lVdDiIu~P-9YFWL^%J&JMrLjUX-L(#?G&i&Ed$P%wU;MtIyjFMbF*-sW7)8zqFvK5zPgdfchD%V{6kW9iVO z?$IJI)eMW$mvt-QZ_FxkVx8)YBBG^c0XfmAK%8H)+Vl&46&Dw8wL-otD=^D#!Ff6k z^*;;iS17D|3o{5rte9oTIN2_5WqCK=#*UBc?$}P}?zcavzhr*&V4Jw`aRp0{yG+rV zBW*uE>8Yy=c9-7H7*AEEAffo<-WMImy))061Bt`Js>{ta<`&Sg5gX0D6_4ct*>(81 zuv=egqwQxvb`z`ksbl0R-;m}O3jO17te=0NR!vl4+^NRPQKAO#0eH&vlhv7piGv=` zr@V!PV@Q8Kh!~WVQAh`WI@G>t=DJ3TGELu5!Fnhm#YExLpmAOTUf!xB#>7EWkgP}_ zV6*OwTpG?zXsK*I6>{voTTT;wRVQnBn4;+KAIO_ssLWoz(|z@4mAENX;@UrsQ}L`! z-(L*M^^yG&@_JLVLs;Z~SNCS&V#{Z5kmr1O)Sj}AYTzEXO5b2;SAFU7_+c;_+Z~o~ zcK9hjDlZt?JgxkjhVl~=H1??X*XKt(hM1DC$2&#o02=A-B-p)?wWL#=3lF?EQpr_uZP!K%%XtvaJW=}W@ zPz0g_Ie|ZE1wVhx$X)U&wiD!Up9@8ZM=)v~P|?*+Xm^(PgUXQGPqcl8HWGwKU5Y!? znKFYE-S9OD{@y(Sh~@4>3DAh$)-l{{4tQR#b1Nm9We5L1Xixdr6EuoXGIIPM!+=eC zhvV3|>-v=p`c>GXB0}SkKWYLQgP5qO8(-93(7T|*_ww=rX_e$S_*pX0PX;;@jq(CK z6rj%L=bhE9Z6!pdM3@l%mRUs?ayU zXl=zVOt!2f(9HlTC}dM$O_!wb0OhzYdb!N~5ss?wm0;##mwU{yd(A zVQ`N0v>IV8xM093mnG}Lf@#UFRJGid`)eIr@yWTP;NsL{9yhWDvZI|K$@h?i+rb+e z&C1+d08_1pwPY!n?F2a7C%j~e_5zrq3>==Y24Dpc>yCjOtTsu?^<;$ayyOFoe9=6< zs_FoHcbw~3=nI(0{a22Jim`FoCS5*CnH*P*dllfl*1!{wwaS?7m?O1gQVCdI8lgdM zf+?niup>ZMqE8IkSS}qfL0($X32cFvr4dIuqRq`$4=ytKG^PZe)!mqofCkd5-FYr~q?8RXta>U0O1u+km}&KUPaU zfx|)p+1j{{VD0k*nWHh3lGm^2(Yz@~nj!wl{egke`Piwkdt5WfMr z)WyrCrd+CLF!?bg!kVG;0|CNAb_rzD!vsKB#ttC3Qi5P{_eo}+L#B_Sliu=4xOP@k zyW-bWFDDV;zAP{daNn^0-UwMWz6x0Bz-#dsq6r26>mj6fgyOLZ!gorQp2L@hnf+8H zMdtEOv?pXh0bgsG9)+jf9EX;cs>({Vl)u!WOcWas?Sml)_4M3u2?I0L@5yc6kEhw? z%hM#r@i6``g#Lg1D^peje?GJXssiAy4G7xS>Fnh*{wNX^Q6#QB1zuJNCfpwOI@M_r zNR+2-7Ncjd_g=|x`|y=R(i)xp1l}sC5FQQ8=aA;CJoPvH(cgC|F(KLI4?w z0dd6YGz)QTxo%zJudY;$rZ55z4@gT(0|($C{?E??5WOXoS2(~RQMK=SZvssvlvrL> z*!3Kx@Jj!0r6hV|W6*$uweEQ2TTqUb>d}c_U{{P{!?A(|hM))Or=&NKQD-2E5bN8O zLa+h_zefNL6DbKvNx~`^-QtV-5$^%E6zUM|J{gHnU&acB7{CM$b7gS2!u8?GDB8qc zQo{h%aFijxawWraW*`|z@lvYv=M>%|bsPsEM4>q8o`waQV240?nECuUp)5lSn(vMe zz@R~rK}f`AO%Vzzii{RDqJbC-w~H~ z@#b66a0J9;j%mBXUmQwkyoS!%VU6qzyk#jqY18-3UTDP_9vaBA>DTDj)@Ad~O!BzEYK zW6bg;w2I6e0J&&*^2ws%Ot;J!ERd3;uw09&d)UkWNUUqrk(WmEW}DfDI^2gC8pD#o z%fYUq1C<|=m}9#5#wEKT?e>jGm^WN-!f&D0zuCL z1%_afryI>p*rszEPa<=`2%tGt(DbeAI|06lIxZ(7H6mBkO}jT?}3Z*U|Py2@X%zl)$5trvi*`{R|&jmDCee*CrS zOu;LvDwQl1j&8_`X-rWO)ho7}KkxO_f)wMwftaeZf?$; ztCc>%03uYKnu>%prq;c}q30zkD^v%y%Dykt2{5*Niaj+c?$)EsFLQ<8j=Ejge2!{ExS?U1EaQ6msw)SreN(|jz zCCr|@asQ5i?&0@Ohd%`658Aavy%>Mn0ZkxpUMc)RX@VQxQK9x>j;(%)cyOuXy%>tn4Rtfl}^g^TN(;O|7!Xp4Y1MsL*3@ zMB3_c)umX+!biS==OlPqQ_Su+bXhAO#}!lSTpZ0yT2e9<#-pN$XUo|7W^ssK1Hfs+ zWcK9^M{dWcj8jqVPWs#}O{7UQDuq8ga=_g^+r~eQM_>_oor2MsUY$$*_kRqlpUuTh zBfE?}%brCdPjN01pfk&M9s14RfB1kusuT3eCAT$%8idd!Hx)^2bcN;#+AHaFp406g zkJfEhu>7&mqJ4*un*u{NlTY?8A&&}SLEB?klj=Yn(F7K`FYAeyL!cnEpibI#zX6wW zL;X(Yck2gm${Ggb@>2F{ciu=ysH8`ymjMi03*DXBhx!{y^dC6aSvkgTd{rtb#hL6$ z$pYR1nUYWTYgGP4G+%P0?l9&ks_}To#Vxa(dJL}g)%)X}GD8a#U9QZ(Er@JmDW!zM z9yi<^qwHZtd6k)hjtw#{wQz*oe@=|@L z&q#(tGCndj#cX2Soi$N?X&t~9^0`UnlJruyAf*C<==YRauRd~w6Nc`JVBL2TG{H$7 zECUbz*JUxM-HpoMQa-q?cW<+;TbR`!S_3+KlkB*jeMZ(qzo>WPyF;UWNm9LK;ReN( z=Tfo=phH|-VXOY}$+ii)^xNS^w%Z*a?SWo1zeI>bx zyVXQ8o0TbiBLqAXz8V5QyyjoW_xFCtTK z1>K1xVZ!cvdNqgm*Lp#IRK1H1`$vUAkL=6y zL*%VDCdW!;rx?AXW_t#|VHO+^kegs#^ecOK;C`Xv1cN6tXpO64nv)jx{f@eWw?QU7 zjT`Ui3J+m--yPw9kf*6{Xo$EG^8|o;-(*R7b9?8(%1$rItbz791MwXFaZM{G8w{3w znAGadHW~NdC!OY3P=n;j?(kh zF=U%MN#+v|>SHU=cR#rp^+9ww$3iuF_7@;1k@Y>wMK}GMmu|7l_xtWkWCf0nsAUb-#dPnUGH}T{7_RYYy7kxtM%nTbKRE0X-#60+m{#c7QCw_Ay?iD}$1(ij)V zD*ydQLk1=L`BbA~GRuHXThr*i+nIKSUFYzM;`K0liV4GG(p}D&^hOJvu)7Xf65(2= zfzHkt(NAHK8@BFXzP&*ie(k~jRM9&~Z`|b4O&(1j&9$=N6OtuT2=-+AfB z#fgVbO_>V+a%1;m?b0T3&x><3&QtX4VlAaZ=d9?p9b|fQ6}mo4H7(Hy2(n}8zMWT= z#-MueUn9@FAj{SFhQQ-VIegta(k_hWqV?tIwPTAitwv+=T|$epCDsoyZoQz?k0l;u z#{4+wM~|86W^48E6|s0Ru)0a`(vB}ZP#zJ%`yU!5cK#=P8+DY|7#z*YVxs-+cF#JmTMGvEyU<+1%IZf!Rr{Vi=vX%W3RJ z4_}rG!$~(N%!FOlXGdw|P3s$@%&H}BVfx3AW=;i)zoi6c{#>3w@%5e%E>~jt4(wH7 zn+@u3U|X57*LSi77Y&l*+Sz;dUaqNY%@)WwfT0)}hStV}gpNciIM(>?2fh#4x;8A~ z#%gx0Ukm%%wMlh@Y>KbXkr(aRfmIGH9oAKL>=pK&*xqbP3Y~J`{?vHsNToLML>5eY zA~`t<6#Ich_DoVn@r(NejZ7l(opRg5d*TE!IYd#g?(P>u+Rc>?=Vr{>jVoy=|L=d1 zL~^WcdP}gvB$D^@tVb9^l~X&*;S*6UkWP4y3hLp%4KgWP3Amm_I`yo-14jG-J;?)<&=%t6fZo3ddyXo)$$V6LD@BVA_k1EHZ zEDMX!jm~xI#iKl)y!(WS!x7V`~IQUV-wu zI>Lpesp*7Q{%Xlp$2Xvw4F9zIZO)E4-`*=3*n!_XX$(YD4%qv1z}^b+V7Qc?4N7vI zunj;-8fGM^pYh@Sdn1`(iDkubh6LWWv2$Qx09e1V7UMWP^`L@!N{^p2XU^UO2cn~* zAO-6Ln)g@)6D5h+kb6~CZPSE`^yAOmFj}*EwWpUCyyi-3XMcMo)E$@%>e_|EeazoW zL`3{brb%s0&5DH!7cO4hdd?#3$0bV$yF`z?cW`Oe(~DaD!9G0`KG5X}KqI%Ox6n{s zc5#WZHm)-{d-k9x6hnvOOe@3!?qhPA5mcm`I#Idq-M)GAEr$gcnrBa2dT-(CE1ZEY z!KJRZ&HHPi5YsD9K)D0Va32ATL}X-TDIu8<5&5NY^P`A}*VRiY=r>B(Z;OH$4!z2Yh+zl0XoZWc zFpaMUr~`@aO-$s|z}21`Tg?*SioE z1lK-YZyvfNrPXRNuW|bsl)ah0*(QJZ?WnL!6cQ6oyMXWX!4K3t6nH9a@G{GtiGOIj0rq3SZ^J^6BHy?0!UM=6NUbE#MKV*?qyosV8uDNK#<%J;i__xRz%4^3)-8{Nx<|Day&o_36BoIN{ue;B9~L3!I> z0v7-E?*3DeG}j7-+EB##KnLyT=y zD~DGxY;|-08UY%&WB##gL$q#c%W=^whec;+RLqB8XoqGQ5SzR~fF~8(Zmgru81a}E zi%YHavRo&4Ho=3=K{DIEH%KgSrn8bVY!Jf;G+itmQ#y(OpH_C(tj8R+HS5>EGEGD$ z)C4rNU3VaTEZPhxQCQmasG}nt5^@p3ArkVlqFX>6ZTH*R4Ws!0{+1HUyfQ8ug<6AD z2;!HY*}wAJdu8>YNmuN>EBs+KR?pQ~tr26cf8@v`lNxlZYS;rc^~-b|JbC1Zf3j84I&Ohdz9%F*&9@@$T+} z2TS4lDZstw$dM!G&nJ4GsO=qt(YRVUqIXx1CmDWlkz3)0c<=Fp2R{Wspsrd(WHF8)PAZw-aQ(T~bRaKAxUs>T<9xUHZ)O9K5=2nyaW>|C?~L5-4kEQvJ8U@v3&PWJL?55Q3w@fDNX@voqFk1zt$>=!`3AMN%4V**R3=<(ZsN3=a%TU(3n zzoS7!Fx@&Wm=0V88u-Y*s8pDWg&l`!=S4=agR=bj=Zg{bS#Ig^yo2q~A7AnC*k_2j zc-vsl^5iiN$Bw^=^%)N^PYJVY76{12K6<1qses|5Fb~;o*jv;PUJc-Fwbi z2t$U#FCbvPm37dTI0W2TE%`L+<(S*60$4!xb(=RsVPQ*^W4>oXZ(wj*@8J0`e*HwT3gE-5{5 zw-5pIq8)Db2$8@W9x9O)+nt6!f`N%R1t-9Dh0ib}6+|&kHBd?ZJ!sLqy;ePB<8^q{gm>TSfz|k4nboTwrlin;o@coIDr_(O0~ zR8-WA88bF--h80y%nG!c!e+!qP5@VfXC!vfBDoe6x3n6f_mg;W);%gU<}JMZZT+rZ zouzSDTU$&N{QTi+F#G$)V729kX^=jT7cXAyR7K)*pO(2dfpZ#qfh6n53*(}(tX^E8 zw3^x3vvO{e2_legKX~2U-5nnnhY1)Xf?JU&A$YeVzmCLk55gj!+zfi?Xl&ZMP?*xk zp*`yF!i^lP)objP7rB|Hz%f;=9I|&v4@7J}y;;7&+|(3FwoEYGbh-f3A)P#&XxDzg zSh9r%F-edIY-j&2TX~(l$<3motgNNKpL+N1a=8DlSYeu68ybrdm*oji$VZ%lOyI*5 z9V+{{-=Lmx?cL3EU4_{mOylVfV1e5G?G+gRQJw(UG|~@P`(91fxwNq>A(&m$kBKA~ z5N!i6S3BnE(?cdiFr`fkraAmI@bat?Ri?O3;_$;fg|+@B5b9viX@os;&bRt6sqj+Z z6%yKX1(widgc*-tmgC^Vzlrq&S22sDr@dVkk;})2J97XXD(JC@J+={vCLV537%uM( zqI-OKqT4yJe%98pMMi|30{VrH%K!E5{!@|Vf2T(L?}OY0t?M{8x4|69IHpQr+qR=Y z-PNG;ZwQl>7ARGei+%t1rHcty}`oZ;dtP1E0S60Bi&4Ky*N+2Ew81sjYld zz&aB%Go2J`L!>OQI)NAp#*8gOl!kN%D;($9*%YKS_cX0wMH21gfTRbAb) z*Q0bh7ngLA@4ovE<{r_>+R)!1UkA(fI2FSaZwC6mgqEf4JH+(I8fVUY!gvm}1}&C< zP+^LC^Apq^ZCrkp{IKG_NW;B`PQTQ((3?oB@k^b^0a>qcov0J3Zez!--ok@uEltFP643CQ5>o`xw^XU+2b)RJl=R` zn~C4!ty{N(@b6=JA}cy#bLOcY3~^A3dy*ropf;V0F`Nk*4@=HWfvSJMhM+Rb?2(>; znhbmz7=h{OOFq9Uhv%g`$ya}+Q- z@35sza7BCxfKJ;_#ArO!P<4t~ikMAR6oM(K6TXA(s0Uvb7Dna_-PwE!@2fs})EWxO zeClX%iqmkI5ytdI>t~7`PbC%Y-m@q2YESkIQvZo*0VX3A@k6bgp)0bgT3-fIv&RBd zv@ZpAOpzmdgD@q1MLNco-Pym^BX9GWH+Tt|t(G{8OWq|J#r3Fx&;<&?@u76|kkHQy^s zzH6mn#^`(fkKlfZWcwGanfAHq`PB0`I6?deM11^cq)}Eyk3aagpLqKDK5Edt?dMg? z6F@n2iehLEmX+Tfw47D3pJZn0UGW)h6oCHcw^kIgI*bYQNuo~-l>OvgYz&NadY_b- zC{Mt84%3S#11|#BgBqoyzh8Ly@(IR-T@iluhuO0u>BGPp_OJCXIaA*HhA`4Pd)C^% zm;J2pHmL9o#QvABZ=u1}NF#e4z3Iiw^B)Ld6l52#qBHd=@~|STe2obx zPQv%!J&8k8(@b9<%=o!vCKX=$hbt-IHR|>NrJel9&#a{41#5e;=`d1pa3L#=l(3PAWzOm;|A|h_5meTCqZ`B=rXA z4pr(i)_H*y3pjK@+diF8kV-}dh5KF=7%gRRD z;<~=xXFPGAXApaX!WQsJoj^Zz#4ki*Obl`kC_!2F@l{)KU}O)Je4;K$aRG|OSDQaS z2HVzT>xIcroP z4|%Nn)+$lEZ9BYBZr{GWwhaJTmx<|LugUgr;&f$YC9-LZa|k!vdIJ_>tCX+G3HV@A z1_mV}U6bq>*JV>0X*8N?qUz6aI7Q-*Z4_hGH8gB$I}?9gu@;^( zE30n%`K|86U8e7O)m{?3t{RvwkyM^=&;5dpPnLs~$eBW+YZCOSef8WwGT~4aSeO8o z5Pg0!f1ZXuM7se2OEFdI{GoK?C-h;ciz^J#7yFwzHlh2^ZtQl#seQGCMAY+rIH-$^ zfM`SAcq;;j$dk3e$>O{jPl5iU6CutP=DOhn>zKuJPA2B zI;}pox9_V!`{aG0LF2?r#K`lJ#a)B#SQ@61Lj%ou=tfp~n3-8W>wVGwWLsjQ zV6MerGI8Fvwzfj#)N7v}-yOIBfic3T8UdlWg$DhrUk{`NpFbI4N9AP8%j*X0N5CI} zempIs*u}=jAMpt!-zP@kkqy7_AWRd`6a!h-DH*%-pt!;a53`^!kCea_5$~$X# zoiXtUj_+hw`oWsryLT&5Sh{f*78V%sAjV?A#oWKhD870`ZWuWojCpa_#9Xjb4*9W76Y`9{reXY+S|*Qzt>2|;I{BRjOtG3K5f-M z;<=DHXFUI?fNU=-D+>qH;f4<%&SJ5Ooz5wj^^YA7@WoerDsjaP(wiozm4&(9{;%9{VpCo4f z5;y-%98psu-rD~11PmePtwfP8vv%!nA17H}LMVnp8OiLjWio)}QNlK?B=z@COX@n3 zd+euh6zXe=YjAr!YTr5{K=JYMNvbwSq1^wwB*1~&;+um@;C&CO!=E^{fr1vKbA3yl zF)Ojax2Z)wc{1?K@8_pt>(>K(P%NZkYGO@oEhS)IVr{SD_jzzllMMV31Lfbpf6S(c zh~n67*PAsEGX2PQk3_X;hm0c7#YsaW;4>C`$&e!>n0&yTI9=)RdMU zd>Zf)%!cS@AjnW`Lw~HZ@isCgCCJazI0slvr5y5g9Q2)GoQZbseHR zCWWP?X=4Plk47T$)-!=gNirr4y{5hPQv~TVfLQG(C~JAoGokQvDw*ns-tK`Jq^6Q- z82|5NcuZDovw(^p39*(aftI?DRU5~cM=< zU=VSJ{j?%tCvimNyNkY?(hM1Fhl%jsBJeu*Grh;gEP+!GpXM=q^ukBJxY9TipYi8Xi9BA&vpTyJ9q9_W|N;_)Ma1Y zhH6~mK-kQInrW#NDZd$CAjUG6!7%AyQ4o#W9S53_sDmjjtWKv7Ls0TqOhQ5}AW|^I zVOxzE%vEkXv*sd`Y^6b1b`T2s_9-bt26O9Bo`Uc|(6~JUj^n6>RSONL5cc@t9p}4_jvkGFRa9h?CBoY1=kFit z3RjOl6#rMc0v96H#tg@mop2#1##%&RMP9mi@uRtzL|_euLxJ0wzJ^Kq!Jg#s&`@L( z3s!7#nGfV1PTP1SB-fR?yn)aD~Y|89Z|-)+K@i&{ahH|Woa3mfATmQJ*YzN_Mj{Wgr+~r|Cft| z9JBw)31ek7ZHF~UJg@CV`+&>wsH5@%J|VPHJBB3Bhbon z)KqkEr1OZ%{(ExIF+>MWGRwR~ckGzLS<@AZptR{&>`rd(IdiW0`od)OrdUj5q`xL% zX`-%9Ov;Upz7382DGdvN0@a}inL!Te+UfO5<{zXsK!XrwRCt3we047hkG1|P(H}va zAbZXzn!e0xk7#R0nAE_Vk+Q9Y%Fk zl`QN+Wo5Ml!-S&k$kF&jtBG^Bx)p@H?U`J!2yS7mQf zN=k~Xg$6@jO6p+#WpIOHMYV^S+Z%23-o;MBu_RirtZks6^W1a)9v zwtRVKYwIH+M@{vE07~$hMl^wh1o)wB+2@QNsbyZfHtj64728lX&OE5lN&|cmp=!~# zYW6_oOvm2o+uz{?03)$6@#_uAL*8Vo53xU~j`h`d_vkTF@7l4cMr!H1(EeIIOx#V>VV_z7*3$-4&S+ZcLN0yo1;tJ;V@k9PAP4+23B@% ziuu#$(ai^nEI-8lDl7PxH_XqCZ#Xn)9AAxY$BosfDzOE4_@tUpLA`CrrxNqyFv`^= zLbrFIPWkiLye|1(Obv!9cVsS6#i?L|V2y*eIiNVAh&YO%j|&quNv~(sHnKG1(0i28 z`R$9#F(V{7^XCV{v*_HpgvZ7uL7}EDiz$|7W?t8>-Nb~(@QCoBU0~~nMF)*}u zJB_s@w!qQs%W^Q`K>?!z;l;pIf3{Yl7)xNu5)tDgCaLe=zCFHn2o46- z=94F9(UA!p8-1XV+-N3dGMF!j-oiUlKdCNSvDLoO^!UldUq%;u^x-x6 z|Mr_m`>iU%Kv12`e5{25>kJxtbj-Hgg>0cBOMvw4n=4gBCk-|ofF}gA@u;+3_zhYq zO}i*k+h30v_t9zVjIvKPs|0B&#uG4k zF8owfIE9fkcyU_?PTm+@OCrruhUO0cDXpo|hAh0VPF}@Y_Cymc)%8_2y~PXju^(-! zJZAMB>4X$9ww-i>L}3k6v#0uY^v#^SgbE(^Zn;mqq$`7S(zk(!K)M7dbMz^QgI~i; zfDkYe4}5Tel4z(b!{#4KsR0@62`j6_`Hlg&JjpEjmyg&fi6aUgX@M1E2irD%KBY2; zsN86HG!4>(N-IwBCAD7tLsUh~Mf%oK^*>-?shosmQVp|1@FNPY=4TcK!yZ19w-v># z2ba^4I|TN&w?>Z-8jml|(kC{3ya&XJJIQ~L{yC&_?Hos8NBoFwgSvaP_%WvtMc-+W z;=QSVasVAZov59-31pV_7$2oly`x(79+{#fEnQ(QQ*4MtOHfeoFddpY`7;>3hRyG< zi90`>1=^sa<1B^328&`@9hYyJ?68AOI`v0HKXJiC;zS0I!l3c*2C35}bsAQ0OF3#7 z{-sMrtC80}tw9w4N>nu1-~Y(*cT$1Smqp9*SSWTZhSNm$nXgoC@h4!@nvtVO*Znd< z!%o>1XEsvu6qV|tz{7+Y;S_-!p?uXko;h|LJ}*a}BI`Bm*53g8G{qaa zG$J!N&LcN}`6XyuRI}mwYne>2Hydxm;Xpd=&HWu(rl>w+T4)$@JE;P`i%67T=fCQB zn$?VQy2r_#H0zId)1-L662#NffF2Y`b;U#s?>?y!x(*TUbfy9}0JZUXJ! zZ_nZ_`2W~@%djfjt!o%XMG3JGkyJwI?pPwyjfAvHgLLOo5fkZ7m2M?O8bpw8L|U3f z$D(1aZ!YiKz4!Bc`^SEdm!AfG@%?>KM+D901u z4wL`j=WP$so0g1>|KYcn?hY5nrtwt|Ddv++FWnusll!1bGOl~LhnwR&)R(3(gY=GT zZNq|L^%M{If{N9;zUKFQsK6ca8 zy{GHw5l&W_dx%=%#*9eilqv)xytVUSoECD1mmAz`c>mjCxi5B|)zw4$t^4tt0G4J7 z&w^z7LjUUd^Y3Yqli$vieXcp2;T$`Basxw*)*mlNm-LIiL9`0pL_vZsz;=TT;}Qju zM#$k^W%0i2ZAo|@FaEADDua86z`MNp0~~E_@rhg-Qf--F8g|B&PTtmR&#_8RaK!6Z z9Am)bTK8Y300>B@fop1C2ouLO%dD6~@Ih}8o=FO%;FTd2+UZh|yAG!WARQ>p zS9)N9)rXd3G0zVVKSTX&hk}TPZk=CR8Y#94*Oboz8lYko3Fq=6HTCl9Dgx$fVG6m1 z3p4WL2h&bAAWfX1)woTG#ly>^$D2TMyTc_WW?*Ck?DayKWyT_ECpVYvNN!8XkhCHd zT>rMfXur1zVCSf*QPG(IT-e6jPmNxm13oef*7iiVZ1Z47^7;Y%Wx$~}k&$)kC)|5=3Q)M0bA17)@HIQ^D7$kIoP&! zZG+pE7U}g(FA693(wZssi=M@?Yktk}ZOo*BTcvf?Shr{fUJJJwNI>9+bxHQJq?e^D z*f7EkhMsGl6S?=s<&F^_Y#^?Rq7pD|3W2HJ=BlvPaK%U>Z7m>=ol3y}12%yvKYaKh z!QkZOzQLZ}UZ6mL3Mm3rCtQB0yrNRIxn|kUK3i$KgBa%mQaxVh|lR6QdG2BuLuwoj_ zV#y&bSy7CZ8*=?4@2!m~+aUK*H^>h13JN$`S;Oz#yBAZPUq1 z$`L==@)Q&l7#ufem4%y=G3Xmd#7^N_&wK#)f&!}_8(LDHYPW6yd^eatg7_502uYCm z%$Zo|iQrOHLpcTFSx>=!|1KyL!?fNzWw2W2juV#UIN5K7HywQVOtaihumtYC5dV{E zgiyBsINKH!j8L?snZi6b?fA0nnKU96Xvup+LP9`gkvTQf1pN+lG-$vl zK_CRrcwx{48oV@XPz;Hef@HtGqrIIPz(K$fRnxPS!=MM|Fa$K$z|u1FbuswibfCPv zbbGABRi#(ELDxD$dj~mz>0F>S&f#hx0v~Gmvzgi1PF_pcTsJB$E%;FIk*YU(`@6eC zlag?Y8IO)VDmbbI!U3w;zsv4lRq;R0(n=Wg^7$SFfVR0A*KZ}s0;B-CH`Rb30CC?% zLn77{H)H|_p04^d4v)NOj5s|A($`@*+7$7%Uum0&eyR&PmbX=RaDnL%ac z4*5IrjfP7Bca;wx>J|=0;Fg3JMBLx0s;b zXTgARwRRS6=Z~i#NKrJ%prywolvy0^!QD%$-~)y?Pk-dz$}6utxHTnnpS>`+#TFD4 z6kd@A!I~&a*%~ECSk0#UVEVFeKd0DF*#=}1S-0yX>@=I94TiYZ=NATg z%Y}g*oIJ?yr^1z-1Fk|Si8UV(cQ`_tJzfJ=t9lm#v2!9TdKU<-@W<_ z^LqydbhNg*tU;9wz}G&e6LgO{>vP~Spm1B9e%VD_W@TWjKt6zUi-I6be;ejX zI^-<^ims7xh&xrBzoP_RE#9AELADprEgBk{e5<&FF`~0C=z)ZH@KTbKldD;sz{dREy`R921XGJfB_`scXZEx;{0$h&z=2qxzQUR1FfdHR`@zL6 z7Z)@j6*CD=>ri3>xF=o;n~O7xK^VOPpfL_s*iZAj%)-nppD(F$0Lqdaf~GCnrSJo` z8_`Z>Z#ObVrop}|W3k(%Oc=MJ5ayREL%cH3yG0|cMT1M8Z=k1{UZ#Y_Onh13!1eZR zWCa=^_6$}Gn{PC0wEhiF(-J*cWSXi6QL}jva(F5cWKK||LVY|5PPIxnexcK5M zl!1ShYIu0WTz??}wcdPrkU~>mhb{!4V}%MxV}5RK2bkWBMT$`%wx2(H2Eub$Sy@%c(gkZgvdGP^61eL5Lu(iZlIre4&`C8vzt2zD53BIau9SaXMp&I6EnjA@Aj^@ zu^d$8=D<+1uv)+>1XloT&^Nic*P)QuAM^t~8u;N$azG7H0njB&L2hnDG|n6hHs>+t zL1LY%9^Y!!dxcxDym1?TFbyL^L-s0d(knnZu+q~@bx=UZcmoT%v)Rt%g1F++(gNKg zCKyXIlJj~m@J$U;ADDqLiaYJ7osvk5Hpkl7nh znE-hP4)|YaD?tp7isM6-X`4ag1hL`D`SaH-R1_43pte2Ocw%L3joTyU;qfIo*%;2y z_VO_GS=mlypi-E8VIA#iR5c9B%vnfDNMj>@V+Gg{`@Fx=lPy(-sQ@1-=^w~cGR{x}AkT-80c0i)T!PyQ+A=vo93g54;Aj~bp1+I-gCu0jze98WofN;C-E)KD z&krY)_(MQY@BqlkD^4I(@$QFaF^!9v8THRj&~10^!BGruc<#2YWDRF&T2+;}n3$Nj zxFPb(fL*DgAaqiBg%FyYYJoE7KL%hbfE*B%&h?igMc6o*&H=9t6jj7Jg4P-~VW}4C6z6;W`UvERH@IU9+)Zc0?wX zef;R~r)M;xKx#Y636~Xz>70R?OL_S+$EoMpzECYgUtm@OLQ=3nbDrHFe&P2FFLQb$?li3YC{ zD)skp6?0DiK7b9GORzA4`TNpH*(NN63<=Vdpi;owz8~K8p2k_L@jjf*uLEsif7cg4D zSe16@nxy|Jlw=?r#eVIvuHs@oS7#VF9qH-W`0Csu4I@l;W0iwPEj3t-ciD}F0!k3a zUQa$zCIBgbmiQ>q>+S9)ypoyCuCA_kg+0BH#9H%UWkZB_p+zUnUP4n20~3=`rH48- zy_S=}UakTs`1ybxw#p@gAFo497h$Zu7s^H{4;T(U2QnididAC#YNZOBhOqvi?yR_t>@m# zhcn4P1SZx?3&JiTHP}C`gQ~#q!KP7TasX6jP`sc(5S(EXt)d&F_ycOa#>Q+G;rGzv z=Wg$Gm10hxITKo(Agh&yH~Z@1jlRucs1R*WU03wp>H)%~5MYf_4&1bn#u(h)&BRd3 z>*NA}4nc$Sb9sBL4+AEAk-Qb(=OTg5V7{A+SwUx z6cHVb1YfA0J7@1!*uvap%LAANfH0Gm@Boz1P>*cPCIR0C;}L?1?|V!WO1Mv z#8nL^&=Uf#1n*S%0W2GWd4m3J=A=0>s5XfT3_xhb|96{A$v}I-lf2I#31S%FxRT4& zgEqF9l z<%gp2pc%-k$gg9?k=>Hj!u^QT3-j|ZPaS5Dp!f(K)U>Fw^v9Vunk1nMHg^GW5H(1Y zg!?LwuCWXcl{-qyZs|xy7#9NR6KOZ?`%&pxm_M752CcS_udmie&6BnL`-Z^TO-|y> z?;!R;!&V{|jRt^*$Kltl=9aAKCTRRiGNZ#`S**S|e(qCZ9Iz$22B*m`ngJ)=(HX0P zpq<&Z1BzWQ-JB0uNe4k-f7$Aan~8?XPHo1i9qgqSo{Rz6sL+qQ(^_DL!?5E6yq$_p z?!A@Am}-M+pTQ&PnQ6!yETgvAw=iv&nR(Nfw+C|S{BVYgXvQT495%gESm6j*>P~V- zv0fh3Qymf*%)9;8lNJN?^Ej5lqgt8@9Rw>IPP=YFL3m;6kGi{m0-$hp__*HVECZ!Zr{{RmB<>@o-yiN3X3p zG{ane$_t|%9*lvpaRE)HTg^4`0l%ZI0gcJBGRx9kob&z(GD^6aAUJ+vSOwg&Ih?}r&m%<_ zL*|_*Trz|p(2}jpC4t#B>7)mwSX1fC@WmS6w}?gcmBvPyjKwDtKo%YmoRjwQD(|Jo zBt%Be&>+D}HsQ5x_1ZQ4PXMgYcvV^TG6~Dr+OG2~ke`8>`RSx4LoX=a?XCMUlq(Zv z(r2|Ro_YZ0t5UmC<2wNGYkkk;T!uK@Z+;gPG%$EtapgSnR7~b? zkBZ0Y86{A2auSb0`_(*D_P@G!!0~virj;i<3(?zKTUzh?K%)fPAkqAo-yowvCoRQN z9Y9Q}&7d?IA%bK~dGX>VbS9Bns`Ra)kSx)}mNmKCfW<;!9i=&sgjCz`P(1)dQNRIZ zUuggl!i-&$R-${#_SxmjtS}??71TuN+ zk~fEb4g^5BC^Q6Ch+El^CqB%pW5O9TYxTEf;|nu=zh^@DxNP{>|rAf*Bm4k6s$^-TkV2xWTunn#iPSR z!0`>BX)D=ybrs4&m`0t!h*msp+vhaQCi#Tj{f+>QH|}AU0nXzx9KHZ`C67X!QaZeQ zn2E^L+uAHB2t>5*lnv3a@bhcMky49j_JG%q3+SHyoHxmg60mm23)s3+&`b7QYr=pm z{%9empzrec>mV5cMdEcR_{po9<3XD7P3swqQ9lxh#8GC8ONxhkFq+Ep2nI)Twi&lK zH$j>Fxpxa)Ust1tAovCpL>>Q{>0oxpTB!JW8k(1}_elFr%w{Rt) z^;vt8zyNQtV{@PeqZNs3Q>{y&mf8;>*VfUA4hwt86PxM|$$3GTFJm#wicDy$k{y1Y z73f4~F5`A-f^Y*c{1X-$-VFc_U@qaQ_sU%0PV^IKqUA(rH00z{0YK zeo0nZQbKm=67f~On?gb!FbR#jFudl0uS1|P1l~^0t^owfeRYtElDQr{t@MREys?eC zX3z;UpIc3z%B^6~5Olz!HOLwx@N-}x zYw(0=#XR0UZG%|4As;SEm)J9YX|gj{@EG1a;!osQE52+WIfv=&+Tv+Cy!yXjGQa@d zl)m0|CVh$6#PZ^Tg#E0c+REV zCwIbaTHuyBbtqed+{3OmQFgcPdpmxls}pTy?v99f4Z#WtS_Id?o?9ZP_iVyqrI7b> z4Yu8auN1KA>7P(zhFp~RM)SjC>;&Gg0PDgtLIn$V z-{)56eQWEM&OVFQ_%wohb{K~E6 z5yQ(-%Sy`1CAcAM<&~I`iqg`gB`Tm%YH&9O zs!l{y^zBLi^=CJ5yvH>M=L~UpnvZ{3|B!ITu(4fkg~4Pk3|3Q+_&Cj*dzr=XgH$L^v5W8xM}NKhnB z;Ff8B{p!*K_&^%l);z5VEDBT4HgKOZWG}42Y_7+`wkl2qyfn&bOS&G zafu`Qn9aHFZuOMgo#@tb5g{?LB@4sZsx{tWp!?xdffdd3PX#rY@<2#^74UGQl*4=v zVXpDQ#fxTKFpt(!19L#2YbI9E;;{gOmk6jwKoW1jF#u*=W*>fGjR-zh zSNnmqyHWIK;cKW%;^MA$Dq(!Kmvj>uC=;Thv<(c{X5ch6r$ULH2wpx+k$~0o@FtSA zUcOpoN=!mxSmxAl&rn^x8@QR0M=MCFS>w--bRo!a&%xt_7rruHv#OtAAtDfM5|fz7 zz6dD|il_U_INi+Lyye%hea-S*?PYTE)^wj&I{W4EiDIKF;>=AgNeV|9k87Mu^`~zu!_Q^QWyzRr1A(e6%%H| z1Hhv!@_OlRJf?Y4S0}mYkUIGkrN7K`)qAuWI@Tb@_~@@8#!x)Y>dvxmZw&4bYbFJ* zI?4DI=IwC+s>ObX-SjsY7(C2dlYzCS0w_8FBtN_Pa{hJ&xj281LfJyxw$Jd<2-o&F z#G<2sMHA1MM}hZ`!@(IRc@n2@;78->Sw^Gb3RM%es9RHddTMHds5{5sXW|Ovtw%!; zP%AlRMU2dMk*wU4Bd=baIm&-KVmZx2NkO5jshK*y@iHh#AU_Q-i^A{rm`9rP6%h_X zRDcMD6TNF=awlAVW$EOkF$>b}afn%LIP75x*LS;X<@Cpa@-y1Kba+_{fhX44p12LC z3h!x-?m_XP?sz=e{%P9#7`EWC*_~uVj_9^r@iTCg0D4b5M@ws;Yxr5VkxJNUdT-NW z@I0tQ)NP?w!d#d%ZcjD~4s?1912TcP?%56&&RDG?z}Ns<9Ol0M{^lPrrY@vZOln<_ zE5ym!Ig;}n6ZS5ueGgE>kO-JYo zdbDW7jgN+Q6TN@TBSc{`42+#Sb!sq#VQa(PF)x72%$;iWXS@vWJU%gW^ zdxiwytOQux4;pqLH@x*69oKoMJE4s49jC%c>WN$I09(~Xrep)43uu2ebx4ccn+6^c zk!*h)+^VS=QHGrZI#S_A^D%iPCD>LIikply)o^z&-Sh8ys-t}f8!)cy;YQ>k0Rtj|vy=H++t2Rvbm*_uF`V%8?4 z+LP0Au{SRPrc)TPJ#|gTZ`1v0Q}VI4=Kp!9QZq7aUc<=n1D}h7NX$~KU#kwm+f~lR z0z$}B6bKiOeYBM`{P!~+(zu_v_Z5`C{M}?5zi7dSZwBldy)g@KjB=AL{L5{3K@Bivh|5bkb8#RZ+3m@A=#Bd*IFsV+<=Rb_Iu?*47H28Gsj#QczMl1o9Hi zj}k^zk#tMxO3pRIF9cDsy{eP znO0!n4<>RVMsU@?kBk@$8ppSr_oWl8`IAbli9a^d{nR=G%X$pJ;9p6p4F)cu&q|1m z%_3-DcEinyZM(xjjfk_2%_6M$P5T-V5dobGMda?@QC*kl?{k+`TB=hq+Qr#%VuiLf zd8T6nS+ocH3w$@{78a~2=*cGlxF9)uHqJZx`oCttC+6%J?dl`M6s-{M+juS-_hJ?_ zu=g-aPGzZ77cV+vYDQtCOb{6a)V9_l6942L?_=SbvVT!sI`nB;`D>CRg(&v4-k zRtl7${_d~C+q5W1LsN1C4A55x1rteI)rLjsux*JRJctrb`ifB%4DtOA`;}sx9qzf~ z?RtlKP083BxZ`l{;qkWQtsAP}wnE-t%!H;Dd6&x3dR?VP9^bU$sS zf8US)^uJW>rDtU|qg*!BkK>)YI{lgB?-B}c?V-eYyC9f9fG?s?93I}YQ!nZr*C(Za z7g&G)DMvIch;+Pv{}!Q7Iy7DduU@?Z#sR>X=4SbS`8p|;j3Njr@83_r&vUMLT6+vf z=^x|y-LC!qptvXc-(SC9~b?xClAr%&aUdUBE+< zpOEvmm5Z~QnUj>gt%JRtnVk!r5Fw|O{Udv)yABUb;2YjCbF(rrlXZIFNyw>WW@mx> zCPIRIfZ@O|LQWa0M=oYgbeuAe9=Mp@F*C6@H6xUeApHA-dJHYs9V3Asli+u<8!Ez)kIb!eK0l)6VqmzIE_aen+|BQ3n}{K8;!7m~ z*^^VJ-%%dzmS;upATj4{&#o|yPzg-T&c)NN+8b5N_bQj=Y>17HtO}}AtY&AAT+*#% z{b1@m{%DK<%WbE*ye%Lf{!7qxOr~zogSp#W){HWK zsx0+Wrx?^u_faxHW6udNxFBkgNS^HpRn}AMD=4ovQsVGM|~9&*3Tb}#Jn%% z$wAGOl#b{qr%(=>R-gNI_R2c3bOlRm%=?sRd=*n_xeL|57$P^1Kkz(0f81MTbF!F1 zi9fM&!I+!JZii6#?NjQ+hs)=7WbsG7NnaJ46jcx@z9cZe#rZDnovmE+Q*oD<+0W0% zY4dstWj`?%p15snmKfR)VB60>;=#Hp*YwjG?SI|QatMEYIGD%slqZt3)Q(2U=F}Ko z--FUB<*7?CUD^ewMJ#B@61m&)`}Oj!is{9jd~IcmaG)hSc_!apR+IL-=>*X!>wa#O z62S@7_BO*R+Y;%4EPmK}seaRP4^v`pK(t<0+QWfW_Ulhx~|5wYBPx=YAupU0o< zfT&zyA@wsSL6PQR>bR}4_`0Oyue813QYevDIA0kov2kvcP=KS)aVMG$EukRsG{U(n z+t`chI)8U^D7&pBw)f`Q1eEn4iX9=6XQta@;h|?}Q(w?rt6uO)Al~A0#059nc8BR7 zoI5pD^_OqPhzO;}c2Fd}5~(jHeQ^0JJNGC4(HjE18alBZCLJc4}NW*VFNN=XL+rZDUXLq_)TPVe`*x z#FnSjUzyk9`wp}$?knbXE)hH4|Liz_7?{9w*T{7(GR^w7dzMH}`K|{aKZp0Xy^~GO zzn&xRoDX^N$V-PIEYYHc#tH?f@0CJQR~XOD5>8u_qxR0J4T2z zKL-^?EcaEM0iq|QZaG3>8kwNRiw13s=g!i#T_o@$HxcB1Mg1V zJAX^f=B4*iNbVZRDoMzfH7mZ1nz#czrvOZfNliq6-^;lVait?C5p_JKRc@W%EpznO zTq(-sou)mWezQ@T`qKJ@H_pt{<+tZ{%y1Lh-w~XF)y2d8Eem8;SHA|2OCzVg}!qP|i>ZNaYin&um z#u>jv8P%2?f6Vc#Sj}LRB6aIhU&GDuCe`D0Nq4wZI%+Y7ZnJOuDMml=5P3@Wja<4ybpxI1{v^e9tFek4>o_xcZ3A5#L4k z^s8T={np%7t?qq!hQ~0(=OL~qO37|{)liw=zE<+Y09vTseq=QN6t*sif9j{t?UpjU z-bq4l?sG;AY+c!#4e%T`qVUx^csJMgv`JM9kocF8wi)#|5F%*;fA4yyqYiXGYx@hPgWR*2v zXgW8kKX{uT`BviA*>KLvn52{CqAYj=;UNPa_mf?z+f-k(pZ!8WxApWD@$)%@^Gew( zj8P8Bl1h__jAjWhb)3Gk783ivk?xSA`@K+iiHhtMl5zQKpGJxrl(#!GB^n`B`$=mtKowufF`W zXGF*4UsJxr+2iq^{FbZvl}Ls!Kapt8Zyzq*68%7mYB-*H@1%Owk5-ZXhY|<-nsh{G z*_TO!dpbSFTNubQ#s~yJ|T7Xi-{{sI*WfgWBL``JI&=xBm8BvN`m2^Fr_Xa8gm< z@w?iD3cBYx7mT^IN-ga660SI|Me)9R`BkgAOepDUXx*zzuIhqu4JWRiJyx%BJjrnV zsaftmnto)3(0Ngk>Fu{xHK|6zmJ8R>D$kfxVzg4$vY)7(V^4ZZ!j%1)>>g+DCw$CI zZS&X4GiM)+ebOwZ^=M47dY)o@^~$3eO*<~zic>ONOvKL0cE>3czwZss%Sv8y^BAKU zzaH=Tv_X>ID?xj#c^c)KcQ)f;hfeMOH@fPUV3|SH!w#z_bIzZfQxP7rLDX`JJ0`8X zP0RG5;kpcUT#A%u>9;6FI@T(cHL?}Uj$Mw9|HLGF>LWo21iJN$+(f7FsgnSr(qyNtCP%pCgIl;=uQiOY&*hf+MDP1R=a2%WkRX;n_M zZyDazWE5q4L{pk>1KG1M*3)0`QX})&xTJNK=oTh_RAXhXAV(Dp3Fwl%uEo^dFMMrr z=1uLCq!8Z0v3Fb3ll+S})wrI%Z0n-gVB6(vJT2bz6#3JyW=d!LNu%V#*0sQJTMg}> zr>RLtw^Hs_Qe`nL%udv5ow|G>P%JRrHr-$BZnQV!FyqVEzDZLj*vXmv#==`vg=ic?I+bfxcdOa4@ zSN6S4Id{(_j27(&8(nl)xqvY^A<>+`%|_oKBh^b2fUUx9O(aOdO5Ayblb+hocHiK8ELF`#q}f>2iKeg)!r$d6X*DI z)H$-K?Us{wDay5|2PU1fqG$J(hC)sYeX`HkRW5M%lMJ4I;}V^r!}#UsrS2so3$l~$ zSJ#aEwIU<#ThBW8^hN8u<3?u5x7xVqa+QCxD!O=*P4N}t93xp4hoW6qbYE)a;e5QZ zZV~5ny=I4f%igWxf`|3S>zB#LsFW*-#YVU*k(1<#90=wc8&9q#>9sS4SZiMpGrhV` zs37bcl)3ii(Og%e@*owlv$>Jc7f}RXgUs>k8b1`WR~_c$!`>*SX1*}Q;Q6y-B0?@& zu9#}Zv`42|Ed7W!JVo~g_2M&9ZF#{Ur1p)~=QK`&KF--v!~s_OmVN@(?ro8~7oF>| z^S6$3U4KZ;x0AN?MB1+-S0VU|LZ7CB%q>z~(JV^@nGuS9ZfkB&L+!HIc#J>xe$eQo z@vpDf49eD*CSC=K?b}zF@gT91EklgT@;ZnkipK(o-Q9XUtGCCanH)ZL|Dt1)Weh$} zjul!v`T5y~YS=IIPH+h#JYpjMbSrkMhCvm}LeDxg!_lfSyCf^>wqX&CI4|&{MW70c z3@p^kK8<`dMTj<_JVYMXrtxW(bRZEx+JE9A=ymA1J05$9PHjzZQY_`rjLMkz2r>WD zlk(qmYr_AgTjLhs7WhxQwKN^Y;9-Gt)dxEB+7vI#u1hbdzk8wFbLos4U-Q)}24M%r zympx=s#jy}n1c=nxx8XGy-VV!87yS#W9?}e3O>`0Z(0iGhgp&{cH8Gf5BK*|GN(L^ z)MZLpHvC3Z*HZY>iSb@Xucf|h@+6|&qi5MGyoC>ANIM`V5oag6hzV+p$M9*AbVwg& z8{6NZ81S?5zED<8z5I?5@G2s*Gns4kh)ldUgi#C|O87t9E=6Ny13=%%FSau!JQk0)YySL8tN1Z4$rVXW z!xhIU|A?nI&97&Sc$>Io9J|P@Jass?C8I!AW0$Q>Iuut;|6K9*m1Vu@b^LTK)n^AK z8lPy0Q**0rBlvkpfIpvz??P+JjeHrKciQA0k!KWzo-PGu z9*d;({>V^vOIsm|k>|9VBEG~8maMs#*By)RH^09|dFyGoI%(sII( z^r)6e@!hc<&d1tObqdnWr#{)p`|n&bFTHZ~Q|rVxvxyI-d?}g@>+hDQFNEzRAzHl+ zMDS)0JI_SdTUv2D6Xks)yzPv9l35P;_h?2m;N}r4b_>V z7ByozUP-Q58ku$cD;{4>;}YxXE_1!4tqbXRaI0V^{VUs0xM9A~{Ut9&nO~833W1j| z{fwnleR9Kr=ZjwI%!Pz3|5aat=V4o;FRt9}eW*h8qMocHG$4r5MD?r&pXyA=qOrQ9 z6zigwOSVBJ%L^to(kH{cnZe(;L(|Dp0_+MiP9H+n3UJVP`+ESX>Y;zDz+j3U=f z`V7MLng{)*_mjGZKX?;d&n5*}R2K-0LyscB!7NoZdxqLQSk^cpXxdF$JH+vYev{Ze z)gyF9_eFE_`i;kj;jB{kMn#e=NZQ|LNW3b(D*pI+8Hp(M3mUJwCzs8M-7H0Q`HYt6 z$nKA5UbACP8jv8^?`SwK_d>?qVWv{Fkh}S^4!JY12yD_(_1&z9@niCw_Em!Xf@{#IXniQ%!0fx`&@)Lu82%g%1AowL>4Zl&d?#f`~Tnx47RIFZD9k!(!v zq^c8ECN4dQQT?=z6>Ry+BwkI~lzlS(fOmMZCxtG+;?mFX2&K#9G$#X)mnt`E?Qh@2 zm%H|B-rhB+Jxw1&99(2!8TwI-8B@=SJ`7#vH*=uX4iobdP7uK=VNMGl@8|0C_0W~N zOJRlaBWXC+-bfs0)Q)a+>s(duMF;;F5#_#A?oicnZED&D%#9RY&dIL{N(!Fk{x=Cq zi0fbA#Q%s?_xBo}T?Ei~jOl)Q-ZSzK(%-0Z2hKE-3 za=%%1H)4!mVU=!79w)lS!}BWqihn=g_XcFV`I?@jjm{5c(!u1TI%rwE`S<7*HLr}D zRW=c0k_xL#V_JXxo!m9NdlP^Cc7L`01Nj>omVIY+=Wa@A`{TDr)7)S5h)SajQ_yEM zyabu6iacJN{QXa{gp<+Wv5Niv{VlAo2KtdYYUnGHQ}1r^`zH0{?-Ks!UyB$sqhkF1 z#qXTppPMKB^H-b8KkP}9UOvib3O)7?0iP?)J*uZd2}S)q^sf)~TmZh*zm5>6F~P&r zOecL!!(q+tbmWP*Ux2pL@Umd8YK<6kq{7)Ag1+9e=qoQX$;5;w{--;Ob zX>q^Qg#Y{h{`(~U|EIvWuY}{M^r*q*K&!X6G{0P+{m@x)|AURmkg4zf?onxvJ{EH` zZ?G$ts>zmWHlM_*UC=mP6@j@~1v*RAXxPq}Dy<*dasRN@zGsNUu-TFFF@24n);_v^ zr`LPcb*DKxN5eu#$H`~+)1Q+)^YQCWC>bV0xJ`REd9g`AJegdI88~_vpSRM>bE(8 z=zVHItDs}-U@c<0nq}4YvQbn2ZukOae9yNm?~D``3C_`tF5aw}_MP4Z62lx;x6suE zC1d2io!4dwGBs?#2+3Q8-HX%2cY+mP2%CH*eoF%ITKczgVHsK;2A|qp`U}*;BAR&KbFcobN^jMPn01 z-XK>#p0vS+Vm@M@^)3GT5vUqniMHD~UK?!ocZg+qK~^1{yXNntnYV`jjj;1<2w7;F5$B~*{%}mq(V2V_!l+#a8 zYi#0Bq`hW!i7&t0dL(^Le`{?W)pNsG(M)G|3#%YVE5f}clF1+5t3@=fYJ~UKJ3hR( z_y%Ky*60qGR>eD1QYg5crjy!}a~Boa!YY7=7hi;X{eF+(;cYwUO$>g*g?rrJd<+r% zabh(545zFER>f&D%3r-mrA)R`r1dKiv7I0K@*vY#!?!)dK9y1>Cd@iJ+qycr*QPfn ziy-ZdogW+f!9h(g0j+Pum}d@s6?$G7wd>22_Ix4x`GV2+BJ&nqtL__=8YjgO2Ss1) z+lulQ-~5P0J@a)_4$oK$4XQc}MXwAjhLPj>vEkn2a7wX7l{Q+g+h02Pvbd2c!3jT( zG@bW7tQx5PEVP1_SASs>K3-S&?m!-S2YO#&hy* z6ka5_X?b}~jQ7sj2&7)~jAAT7ILTZ!){peEo1fZ7u!@gv*y#ONV=j zR7aBS%4b{96>m5eRtiz5*$RvF=%}isYQ2Hj&s)3|w4dCg#Ut}8r=Pm`b)SqLjhH>$ z8ZCZcVzED+H)Hj~pSOqB4{24t_Tf!$IL$My{Nr{lM*>W4aejmgErSE^=)Tu=?T~nu8(>Q)T1JU z+;LIQrWK1%yjYD9YOh}Y(6?D_=(ucx%Ii1GI`m(A;Dy>oWzFpv?Y?-?6K|ZWGQ3Hp zkKH23a>ekX#`Fu6bqqEtT-F(*Gi>b2d5=&HW~ju@f%LIp%^9ek@YFhzEB)qm zHH(waV`^PgWHvfm%j+a-Ra~UeAQdg%V+HeoReILLThwx_m1JXt#>a4{FaqAEn-MOq zwtRwP_&9Hk-wORx-fAS~x#mry*S?DWJ|=M=hp(Z^Z$zi54!HQ1n`eyelq)=SYJ}R) zm`jgd6}z!4?5K2g>|U}{d{%asp&c&?r7*{``+_oRH91!W%(D^67~eRd4@_D|r$+}? zv7@cysmcVCo9I4@8o4Sg$7BflCsIPW3R#9t9S;7m=b!?K4neL7VnjqbWp%hbm~sZTRx)!A;EL(aj? zS)Zy!Y{zTvs)f6WBHi{sI8Tu$j$;l>ld71OB_5bwHR@9{j`tFO^Sb7i)fNpVuN$?q zshy_$!t}oO?!p=GRDXAhs21_?QkR4q>8x{pF#?Z9cKs1`yA;~H`CBL?D~*b*s-0Xs zmLu(`3T<%}v0Ib+ocVj&jbVhG6ly%|uhbIJtBtnou8=lEtdr$&>JhA6WU1PSFbZg? zl8H2`Qz$S`(-A0u(;wMJI4-Nbd+dHd=~w*;)&N{)ar!&My#Ee=hF|e@XLmoppEgy8 zwn`$=r->#jtGe8(_2AJ?(Z)#}603pe)}!?F3D-c06$v?&#F)8@5?LTxX8R@f5_879+Idsm|H z>rOHRD*J$fD}M(leBA#&r-71+WQY*E9POU3N{ey!^g_>W_F~`K%FX0285#;+R~Fp} zjI0)XyT$ynma~>)6lsEll3XR>kEo-J7E74YFgh|&Nz4ZaPk20{O1s%F|QZI z1b@yn!SMsJywR>mk%0mgT5JaY=vI7I){frjQF2g1cc?&((?KJNamLH^HNTRlmt^9M8ia@(QMV;@mWMf&|rzg)?vqk zsU6keBVXkO&%6I0d+#09RND0oGvkchg33KApdc^~g8~t-fCxx37DPgmt{^aqfB_*& zhlIq21qDPY(iKRM8l?mYBvBAh5F!Z>AwVcnLJK93kc8wr0cT$C@0sU&*1OjC&%4%p z!dj%9oO4~f{Py0zeVr4?s0_1eHj6WnCdkh=C!K|8WrZ;+SaDpk)s%^085^8FMI9Fl z5K+UEo|uj70B65cmHua*lW_iT|IyV%T}dJ%-2vd?l)l&bmz>!?K!Z zQj}z3DlwLw;hyHii|@ZwQgAqH*#VDaLM-IUy$TVI6>Hjz@gTo{)j-P`5bfVG))vmj zJ}hHGx_}$GM57l&S?>|zbmxExs9O}rgyw=XO^$zd^tUCx;g8ET2ddBY=rp5$5_+mM;G{8DS$jy82YC#svqS}cpp5f28=N`a6U-~YhMP7z~*s=I0? z^Ee;a%eGZcPE4JtQ1yKU%AP2_CFHWm5rmv~<&++#kw)%evR&vo3sY`XGT1$%1-KD8 zyDDFHIl#(P9ORRF8-4~}!r+}AZ37TlN{S!mF+7z^kKKRcZTxditYcK@!iu8Q4N`P3 zW|0gM_XcN7QG18LIyP0TPo3ymObPiHETP(&jS;!UDyTfIpy5VY&f%;x7tAYtNWslod)`yo`fM!kf z;sP{xi1*y5s+(xJ3qK5VtjC++E#m5`;I3xYYvxR9lt&v0mgKjaP)SQhzYPGxjM!WmK{j*BY-W{0ImwD;0=uYmPop;NHU5a^bZbux=Zs z6qv(}^=3@2pPQ@^F1tdMoAZh2$S6W|cR6RMsJ*g5r6I^AAAaoEDvO+hIgXTqAiyeJ zXO9-Wpxa$NI^52&pf}m#qkj?4PNk5!Ux8bTc6D0P(;zb+(FJVPdQsV&SYT=F6S8N? zbts`$k4UeXC#oH*Gij9}i;DHf+nM$!|qYk%Facm$(7x;2m;yLsi-p5%6E^H)C41P)3@Az;(xXf zg9#9lTVFf2SWU}Jqhq$q0hd)57b_K4N35(&>PJn!V8zzv*vznz*)6)ICbzc0^ZmIw zK)$UTDxH5%v;tGAQ)VNlZdSTjcX=2P%Z4DLOkWy5PMHp8QqI?2i&CgF%HRt28y-E@T1E=Rq+&&q;`%1igm_fJJV-pFn;tc!&A60v?MyL?rjP0BhgZK$cYwWua8*hv!Qx67IT zVPD?`nf4P>QlG0AxBUfef=bKt8wmB{g*s{Klo~17pCA+`1R8wDm5Fn z`g=0UXS~)ne}iA~)|WF?^@dseQ@x~-p4F#d!D5Z1o-1qCSfX8*Bc+jQJ)L=nY;URr z#dt2VLnub~u-Pv8divHD`#i6OreAMXVKoy3q_n~navw_;YDhRLOyMv?m1)avs~`PYu_x2*DPHuWVQl@1nGDWMIT>q|WlAz$>b=Z;qff+# zdG%(Gza1`?gLXCE^Q9#`OTv=*9=#k4FiF%`88V-SE@+i2vL?FlDyUEN2=+aU-dTB+ z*SI-snX{dGKfUOejO?_SUl5q7oB&Jz+9PE%3&uS4e>O$w;a^?XpjYOfpx59mNEr4q zaA(d|h=9-O17dGMu?g+pMvsd{*=1j9_bd${_Z76}2y9*HRoDCRij>*V)hw4XTf`R7 zv_T2o)ijW7>A-MthtX2e$=OE(kdvXN!d@*Fp)m(r>M2KFq>Y8lqekp;PT{FfsjYBy zB1zDz;va(^uBtp-Hs=pUn5+(c7%Cjk>(*yYaO%az4m}K4t}DMmw4HjynHM)jAoD-A z((Viyz-W7}SV>tF4>6T+(r$0V~$p>AYd{^wWAtzxW+4fC);=MJSP_;8#T z_e_B;*@XD!rBSG`N?ivry25Gpr&u830?!Z=d;^rC2RWSTE_awL@PjL4THC&v^hp_~ zP-g$t(CFTgm$Ee4Lvf0TJAhzip;rw|uQ&9-CSMXd7g0y7j{V+mB95yh`H%3~@VR}S zL$%%cDFry2+Ac-F-O2@tiut~&t2#tcbuvqhU1pO(3U78FGAt8lw-Ki*^|4q#D{CCt zHSHDm0gI&QMnyAMJo((IHr4|-y)$CNi{~bnaB^2eWAp;JDqN9qTnXq=$<$JG%wz{^ z0yJy;Py{qBW+gmryE1Gb&k*BP5NA`9V2~kF`uZ8kN_F{34}N8bB33Y#&dUMX(xk4i6mAZ&)UqrhTwmaKgia@ zSjP8&47ztH`O7#(JJ6nmnes|F946Ri6doSUrPeA@`jacDfXbjm(b7#-aaNI*_Ks+7<(bf`7{Y+uwH|pA=cn_6u&C)LdDQn84PcgN zutVzIgq_)w{zbGgZUL%~#ENZZ&iyIhm zVG+{wuym&Qx6rVF>FJ`*yxy84(Y&T7A?(q87NZ9zHsc>qE zzYR2RvY6IRu2+O#Yug87dAVR2buiCyZ|ouwmfMw2#Hg!tY_c(CtTvct{3NC zzrY{G$YIDtZlo)bblB)6Q>CObuuGAZMNiF_4t%4j5BdJ z7Rzz5E9hvG=;ft!PM$dXMQ}8KY*Eg>RhC=u(8lPunIji31H6;P7Hcn>$GyzQDD*gu z1&5=PoN;WZ+e!zdRmRi!i9b2YPzMRneH`#p+WotP&I3rR-T-kPO+^-B0c3;XnYOak zy+p?RC4#8B8A}Y?l|1S1Pw-bt6|v_WXl)7Y)?)rdh5k6z)Sfc62&YI6s@#ow3XT9O z)vqa*)PvHqiA_fg^3!|y`j@85Sc4<&7U9Q?AjGA2iZFyo>GGA}e@KMO>*dDYRn?1c z$fIPNJ7vfY*Z*OsN!O>n*<>RXa44Nq>N%abS&lN5AEH$;&WBDLzHw>HufBmZ@vc z&~&9ZYv3s%6eZP-V{d^wa1%615~$g{4hJtQZEK}Iqa`VbguPm0v0yo2KRg$&8N%-M zocK8vJoDop;x)JUZfrl_5dGhQNecJ>Nn*T~&hB!}enjH~Sj)?curFVTRHJIKQRx-- znYDJqOH}W7vIKDXn55SFPYvR%9(t10RrJe^ot6$Vauz5U$5kp62; zKNJ??AIntbQE|G6-*sR!{~P1KGE)lc$^98NRNnpdv1HoMxBZ7z0obc58PE5&T=+=$tQvuZlwd{;EA5#dVswOjEyaD>(SCHGv_+^dF;r=CoZ($3RPP<(fq9b2;R+M z_5xvK7J|K(gWTKZS@&C2Xh>ziVl1akmNvreOOH~0V#1Er)(K>v>zi_!K39VbOpk2V zfjvczlyXgpzgC5Ry!Jc{?gS9KQrqYg}&TNiLXYWB4>HK4jLh=FAg1n>4yZ;j3rhW3D1 z3CLrWlidn-% zhZjZK$vc#|mWFMpL;YbN3t5ke;g%z(x7b!0#5lezvxo!myOStq>FX&tqg`6o>KU!u zkNgOy-lTI!2S|K;^to@j1PCsB$ z7sgp@{btF;^pVpEO~1f|U;Al$nzPaZBfsjw7Rd?|upW>tcBG&)zex48c=(fkZIxFT zyAb)Yua>q)U6xfll8U=&hq)>`nW~) z=9nUtoxn8dk}sN+VXDqt7wpamtQm<98F+B7z;thdWrMOGt-9I?vOlihn;SB*iEJGX zF)*ychgag=07sq!ynEv#NfrzMxx_yu+WJp;aQFy9I$d;3Cp6#DuPZZl3OLCPrvqH@ zz6*jg1b!#c!6DkZH=A9mT;paeUf$;r>}DS9)`Kt+j5$#CP8(p;Cr=dhZpDyy?F6H8 zLK@WG8``au!VMY@iwf9_X_d<&;7 zK1`>{m7UB{vzrbm9f z2|M@Vag^S)HLUObM_6@#0oG!B6ZV%a28HwyH`8M(h!IbFON66eG3pCG8_0c(JG$oCWJL?l8p4G(?E?UEA0ilMd>6iK? zWqSieh8y#?)*Nc{XPx=W!~YJ@Kv0o%7$x-Z;vXF#vn;dtO;Q^$oOb)DR4wAjQQ%%T zoXasdmBTt&*SkzvH7+UGZaluTF2W{ikPu9*e7yvdWvl{A2<{)oL2c{aXjb8?3sSss z*nP8a^k)oT99-h#n_#STUj^(ycrc2v{K4HGC+InFPz6024LfcrxRv6O7dn*-DJVu0 z&HeT}8lWIx!~8rrQmaZ{?%i5F$FCGzEWo(=fHP7mD>2LXG5zr6RaT$pY}%y1%#uL{ zsrvJ?t;gV}UHhn{H{Mb{JWK_)!1SKHJq7XQz(sM$J8QXu8cSDuggL^shKC7Zb7<6T zuPN^ID(}cl)YYhKDF5DW8RlxvX)7a5cjV$p7A6K{k3hJ&8jC3`e5;K$@OwomQ7CxZ z2U9A0Ha#m!M>Oip8xDi`a2u!^)!@vr{ed!(&*Y!(1p47den* zLkPB+TwTIp@0F3Y-Fq9n>3vya`~59OQ4K#+gnO+0HLd(7LN%5@?)!Ac(%3jjn2zuz7=a=`@Xu`QN# z_6>B@H8xjA9w*EQ8>k=BixM-oo}QW8#JYtT!&)a?1sb9Uw6l;5SuNXJ@@|CW?$Jfs zqu_)y>VC57Bti9tShUt__IbmCn6?1e6bSi(rO2vt%RbWF>bjg!=BGT9R zTHmYA;9mlbCxrUG*DWWR{}Nv_4zqWdjATxRL5$j>yd$;6{rwDnFxNc|8>gKQXM3)G z2&v~Z+s|HTbq2buA6Z4@N=#{A^Qg%UVyz&&JJjsCeIWPGD4s zO)r1(A}ceEHR0}Oj|0uHgF>RNTtrA9^FSrgdHzSU!}$LUan{FO2~AYr@CQ0+z#7Wl zj|tx;o&`)VqZp9Y1>>oAfUJH%Xqn6LMz<^L zQ&(1qsMHlMrlC3Ej46G5NIEma!=o>gzc^8r86BN&Ot|RB4n*SBjL*C?Cd}!)p5eJIbxt)>M2>$KE`e%VRV`ErYS^*|~czans31&}N=ns1uue2lxhcrG)`j8qx75Ujv5`p;tA?SdqhaRA){E2tD<_}f%qJET$(j=NB z4QN5xN|^A+)LIKxR-?)OwW2osxjbo9j}-Ij})gwI{Rb6~Ax!B11-A zBn%~)+K0|xLn6%hiBGAV=2T49D!EHCRD2EqH4@{1XsiWFNzMUOc1F%^vMV2p4nr10oJ3lV+rmb?@?u#%Y8a9-0QeP z;P#H!I=umKO@=8iMlf0`FRhe!IxM!}_5TD37n*4nPtCD_J^L(NXU)5$Vs5$)xutxv zP*gEC23mq~UdvX75PJGr4Zjx{q-4MwGuGM%lFVp*b@QLkffbll^`4?@O84bS0+1p& z1O_a<(G4J^^;S}PsqXOJm9CC7WbP_21dZ+B+K>WyoG7 zbB!25B8aN1J_Yd7a3%D6Hr3M78LQKGW~x4f`?GXGqJfh-B5u-T38+e<^wJkoIjM@{ zSZ|#G*u&H)+ryOpeNKu+MdaGs2I{n-)HbV8)ja%|8Ebh_&QjgK_;g^@V?V%x6$ec8 z0(xs(0Rs6VE0rvEaP97XebqAjRVeEri$L3I6=x7r$wHXtH|P-#>%d^P0QkzrZKr9` zKUuZ9F=#0$Obn~NV%era#;GByuS0(+sh0L+IbLH%q7})ubH{g4O7g~yo0npk*PVIz zNG`UaDdFg}po$RTw&X>MA!zqrvvu+Bj8t&ok zi7?@--p<9^$tHG2S@c9>r!z7=Fgiw0rTT71EykgYpx5Tv0Y;m#u)QvId@F+J(QWEW z{)x$@{Y7a{bP)uj(9N6v5<-~OegV?Zjf2s!ll?iQ8uYlmxZM#UJ$@>jG5;l z0|6EG!3gt3IdtA3VAaIs^sqS+Z2DDtWgMeuS0Yv=G%$UIx=N@)rk8(5EqD~uV_Q>=h5`GKayyyQ@J8X&!dfNMNg35%vh_NedXpuGRuV>Y~yV6C; zFz8%SY2jLxA1F{wY!|KSuwEC4Gs}*K1r?PQ@dImcYE^Co{sRom_xzAa`Z%Um*LbhB zUy&$?!a5g%eY_IQ>k$_@E+9D$uzS9Y)t4J}eWqHWzjexJi&RTub<=~x8Jr~rR| zNo42M|6;H9yFke56bueqL;Pg>FoR*g!k+Sgx~1sZEIwqu`dC+a1!k#Ti%B7&8*}x; z56enY7va(${+6!zh>KC)qB_n@00pg~bFT7tgK9h`et9-U4`r}KNQ+#gQOEEYBD#4Q zpHexITvJF-C6^y9ot`+ia+{eOYUX+XK6@*Ym@s(Dd+{N;2_UNRW0luLbuo*G=*7C9 zE$#i?5K}2*L)~K-R@cfRwJs8u-`JY&fB-5VK>}~;VTyb!HTP#6R+#S?H9bZ_dyBAM zZ!UTJ0~&aiKm zb>M8toMdU=`h_lU|9D-0d#x;8`xFQS|9^e)Y7U;$8Rv_68pU6VWW8`<(14M)xX?Dx zJ=1NBSosd5+CrvBqAD6?w-E7{gMT@Ae&`*L8?ImWB!TTU-dr*^$+#t}+}Ae1?boPs z$qUI8)(CgWi=CRzYiqD1&!r@@`RmA%;JmgXL-K({A`${rvi%=;lB>}%;@kTT3^T3<_X*{X>ICK!n*He+S2X9 zE6cTgcp+8w%P(bSp8xZaxt!1rbgYM7)RsRVCwl}yamRpV{-tDlNxCoDc#*)fzs)dd z#&hV0Fyvnjj{;U$|EVddDka<8B9L>ReDF=dH*!+tRd+(aALrY%fHAD?x;DeR+a$+z zqdQgNuQ+wk%{(P%yp83sYB?5loWeTJCL+~2VqD`!R!3hh6_k#@aO*dw)$9^!j{BcM9 zBJrmieP*(NQ$EfrAL7$-eum28K3?K3|E9Hx+^C!{hLg8(i-X7~xW!8+t_-bs{3!Y4 zv1{eJhISlsjUc72?VRu2=CLwsc(~2Phj2ddNV@gfyT1kqCmFc+t3`dd&#Qj@xW2`z zm~jM9Rst8y8Gl}v?b#3C9oQhW4e`P1Fu0yUA4@7cz+6+*jYo5!>tt;%D1_|F1%;Di zDG8=a3aIj-Z$|>ZSQq`Th|iKqNsj(&cyb!$6T>HC=!4{?)MHbjh5%)hp{(@j+hj3E_jem}$87#qv1MQbBU9XQMzqx{RI5Wy5!UoAFm^*Y$+1 zTuo;k%4#m%OuM{QNy69wThGV8&zDg5_pJXOtIOXf1|JHxDja&Y_NB*J!j^vA@M0P^Y^4IEbCwbeYyLR|9 z^4|{hZ3n5s6B7Lcn@jE5zh9lciDA%BaqW$KnQv#jFteun8;;+EeWTcYZHLF&-;Ci} zmF(IZ_Av_UFjywJLp7l2qW_ISoGFbSEvR^Y|FQ5vPM74{xzE>i3!E-rLFd@|$z^u^ zJ*eNRj-+O{e^I~l)mB)t5%|Kt{}R!Kncu3e3~gH5Q(|Llg8`;hXeMjB_Uguo(|=F? zjE-x>x+o+zEjh8&n_e@&j{0@UOWb9{?8NdV?-}#jq%Jnwacc?W#kI>)zeetB`|WVs z{=;T>M*yU7sa?6Jki9>YaRcZ0gD*MJ+pfTixiS0J zl<>!%ycpY@ZHEUqhod)u4|(5R{9Kyx$}Ms%2kbmhbM9w^_?DF5ra?8H-n%@q6xNIm0nIB z_JyBjDm*r+#+Bx%I37)XoaK{_|AA(`loHl=?6O}B=SbsSbv1m)1%Kh|2|vIc&?+_1*Au0GT|m4k?T3cyWTc6SMl?AWz%g(jC38wLxWPv5+@VU@CDr(+)>dWqN)OE}I7v zGCpPp$cBxorTc+pA63s(#p%(SG3c(jz@4qeG%D|UEp7TRzyxXTuAHm6|DC?UfWbXI zE}H|oGL)AWW4^y~u5!*!qNMV1KSf}^-)Tu+4K1>yF(VkFXDlyOgUY$rk4S^T5*0Y? z9UxsOEGee?%i-<%)1zR3-I zsrXQNBhvfa(@2PtT6mU1wu0*G9X7IT>)K22!&gVvCtPCw#%Ew>Cr!jnqfTPLgSTwg;JY98jd!}=2 zTk?v+K+bW|)nVQSz{SsO{0@gCqI(Jq3VmfrMF+XJqrM=Fab>9D2LN`S`{2S7H-B%< zU)%H((vSO8Y_d5itHva3s!b-L51-#PH&xh@EpMJYmt-Ek1ElWO?o!uwVQ%`gMvE(S zYpRwxeuvqod3O!y(((uM50Ka6NyC;V1md|u%~D5X2||r4`qI8_^h5V=jRi(E>?C`@ zL4QYIE!HkddZO+f%kJn1FpI@?&^?eZ9RY1P+AlrB;$rkn&%gLdO^azFw!R6ura%2b z@XJ=P?gQ1CmRRjZ^Dzj%;vDdcrqzldGihjYOF0x%_vXY3Z>S5ppo26!kFiI-as&(# z*k|l2$T^;^YiFYZa*!F~?m%8$Tpcr@eJs)MNOcH|-{sjGSkcNuvklB2;S8z%@jYH) zF}&J8)JJ9})qPxkx@YwxZbK8JYY@!GuTXEX%|*(f2lzSCPw;zr(=oXI1pA_P876J} z*p2xro`{dv;2-Ut6L|G0kf^+#De*pJ6MvU#bew4Og6~sEg)sADvie4;l~@HZfGPhA zyZ0q1fFmW|2%rtE-o%ynlZW(`A>iVvJRrK1h0*pr%`4fl3!PfhL&SdXr2`j6{F$)2 zpZ^6Yj9oX?haY^rFf+XhxjY#SN|u=XW}cw1g)m>6JgA?ocMtBAEl%APTb-7(g?D-$ z-A^2TIXvSVF>|=K#Oapx6=5BR?4#YqaDbbcS3bk>sK~n2EX%DRWh~oafH*Ac{TYtc zG8Ki~qK+#*O?L68IQxKWoO`Or-q;gQch!!0!>>9GkU|m{K8crJQx_?FHAzj!#Ye5@ zYn8xib;oGL1WRMh4qZy3k!6&|{7Jn%kI`3`IRh4UqYGP6WEX(T+3c~ zP^sq@X4bGH;ThT1^kVO`s3!IgZ+h!4<1<)$I}`>I z>L4<9%k>G5)G%Kuad}PEee8#|@AdUXNAU+lNBJuiZI>5~)QV%6mxQPXwU5|Ykn*-i zY*t1SWqEG=sb)eRq*Zu-rGi2Mfz3AjvFE}J7RY1vtOv;#FJf#}*T4=yi5xSDSx6p` zg(6G9@#Uletv8G2!65%by9sJK%Rr z!3wJmkX;24#XE)cKR1dR^fAr0&7c9FsDx=TU({u}LoE_qx11g@L@aja2=|W0ptD|t z2tFRw=Puou(PDR9y5@*i4lkNai3%|$ryKr_K-4e6p1X0(m) z#lO=%P4Vf9an(%&N1`kz#51_p;ND0fX6629ztuG>o+i>ge5cx~Nw=#XK88!`QvTw+ zS9FM2unZ0d+)8193|G6F{!G&!=O1{a)jBMDWk^93HWF@q%BWVjnOkAn!ra5p+KGx^ zIsePC#nqm1$|2%Lmh{6SCEYv z-E+OszYAk10sM|nv-4F{cIwk)Ag~d|K~D~q%pb#uaahfP$7NBz_ZCL)m$o2WkS{pA zicKwPx}t<;&{H-as%3h7ou2IUkjFix+44k%;5ulmen8oQe}cE_Hm{ur$zj6KLmz9u(I{`J*hPh86+H%uFK$CHot)fT-GWbFg6 ztvO5qgzHYn{KQV$IWT?Ld*wqw%kt0C$<{fu4nC$;HY{$dR;(?XyfBzx)>sJDX!bbx z>FoC1=B8f`gdKVqXZXktwV|_lep$6Ub%eP&+<={KF{Zxw$cV|~jPFF7CK$9-C%B4l zMuOfq-;;|+p^1^8AJr~`MPSrL*Nnkm7UA`)FZs;-D+>JRpHWt`2EdS{AE{NJb3@fw zILUhYoPIW#$RW-pGR&2k%{TY*juJrDLWH&-^i2#mahV}q7od=q5Eni%RJ#?~!t>|& zK4!X2Bw%p|k$SNZ(CUQ+UrR^aY-7)8wB=1BfVDxy+0J` zw3J3?JG*DpNidK_=$Sa?i-*j?jI*+%Ph?f$*( z_8X%`$w8wV5;Wt|4P1mzXGYjoXX{&cN547|Im0;Fq7FyBywJO1GupT4s$DhJ?W9zF zwKU|-YT)SW@V(k64QE`Mc9+@b8rJ2>C4ngWS6pi_^gl3>j4)^xe@jx6j;mJc>MRN~ zUK!c)xlKCBrk}6v8&}i0yiKD{c+Z@SyF+>U?x8d2wnB-0S=8J?jNRg5lJU|Mh+C&` zW#n(yTiD>#5T518Z#!Sa>&6o}hcFjuSZM#oEy+HLc&D=H{9-Di=|^u~*>B#{iQ26R zYSNG&hh~t%tOv2|C3P=|qb{rSJnTzJjXlmz9b;B&nd~M2mu@~^s0UAqbc{%~r`E~~YDYaHTYv`Jk+OpSC@kX7HpkYoQ%HET zO?cwK4z(Wp{P!2UyI3WwS5mj_*6UYZHc*>(L|QG)l?a^G96W?MIoj{$=g1MT3j6bR znBSv*S}2Us1M+hZ(ae602&7Q;E_M58OG4&r?_|Jbb?oZN;D3{2xaNLiPyOW~+lAS& zayZYT9cue^z&jU1y~Ko(Sn-ebX~Az%%|mn5X3!&KCC8oYg}qAbhYeo-0z*R1v!JIF zgxQ1dME!K0bi3(@dQgskT~^rr}|UAVWFgcmgKM!70Bu! zcj_RK>oID6J$;Fg`mnF2tDb!zA$@S-EBvfG1!dISV9qwb!x*z1w|-Y?>|rSj{7Q;|V^`&w2~ z-3vQohcknd9s(^F){7fSB2Ou`$6*ymmiDBhY1LM2ENYG4iWUv`OK zk(Os;i3g9p65ZLBz0f%y)aZ65CrI>s3$zITd%q4cA;;z{I5H$|8=1ZwCTg=((0Vrz zw))i+T2j*jB>lFk?xmSsXT#kJ-unq}lhO$0o7x5Msw|Tnnp=`jCHw)dnyXDwwc99M zb<~ImoOTU6^?=;e;1-+YD`qDoPv{6Am}<04$SP=piMZ(I;gis$yszY=|GtnXo;;2x z{unXBV$%4}h78l^JP-7HBh$Y&la}`${9-w4I6uBHPf6X${#=kj+}jaw##5HvUs+vA z`ntENIVS;vBZJ(%%gHf0KDQV>v{Pwx^FXfp$l^c%^1|wLWc(dB-XN~8i9j3tG?lOk zQXmB1IaU3hK1%*7NT2t}ySYi1NuQuNqr-d|#4QqTfY1S?yAhHHAU+9=;k!`>2^4}C zE9U@XGB<**{co0(JZb{s10Vvw>9u}D_UG{yU#F7S??>cSvT@o#rEQV~K_kdnJ7sbx=U@>5viAgrYl_U3nYhGBhDU;|*yFs5`@! z5uqdC(P3q{T|Ho|!< z^$!I#QtnKc^Ms%>=hL$~cI#}?%)-u9W=R7&Z`uTbdHMT6QU~LME!Augu&z^?QNpR>Qm{DcpF!^Q~Z&-Vpl-5nU&^@5|~XmM2ea4?Z}{q?p{(N8%|Y~T9>_3V|&mkZF+ z^DSx2L%ea@9q327yEW8O2by1VHLQqSkOsr!hdPV8M&6Z0Us9YtH@$E#)Kay7w23~t ziZ!IC^>H~>b5qu@EVw3Hgn~&;%ZRSaXj$>2O@ZTzdt2%T@Y3cLT&5DQ>2gQy<(Dz! zfvZR6$8`=4k^i0P|D;D7{xw>8;1K=UAbc0^aBoh($ZFidQ0nVcSKu03A%PjdP$WbW z5UqLVt_%S&JRwE_++vp-lK@mvU{~J$sMLI*Y`VPK6^LvG=#im#3e!ZmyW=DV2p8Y z*|-Z-mUE-YsH(%b7Af;Z#vw4DDm!=0ry5a+A*su2N?>p}A7YP})>^XCq zAL1u`dLtofhpM6A`UqQVNQ1XiPcMB=vrmLBGe9k6Z9%ADV&Ld0m6nNBZ|Ivil^SkR zkl^ija&PFQ=DSE7a?sA3H=8@eOt4fV$G|&<$40sEI?SV9&!YEAYNFXaIM9&Bg;haw zQlbEwJa?fXY`04pI=6OPpDX#{Aj##lrX=t4^?5!2g&iIF#=aW(>@9~fFgWVrUQud zcmpE0RXv^Jax1v2b0ot>NBA-T`YJ|^4olm%dMrmYsI3ttOO77evsRt*IHqXu@%i9G z&|8a9;5T7~PXcfo+#jL+C;Nw8gQ>iibk;eXVX1Z7d9>5zz(cp&ej3S365O8^ZMn9< zNC7AaOid^62_yYfwT{3o0)oE9*fk2DxF!6+t3$mPPIyy-fA6bj!j{t@b9}G6>4SC) zo{?voyR#2ve41$jkm<~Pp|aG%lAj@U>6h=#<-2evso6lmO0*9^td)^&=Tw! zcy_pY4*W#eW-6^cFx~7*f4N~48iC|V3J9zE;8IwC2Z`Mx23!eYE8IG&($I8!?9b!e38AG>^VKZ4QlaryuJx+eMnn-{$Y3 z#v}oW9Q4{iE1TrG-VE7hCYu8=pBFXmwI=9?y*OY~z@D(-}js_cDjA3j_R&xxxbCc&`-}IoTMO6aOD>692AR!H8?X|r)mHhs9nhzDwg?VC@PF?u!R%^8p z%KgFq{h~+9zSR)g$~y99Xs&02>-pfnKTGn#Z%`~LG$cxC=sv3JDY`dsr zbfimA@b28Iz{#iLV|%oflPsIn$3Bk<%QCJat9>M+M)pFv78S{kRdfA&+Z^i|k=)0I zNfTQ_$4L~;A>dczm%wdq+EUS z%TjxBUsEi1dc3nN+Gg4ge@mCda@MHQK~f5XlP9a@aDiWn_5H%KxA~g@!~w8AVqzT) zRMf9Zj}VK(OS%{*TzQ?jG2w-NL!N~OmLKW#dk#(H<1+!b+_McAtgU=2-v54B33)EY z=;GG2{-)+0RL4*3?MD|~_U!E#qe}`b^TYt1I*9G6B7$=Go#)b$ZyDraG!z}S7zyB! zz^@u;ion($lJg5sueslPeFn5~J_84Efx{~2(1T0&fOLA{jHqgE_Ek;mPfUkP^R{=v zK^-o8Gq}1{Izo2}vFImr14*5>@byiCM0^S4!*-kC1zdUu!VeCx6YRGuprl3JKwM@h zx{1-;_zUysEk46DpdC5obWf`_hHH~e#bqGh?fEE!iE?{3 zrGuP*Vefspbp?8@tYrbJ!B0_C(R8wzHNorv+BZeo#YsT5#CPR&v1c4_S!S<%b;SrB zG274{Ox)`L1#Mqfo~N}P9u;=>fdL(0#*+S&6?eq7CTW^Cx$$6fC6eUIL_x*=u8^_0 zu>d{K^wnm<0-R#zZq4`aU3?7l5h~LUqnEsp93I^2wFOu2W|@Jg6bFBh;0kH{olps6 zCZgJxDv$JP;vS%w^$|~3b%am%zR6-(Cq=Ga;U&&TnkYe{dKNFd>`@qiV5Sdu`r{RL!_?g0Q~>nesxI(fE0&6E@gJT1vuE0Eg0`jQb= zLS2ze#fN-Bh3A4jqn>$?XNC#uCqQ4hq5XuLHJkt^ z{V1l2k$l%W%vk`ZOpzFprurss^)}j z*&|*qEccqfs8Pju3h4A>;-S&3tq+q2FGw3^p3N50af{cMgm3l^BVi2 zei~bH$7Oca+xC_IE_!vOF%q zdo?YxFB5)?`LC5TOW+@u6`Pb-Laruo@vCtObu(mpW9quDJ(a~6N6+#E4Epn_^Yz7h zfxyI$xy@jW|B)2Q%^1XOzTkD_*BEL-HvpD80I&fGGbYI`o{qDVmD^_a z_CY*+QZlC5O!p#J-obmINR-*umB|4$TE2@Y~bLP`;wr(oLJV< z6R^aZ|KiC5xHj6c+G*v{Tc9$#TMl7$BdVj4T%drta+PGK-Kq^J+jA?Uh?mrm-oDmY%@0Ea5o7f9 z3LSlbmh7f>gQ>zQs8d%sdd|NHj-9r~)|SbI?*oxm?c%9?3q;aEMIGKApbmEq;>wTQS?LQg z1aA$yc6nMkF2PRC-w1qZJQ*o|LZc@pg}{(?t{dj22k-aD$vY;7NQMrRxbg%K1-LC~RtfOHTDb_`Vk zLAr{7)F7y|6dPazq)8K^(nOkqfJjILDbf{?NK1fFq=wLvkdS=)2{LESdEfK<)^Dxv zpU=#4W{vn{KfB!Jx~}`)TinJR`V-GvnOCwD$68MhB`Mh$$0RA{4Q4nZh6(wH)2~x* z$giPw?f8m_jn*+=>!}VX(>)7@hUbt)a~L&e=16v%vD?YSxbHi@F=)=KM`|luDYPgi&M(QXJhcRE%~s;Cf{oyrRA$V+o(M8b74D5ELi(`y zx%1EKyyj7+48bCYS4-TlJHbYv8hQ4_uM03}zT*Avw*sz(y%FM?uILFPoge}W-bmHC zoPnX*UuLU3$LTI3B9%22%?hL$^!LlAPF?K`vU9ylRg5ya5dL;BdYL1NOZvcF@Q#6x z%O$448XT+%CN)V^`T_Q*J>D7GHCOveE5qrm?aCVX*42gzt)7Y{k{Tdw$_$4HG zArh?v;8o{Mp8dUTif)vW(G=UyCT7Zb7C^)ZR80QC=a-iJ6FVomP8yf;_k$c%rE=0{ zEc?11`Q0i;&tG+8v-sd&R$P2JLv8g!IEa2+&R3rF94IM^n-js)AN^wd4o;Y(`8Cfwoir{mdQF{?Hck&+ zUYC14^Z#5c+aVg&F%nAFzlaOR^t5UXPulK5ST0Zt6c3+y6>O-(Ff_B$YWtRZH+QLr zv3x2p(GR2VoDxblmL8`1_w2J3A-uJ-V+`a-w9SaSDAsU7KO34`T{5FGLu>uAaaqUs zir)NcA8dtuY08|Ba5JVGiWf6*)!a*Q3+9n0AH?h;fS{r|OV3trGxy@FfSfPoCurDzLPgaNUrd9H58czlwQ2F#|s z;t4I6>V7-Zm-T6$<+4%qn1s6 zn%!KlJy5X4VHpB!EpUb>X-eCQrcJ#OU0aA>N8(Hp{e(Rf5I%-E!I=h^f@0Nzzj$R+ zoapkynW@ntoXRoL#6X9)&`J#MsLtuIJvFCz4yort4m~lcdcCN##A&NE&Y72@>?eEy zd$;i~v`)6OCTz}|lYU*)W5%j|={`5?$w-t}aP!DVw+As!c zM{&I3gnZQFJ0Q)&1A!+BHJUxv{L~c;%8{npAI9-{A>`(QbfjN28<$V>n6-@yNl*kX z6nH=dNNfdP`X&x>R`&Q9Iv%9d^{k~Koy9jJuB8N$e-VhJ4oKfZ{Op^+h>@@>Tw;yw zl;*sLKxMTm)?v5RJ0L1{2%eB^in#)Fp?#Um6syiK#Ju&M`eEq!BTYnF55&J?s0g;e-S2?*kMvK=OZ=7N zuu0X0)_Q?L*zt#33aOjyW0QPY+=v`;J|?q*-Dt|&EfQ-XTQ+59G|AkpU`yh@?#wg7)Hmu9h8}n) z1bPu+6RaIi+f6Yw;mztM1e^A~!myJ{xEN8)psR&^4Q#NYjz!Ct2t5Xf(fwn6M(-7r zRAyg3wW^91ddg|uDt&4ePSm}p2cqh*%?(o?#J zW(qFl0kl&mtvxL9yf<$AxoiSN963TjZKaaE!khBawleK?>FE*t3!b3k1x$24YQ$;y zWe--d@x~LmVfi3s-2zCV!;?xN)!mBYzVGEP1gZ@gcF9GDorqB^BwZt6bm25u=nyby zr^mKfq#PD`8wh+6#tVHL_bP zn{g&D&Xk`jfKaxCiM~5ZJB9vGoTI?0+FXr>oefzbpb8jQomUz}>N`GK8>BMpM7^jZ zje8}k2aE)YJ#J^#g;j-ChmZ=inh^Jc(z{v*bHe$epSVE%$1hPNzp^Ob1_rz$Qha63d31-3TT&WVywn@5sYD-jGQe_xLJ(+n zH_{=XV}-O+K)x7L(gvluN8ipv8d~t_n1+O{53fD!tR*6@6$SKsG9guB1I-JmRn&RH zjWz%GYUBRdanT}Zz*YmwzL3ZE-F9LN_6LL}zx=%K=iwbQE{@e^G`9wOiXwUDtbIB4 z*$HmKcWlaZPjNBeVd%Km0r{OTL-?gR-|XIqSItv*Jo^H|g~ciVZk#mRSrcB{h z6B*Ns^6`B{4{ll4`Tjp_e1w7pQsWyM&FN!QRlG~hj!!6Njb;Nuv9*HxtQVMvKTod9 zbK6YATJtg>U?wt%mbC1zMOH?9H)z*IHLQ`_KxHZFTYJ3m z()H@4Wo4dvgCt2Ta=s<~|Lb2aKu7=-rypaAYfL&G#IR`)-_)siu+f$H2T+>5| zzDFK9NAs$tYA@VXbxiq?S=yEs5a_m7Ex|pGwM|>R{a6C!8x$7ZO0i@1hhkr}ydgl0Cbv@zp0viL2anq*%J_Abj3^`gv^;%(&+%P3D8UlYiKLnW1 z_8A8^LkQhcdi9CtitCRnS(hvPzdY`H2)bToEXNPEi#p(aawv*juT3ggddf+uJ-@U* zWh?(){qVnSU4Z^yIW;41lVSJXxpEqar1*>R<~#{d1$uLK?=QXyid4+`$py~Nv1O&m zMAb-zUC{@4FZ8XZiIEl8qmAnM@W?SI=a{}ORSPXQk0|AWF3-jmj=TkB=TBPy6?bxu@&_VzeqVc8E!=u9}{OTS!2`>qgZLs1Xnc(v>h%i>}UYRqd2Q8II6{R;!$HPcJ|1Y1511 z{nFzHx*FbU6}I|+-1FO}Bf}eG0y=lWO)tgm8B07Hw(&JpSB7JKjV)~CO5Ya(MSJM0 zg(XaS(Lsqyv%G?rpn&9)ILw`~rK=~{VKsvGrEd{|A!rek?SGm`Wj%^{CJtzN_s5=> z+EwvyAGXTow2sm%uQlWR_9ok_;;sa*{Iuze&OZu>B>O+tN`gY7u3Atj01nB<*w{ya zTiE4ZLEDj-l;-B1NPCl7eQQL2TVL7Cz0O4s;e=ey$9EEH-?h6EHoIWYK%+7;l@y+V zSDUR&grR5h=+uX;^$qlH)`(u4PAQSGboNO|H_}U7l{C&;KyAB0=s3`<7D)dF#!0|a zm)oMoRlC|+HS}4~jQ=nQqayZ;^aH+x1CWmO#kI%ct%uq zF0{?|F9H?}ndpIw3~j%rP09$a>i)V-1F;t|sw+BJ-XW+kh*mS_E1%ztHaOS2Hnu}L zh(5PL=1>kRS;5f3Wwn%yDz=>jP-9+s-V|ftAhM6vS(@t^r1!mB$mmMNm zO_1%ZFa4T4#OxybsKT64cv2)7K?&Du_op|Cnj3nYQ=ndep#WqRfG>am2A(n&7|C8t z8UN^$w(MN>mDA4~t9S)TDIm#H=x_UD@nwk4&(yK7osnw9t%(j(U334F};R;vcCW|{XA zMXQf-kB+w1`;%K8gFO5&I*n!eI-HCMegclaeenLX8+N=6gxkC98K4l%$PW^D0x>l- z^N??fo_9aaY?l{>6 z*qXF30o&){tPU%VSCo7CNC2l?N809V|r9KNn(8VuMkfVDw2U3X%f!lUf); z8R}JLXlz*5oJ(Ht)_u|x#F}Lu>HSI}EC**FRz70FnqSR^qT>V*tTxYNxF~*1Mdsk< z5m<#{?Wg&z!9Gy|7uNWso8LoB&^B*<3qET7GtASRYuw4;dItssEZz^;a>`}CGR`1o z=vY^eqc8r@LsO5y?2wvZ#5AG&P$NSlDXnJwCj6R-ag<7FRVC@#A>v*F5frhw2|2Y~ z>C!48r%dg0pyIKI@NcJEMJPs{jduxM*EI0Cs8+9db|$XwDVQ+v%U)BvNEI~|9$E6* zm7LJ?KA~wB_z4a3cAy0EKUWFV5`p?GD0P8A-2z*lEDqA2+&Xd6bGT8g$U1_0IB(k^ z@=S=^N9VDz<*Gzmd~ZH(W%d9Nj(`H2^#unthDbGBOPJn;44V0&W+9|8q7A@meKUuc zs5zAXtYv-LcRA2NG8|Oal})!uHs5ryIEVDiv_};cU`vcbh5`FaBf=oln24A?qtKF{ zaDMtQkkZmf`m3aqO(tCL9BAHrgvAc9-GI~3s)4;B*m zoRfJcO|lEaq)!(|a{Lw^@y%tDr)xcSfe?*fQ0l=y&&j%Fh3x9OWN=+VxcUAAaxI~J z>~ZJVT3$0GRsm_-ON}d?)_G33(SX2}0V-0LY)9^ips`q02?a@dhUKN*sPIV_&0KKk!LFLlX<`$msmF_D5+4@J8CXL}K%6nMJgQ3WsGXtrHOD<-mA; z-Cglx8aii*nGfd)?p&1}pFO(5Nr50i+uH_=A+Cl~?_ugqA#bOXTTK!n?3#~CY$2p) zFRe09)F<^qPU(E(f#j>&N>(KkJGPb2Po%b(+IA6v)(&QdqHJG~Y;fqlA@gm#L+zYQ zPakoQ1P%7h-uc4eJrad$A{4HnPIipF#2QfordCnse}{$wFa^1l@y6GuNDXati|QFB z>@$&;N;g{ucY@OMR0t9lMMhi13I*u z&HxMH{UbZ!DSiPLzb*nk^5POidk^GZoBHCChw*cjUcKi55~wq$=H$;_5+O(CCrpcn z8S}OGzdPrh-SH@Vy<+#wZ{UGFYLvFriup`a^rJUo+_LiXU|m z0JRzgG|$0(tA!<)!K-6+8CK1)g(sWGE$F;rG?1@Dkjk5$b3mCbHfU3Dm+sxRwF*dK z`(>L`+{y1b(4@NRJxH7h*>VSfK}(zjn$6oabUAQ(^Xvk?U6OmkQU= zlrEwwr$yl5qZln}P^phLHZ9D|UkZs;Z04+LQSa3l^$Ffs%kv#ZNmP4anhLbHg2JBP zyXv>ZOne=G49vy*l%Y;`usa92sIKr%8cnS^8xM9esWuX``~mBRv;j8q?fem-w_6to zq@Lt=0h7CqZ~;sd`~!Rfl#B@N@*6gqRk4~wh1|8wMXwzu$GD0(O-I|xVV*H^cpe34 zjl7Lx_xK{O&B?Zk31be<9ea)L6wl_8&CWuS_i@3#XVwYl6yA(4-{4Gg)tqRRP#M)2 zqew{li&8Is{_z4tl_5Pl*jG_u8gI>hK{A)Oh-`IV+jN;k^Di0D>GS^;fCo0aN&Hw?3=X&;d z1e9u~Ol10pi88Wi?!M1-FX^av4b~DERP{tBlNdQ5KLsEf@>-Xg^tT}d`!Y7}`;$>` zekvuK#aQ$}Q7;y0!?LxPhqE;`YR-yAZj+fvAk-HExDV67mXkd9`}~_Vf<|?eHWeKSxA+(a_Q0n?;0HlRfPV!f zApFaG7Ty8oXjmFVv!WX>emWusm(XNE;8Yv6&0T*NjtQmh&F;~icxwIu%E)l|*?U5S zFXEs`WF1L>%>THr`kg2`6;yqxebk+VYNw2SuysrfZWKGEow8Ke zQhYx{8=IOsNcZRGj=r-8W#QNaZ066HEDuN*D4Ow#8RE@kVFWUz?P&+)&m2#{I;$;^b(VZ?!6_O&Cf7UId@rd*Q{w zi$#3s3H4UMu7ODQA*t5oGD#zM8x?rs{k8xMNMApOU~z5&TpA1K)nj@X+B4?0&EL*X z^2+gl7X}5lWdm>3%i5@|H{$!QGv$`nObxoa8NWGcUf%+?Xr`;7Wi7TrA}kEIUb4R* zlwGS+Uh}O`+xwryN0h3{%_2Ul|q#srG-)_=yQ#)l$^r zod%;R6SNch>&jcc;`|i5D{2@qks{~Zw{yYamWoFq2_H>_mAE}DaixOvd`oWS{M2;9 z#1n)ikdDOYI%b2I<46do<7v)LrvlE`yQsU6e8e-96W+XwZjhy~iH=b7X}nqw*k>rv z1^5E;{XSL6Hp!ogQ>~qt;}Z6a8CxqQTtyL(cK10XYvuTEgN+3FI|0U;i4y4jTx**$ z^n^ic;)uW=+>v3Jc?wf7=j!x8PlspWMxZXxza|mhy3*iC^EqZ~KxF-S|+_0BM z*I*)DnK?y#6|I6P=`tx6uQ!_&D6@WDOl)Fb{nR8v8lN?Az13M{(L^l ze}=s%UabFAnsh{ za}&Yr!L|w@L3es}x1!vQCGfz4QaO=*(Rkc};oLQ$*rx22f45@sX5{)m#vxe{D~vF!BDj`te+fe^oFsTdaK5RF>QKC}9x+()G93t; z{~s&0%=mP+M)6r`s6!y3*KI_lt@sOaOYZr#s+ePhuiZ0v#KEF75FCknGy8Jk!?bIn z#2zfBP7l1~o;Oven=bQn7gh%!kOlwnBk3)F&(*KE6hC6Ml2UozGjMjh@w@Y&r9BWh0Q(sP=A2T71&P zW@@!;sTkFzxK>WADrQi_$$7g58bcz203dUOKV}NbD}N+cpgjTT!vS?ESjh|*1i7a+ zgsnjEZS14WhW?fJ;{_Q+h7S5oQK50`Lm7RM_gt)Z+ur!?VbzsO*KV89p}II=HYf;- zGnb--uyZ14s1gnmdtaaWYI_^C>br)uIyGmIeYe6Ff}Cd3E@d-3H0Esowfih-* z@&xGLL8pu#t1pFk$gHMWbPvm{@llA9Fyci@rdBvBC@9L#t7Kv^r0DHDga{uV;OI%~ zpz=*rtbSFo&#ddy>HroKxzxCl&wc9lW7WXkiJ!>)95(T_4J_T}R{MAfgR?SY-u#3x z=2;%0h19)+ftpT~o@gp;lml<)7kG;%ucF0o#`Z{wVXQ@Xg4if?o;q+l=9xsbDame> zaAq>53~@(d!cE4|nfBlj;m#HbO#V;3@0^K=1cO(DKXwL~aU zQ3+W*iItCP_Xd8a7y#p)GqqZ|foJ=5JOent7zY#lCe_ccM>bwiFA$Nm$j#!auyTL8 zS5rCJ%A_y^MO0kFXiA&&%BE&zDLWDQVeH8*(j3Rdt?0~Pgt(*Y3J5$SZvy2iwWfL+ z)J_=}R69T|EW*SafxYvKIsC8$&Me$>_z?Kfv-MD;UEFg=% z&gy{4x_J%oR{vv$-T@`S#$4WsS-qY_n@+O7%KT)FE?Z7)8n13i6v6jr*%_||iYk^_ zPDRLu{veKcTj;Mve`6&LW?yZ`OoOgV>ysGwI#NFxCz@Kp;bo2l#2r++$f;RFTmQ4vX^CIl3>|l_e zZy_f8f~+Zp`+$}?)q?A0cwY#1T$`d14KP`L>NNN#iC0Mc%C177%l1l~JzvgGZ}@7ALg^e1=wZKS~~SOcE$@RyiZ+_EqI4b)$IF9_^yKecJo?^{6!8elMa zereMrfNVB_zz4!QL0D~D_0q0}mIow>POoWi$^*O~FXe%yym=zzBPZ3<=Qepav*Lz>8GDRzg*|BI&v(5o9)xEqchB}#B#TB#L6UT>?UdK(azLt!M$pY7Mnr=L#a z+e5HS=S`gt!OENgEMf}urjRrNcK%9e%2kbT2nkf@icFWZVtQvA#_3%`C-~+U)>YEHgcL`maWCubx-Zc{|SJo9~0KHJC}wRy~!hq-4T{ zT()9)sr#nK>#m;15bgw{ULSYGKgSJPl=Q?>q&%DlS%gmd9lp2|=?vFEbR2v6xvX@J z?R5fuN7Hfj-KVusO~1lpYK}|SOjeNsaz``x)TNsof;#|xY_e*RDm_V?Inz{$qQ+jE zx?Y+UNQK4mvkd>Lyjpjqi9%LMx;wGXj*hN}R}k;}bNJmKR^<~Y>Vf7A!ux)QzYAAs zU(bUo|J8f%$N?zeg)+MUXum_hif-IG25Gl=x-9?<{^6pn+}+Jg{o~kg2c)!9Dr`1` z`P@(b=X}-yy5YdxKLBBS>k_m;JnkJl>Ik%IwChbsGv*}t-{n|Q2=BZxn!i=!}wuo(F4;W)3T zRAT|dS)Zn#==zE@i$@DqM9<)~g$d--f?kSmn|YE~`KuiHig-80w(%;@a?cmNW9Drc z+|C57J2c~n>5kjW{H&}~W@Z6eW?$pm6J}TgTS49v5v0(##+pb?M|}0MJ6UR{ z+u{z2`P|b)QU{%TpaUPVZj){XgPaN?IiccC@PTa4`UlvAfBeXx@uH=?fm@L_sg|zQ zwcSYO1?YPaCh+I-!>mcAQt8GUSOw1kx&u5h#s)q@^47T_y0vr|Z0xpJA>WHSSK> zfC3F8z`azzW7hn!wK(ittzKgPOs7RDvnX|_w_@yQ>TER82b1dFp9yBTfX}@0Yiz2) zDNkTvM~?C=^QI#>5Hf;*02p(g&UPK)gvf?lmv|8iFu%}`_}#JF*BhYT?dS%G!rlT^ za4cTDRbn@Q5Z3Rp^(wNuj$3&sH0qM~u(tbDXxpe}s;8)iTUt!@4JuK__|=W?a`Jh0 zCKNMl)M)}a9sw8lPEP=^a;7;GX-bu?SF0s170oFC6YfhnbAg(97Q}SmXkvNyP1Cy@*!`t554XQ2y4Gp3O z=;cA%)s4@YZU6WYp9K-l_Q34Q2HYiF+eGq-Yi{1d{F*2GYIEgx$rL33ETSfddQaFq zab~SyLI@kc<#d23Vo|p$=ChBJ6D8fLvQV?XymN+D)Q~$cd~mxG5|-Qi0dxA5_7ypr z`rLfsttvK$(EFU^aaL*e@p{Elyh~mcy*p;-)+%E)UukH47 zp5Lf?@Jzjl(^D{gmw@l{Sm|9kK95h>*?hg&A{B+;+6a-Jw6`^!zOBS=k~MoQMcxe4 z#s=b-H4{l#$vidxcjK`{j>k&a)INB42PQG$eK^-Ad-SnMDEyVC*I&Afk~FErhJ%l{ z64gHYhgEmut%LfT}Mi<3RGR; zXq>icm?)m1^RJ-LZ?l?R5vN!7>04S}ow)?|#D@1c4FJL$KvpFDf8H&o4qg$-`KA_& z(RQ9X0%x^`MewVO52Z1KRA2b(81n?hppfI1^ykdDlEIIN8(jzC{uxm$v|`@jYbjY) zEf~0Rf8gD$37g{6MT3`;N1F}t2PSQNv$ER8)TMS2&a1%j1vhq^B{GmNe4G*(1IYFe zZK37NV)!efp|Pb<4P-79INr~*d?Y(1f?Gbutx-k)qTP_u2QvqHqiAdDUF&9Fz3E=8 zqA@J*Io!fn`f}1hS~_-L&$19aZ?}e9;pj6G-8fojP9^S@Y`Q$HU87ShrzmL#LnTY6 zJuMB|7B>%bR}VAm435N){gmpP=6wU>B`Uq6FkP z+bcs8V72F$GF$h4+;Pu()PtYQZa_%wrgEc4-v=C*=4NgskDqJ0pDqJOZgB@#WX1eg zZef!3)A`aYHn2@wtU7BOpHWNOQN>qN7p#={aH3 zDEgyNbh&ilW>v-$pMZAKBH)fyS(2Sp-G+td^_T;fN~D`Z433uidYiot=TbJOb{1KT z1>xhYy-CIkdXWWdKi86q%|X(z`G@rFX36vL4l(=S%X`<&%YXB_x)RBXKmVGFXzPej z7SH>wVxAT4B$>BdcMJ|3-I`y!;fMiz+@2BWh1p-}l}nV)eagtoYR_DhHp0C-(a8W_ zc;c&=bmfhF+oH7~EQummY%-<4a7XG(iNHR*qb0Q_ByRpjdT(ff)H@m33|)BK6~kbp zA8Z2Y?W=&&aVepajo`qGk*MRJY<#7GL6(%AQ)1uJ-u5^l>%O7naED3Vn>|_KDh?~+ zu#<7$3-rG?u%y3=Y9wTJ*XD|Gv+o~Qo>bx!uVCRcY~l=Ar6cd$;y!lh*v1tFc1?^2 zDnE8f{quS6tM?5ZJ#9f>XI38MbT`hYbxD6sEl*&#&*h&zF0`ft*|fzUeY~ZBdJJrf zPa5ks?VrCt`*S@X+4P4B$Y~dgD*vi$%%*xfYcFmyk~CHvs5OF5xVuWF0M`_4g88T& z2L+huT)wpl2!Iihsf1S>J~+%!oz1 zwRg95T)zl@q2I;$RoNA#UWWXa7g1TYSuP;Od~<)Ts7|@UTZ?-KyjP*I1~?iCt9((w zu8A!Wr=`An6Jvb_u7a3UYp%M(2Fm9*dW-{t%R@j(UoGnIE&pG(zLeMS$0#I5z2D#T zulH=9;f{P&W6P@xzrq)YS~K+Y(O3Ii;u+pm8l2OXV;|2!NhRZvMpj4E6EUPO;qBBh-KRn zDy3N~nOY075!4`1E!~0~ueIoll5sfxI5sOtqrnPvSet}zgO~k}GzL_?Z z;`9Z2N5_e=SAK_lGt#}UsZ2;BN_U@~w7lWkICY$UMSP`1yZ$n?GbPU0 zRpj&nA{T_wj7w!QP7^t8Z4+X2Nw3K0)VA{1-$LG-xwbjGT1-FS#ift9ZZ6vPSDxc zo?%&+@oVq9B@hwfm%O6V;45JZzVP0|HgRjgA32et8a3+I9zIANBzKQ?^mYErf*b<9 z$N`@|^}oBG{dl|vBANK}crwF}I3e@yq+~1dBTE2crLcl5LM}}i{o2k~2NvsyE}w^- z4P=2pU+=f`c3#7|z_>3i$i}_G!0bj^3Y~K^MxUk4C0QrGv#hNWzQaMqxr4iU13>SS z%1WkRs*@iv0qe7oQ$X1}_?oMrEqde{Xod|e9h6P*p1r;acuW6B5F(|&ZGUO^&X47W zep7q7Mb00C1NV_<-+%4az&n|>vLajof&i-4E7*BN(MJ{Yf@vd_LPr(;^}fil3is2m zGOp!-Twr^YE@L50YFDH3lX-mIek%`}VnZ0)P^h>tZ_4Pk&ur^CRlrkC#b3)gam*I__+>EsU2MJk$A7JJE9~FrFsD zL6-vA^k2F>8^q|$>HJ<9hl)U@X5c`&3?-W2Y^{mlN0NVulZ-Yv3mUl#R?qJR;f>Ad zk`& zVsu3e;hx6s735fdXmr1~=OVkvUR4YTG60NHe_sT)i;yZVqm#k@9ZY|hsR|`<9x-kx z8f^hqRHF6jghed$rLr9)$sDi*3-+NP49@Cg1p*f$)un_CziJ{K>DX;$S#9pr)WF1E zEy`M`axocs*QkJeV_!mj93%B(|F8$91|vMniwb*v0$XfSXZ5bEu#d!N=kAAbXhBx! zsl!l)QwQ)BOCY_1UTb4_0MqR7uQ}ZKFW&hasb|qIj_+A4YN{I)mk37}g!Jem778B* zO`GAPQn0|H|9HvkYoLd&ZHkInLVnishA_5b6jzj>u)aDqZ`Y7z_+%G;91*cp#h6t>mUCMDu0-5n{|J3A063--G2TGoA1Hg_!Y_`-Hl103!v`9 z@d=&f^C+{|gML121{}Z3)ijuDa3u~=MRg#k_kPvT+}@Tq4qwcytWPKmr%SIQoi!&! zT`n1PH8}dQ_t7WLKbVyV#`+HzaCBJR!Q43~c3T5smV3!&>p^XaR8oV>^~{uuI%+%W zWt7+&X4&v*sYKZVvpD4_!?<9JLDvawsqJ%X&T;H>_utR3J%>eCSmr{G@mBpY@nZ7; zpstoJ7-R)$&z4?~!+B2udvDEP+qTuY#EssgaQ^E3Rx2@RGm@Dpi)_krksizS{p?e6 zhKUtLE{%_x=noa;N~gV@M{PA$Fg;(yN05p!x~u%{TW*cBu##|`8}O>2J5H{w2A|79 zcuqIr=lQ-9O*&2PbGfI`?q%escx?h9@ z@h@Ve-qCTgh3t-E>86RN^MNy)jEAD2tUJ#yZtj=7j zI_~E1fb6|+_B+=6!aJI|ZUsxq32>VgRQhMHw79Hj)l9PmO-ENYO1CT6#98R*?!`B> ziN9(WJ0?U(qP`Me7lo`^r23p)6sh0!(uVL4I%`=%Ybt%ZGEAX#0)_O+Li>^A@0&I) zfEEl>XiZMOLya;%ch42JZf8>4W*MC^%?9k$sGigd{_=USuJLc*d*x((Y!Z$e?fhL* zieyC}Sl!iR7X<@T_LY-~jvWo;cNzQWBArB4bsPz-y7BO0T+yo z`F^|&FtI1mHe~c1 zd_h_qZM+*kZ~tzpDP1bk5gjk1fl7>wfL|e(940MCrlu{LAuEpA%HHKHvZJ+>|;9x0)nFLW{r7}~yBiqi`BIh7`%ZyCoH(=BKTkAo8Q+ZLa| z%E!1$aNHuB5p9pHz59-p&=T^`S_LrjJWRlC(e7rQy4?1O58LT*1yeiQTQ0Gm{5VTl zGQkZ@aL-+Nr<9?Cu-gO-%HXmcvBy=uAK*i0*m{3EQ9^f62i6pjGn<+LsP+hWeHiWp z6)qd)Y8a+`4-{CTl8j}i$h*^!-$W<9h8 z`9(Usdx5AuJhm#Nq5+EFk$u3&Awg~GVeXCUPq@oPTDB*K2+x*o(P&e;GCQ0=!33T1 zH7;=K(UUEDv}&;K>^JS%;=X%xa`m^Nr;GI*S1pSX%V zJm0I$0j?00q1c=CH=NR0Rd-x@O8z;j#r=lF^WOLG-Q)2pV>92(gi&Vf$>`09&?6;H zSHERdUZxTalj^+5tMQwq_xKMf37ROFI$K@rx;m*CldO=}lNB!I)UuHKGo$PtsAAfz zIccC+p=WM|E|FeHwHD)+j(&3OGr8T^@O;8Wp62o=2oC{k^7waq#(pU11;2qs0I1vl zX%RkA!5y;VbIn*)YRWDd<^b~T;#HXtu5+KeVOQtZ+N$w!-Ni&^gT5!OP{5oz%Txqr z8#8Ms^Nrb~N=)$t&WJYtv#?Lo8Wy3UXyz}^9H?cbx6c2@CEX!=BL$JZ$ki1YT*9al zTR-qNpEk8(FUA68EHOdI9TD!rw-{{~TgkU?9wmmZMB8<}Jztfq$wXDHUgk*nNPEh<6dDsf)z6@w< z7{)p)JRr!K8*pWt@~O07&+3xyVxC`!8V&?ER*lL+JlJiBx*K&+h+-bKEIjGmgh8J< zQ$ihz6!cPNl@5rlc{}>u^4NPvaB1 z^0(ZjHDnZD&>A*%v?X-#E>WG`2Qp>UQ|5evN9ri$#%jjW4#pixrmWX6MgwRC>^R## zT#iRpA*I-IVWME^F#u4Yfr`@KnkHuSu05?w3nCm3sCy%8+-%`&h3>jaWMD?@$+)CY=ElJn&n7vXiJ*L7w>_MxD4iP}?VFBLH zV=+eq1>V!p@OgnI&KfrUwa0Hwz0?y(fS)t=Oc=8Z3VL4Nzf9iUaJpH`Vm)8_M}y3p z5M!{;&_ZPC503R%Ni)v3cJO_SKKC}9Lbg0JE~)Ltcc9{oFOMC3nW_ z1!6Gb@w^7uVRY?bc|)zU4F5ewXUFA(TLTd80pMFQ!^2Ezw2w+Db? zMDACJ)NT|V4r;xyc+Ha39>&Fgu5&Y4EDgsZfPyUnqd7Ua8H|s7sn*I|`)s))7@tMg z=Y*`U+ao%J|7{*U6p-&tEvwzzrb4xIB1VT4S5GULuAZ#r9FVf@r8ZT7&48LYElPAs2! z$a6SV{8NVrsWCv%LbvTiZxQI?9wvjUK&{4G9{Mm32U6$-pgo5|$GeZ&rekz>xB$}u z(dMQNqHH%@7W?h?y*G9o=#5E7C6b>i_Y$~^TzL3>MId#D{3Ia7VEP8@s3geUwn1bt zxPk~`Y5(TMeN5W=Wd;ZYFOlT!GR_W3SP&XbVIG0Oy@ylL?Oe;4vZDa&+ckB}zFa5`{0B|m#oIH=GisccHpb0A|ef&?V{!jL$Q^^;& z_`15Fs)f>Ux~#LlD_`D>c<;gDpVM5FPvdO>k$hH0 ze+(tltS4F>PHz}!d@QJ(pGI@pIt12o(@yb?Q~58!03f_JvZUWP|CmWY{BM{c+Idfy zM)()=aSwiO{-g&^Sa4;qIBi)5w^O1R{gO|)p7ot3ql5BvU__ek6Mnk*Q09w0NDDkc zHyV!EF$3YJC1uPeslE=yN7S!FBookLJzgp)d4B1m*^AlV=N6gZ;z?IV9T*!E+`RXn zlMuYVb!7V{pqLiI&i+%F@E}S%j-=m!a)9Ot#k%F`d3!B*HzklCTwpO6qb{2~WXZXN z)~G2{@Z|^IVdAITi-u&RC;K-$50TUgQ$^wI@E~H!{8Du*v4MV5L+5ymFc$XEr8xLUVi zOGPd5Rs7^G|6873#zLpNYb{iLP5oE7-*di4YlEHDa0^p8(Un4ZI2PDH7caK z`e`PmtjOq86Zj29+tt=jF(h~3)q$%)0>r;vEhJUp-6Z*#4@3-l5^jS=&jnS8qJh+b zHSEW>cXWnf$~@bozm^p|!)bD{G`_z|JE6q3UwlgQ;Cpefo*of2SChLVu42Buit*5F zff-3JU!2%}YLA30wshONr{sMF2#9W)LZ%AH!68d^=Wor-Py2-z2;o7BQ8D3jnVCdw zQfZjv4CQT@!(j6^jeN5gx=H2Xg73*1+OWvIKV3IncGl)tMA+23PPP5k*m|N&K{6$gi6Up!f?71N}uoloZ;|&LZ(EE8z)_Q5# z20#lP&QEs{X4F3r{U5t97i+s>RDok^8F0N_jH+RLpKh9X-EOK4GY>NijISha(+o6A zFc@;pWr-nCMcN$Xv4mwBska}A_<&Fq+=C@(PqsX=5FXgf)d23gdSx3W3h5Gr%&!vagXiMMbtsI&T} zGvEI4|DpHE0z#5a`{%|}u-n2mZ#wf*Bsn{x`YgCNaQ#kEaCCm!bQNlr0Fg@dkHA|( zzW^gD4j2SZRWQf<+U^uWGGD}6Jj#$;5Hf$X&47MIJj`_SrqjhW<6FAO2i0a$AcYd| zF9MMSkT3%J7zt^pdXMtEwEpv}cE)XY4`9^dn?C(wdzPn&S?k>Svj|& zWS`|paxCq2=VI>*YaW^E*^3aJNRw{BHeKXC3y#J%n!#^!e?j&#=n~wAO)Tf&e|t74 zAge`tcn&WD!w_qy0bM38gj~ZX;*L3j9qlw_dg4!as6V7f0H6IpYo`?U${G6M)$D3_+8x?*@2hH(B zuEnTvh_8z(n~;d_Bm9E@5boRrS}&Jiz;uJg_-Y^#7c&H1mi{@nFqVxdL4tw2NmivK zZ?UAM6F*R6o`7{wUcTIC4sc=8{o!ctomV+l7S$y#}b~Y$=33hQT zFs#NAaU1J-{1GOJGC0iOTdil13ye%9C~b`D@#_>qvQ_z$0V5)}f$`-}4(nSNBk;c1 zW+0d2ohC?R(g6d84)7o95S;_0RgLx@+E4UQL)1v8ovu0p5l zFAL+l<|v&Q&ylMChq3pLYx;WMhqbo)QLD64L_vsEmLj4|5h1n$5fCVqp&-P`hEzdj zNUVyAEF}yHLX?OsAu3B&Qb9pL#IPbO0Rn^_!pcsb^N!l~JASX{TmI=QX1vci_qgus z9_MQ3`-rDmCao8}Cz^rEzVrQd(^k>B*sUDvpaP^AWxr#^KvY|A6~ zb(l^GaSndh`=?v&f~dRNUNrfMoK0t+L`?0m8*U!p+<4>+`ibW?-+MIXSx29ye~P>@ zNw_ksE-*GGB!}H*Hbn=aQ^KW&JEZ>3yC0?b`q&?kSOo2D_3-w}WG0}};c?-W!`qf|{XHQtH0mKq*n z*KxP5?=~7NPx~8b=M^|v2h$_)i7JK?R7+}S_(@9m3ymLPz2slNYujwL*}qL1BTedopt5Oh8COEmJ%+@q0dBIMB}H=ndK;DMq&M1clx zkHyAz4zovD>im<`eTPUm;c)p|skxEf>pU9TMmoEO-FdRt3j5)C@p{z;$+s#^N6-B$ z1y&lnRTX44ApwCfQ2RLN#sN^3)z1Oe6`>BE8tv!?845gbdq2T4ox0-RTzjzJTQ z#Gll((|FQR?|^@@2kFTqnJpZ*TXN(wcLg`eZQ;M=mBtlths-No9QNJUymZ5;y4!?njTU<)%u9oOhvDoRUJJ20LfmdZF;2ZoSEbYc~R70HxKvMj=z)S zpkA9(~z`A@*rh~{4T-d#jQW=`X5mw6KVVjzMzNYn( zQ7>Hl{>BOGznB`qt#wV`rt=}l3kAA^5XaMCbpHn%s()Q*C|+i@w4!bsF&U3=ESV>pw7vU&0cvZlD4e09w_KhgS| z_4lfn>v}n9zUQv%_#yTC;|&r(AaXrh?=@2URnI|8>eNa-8yhq}#Y!74o{yCjd}`snOf;NSiKEwvUXIIQKY}GUZpS~r zwFxX)HOhx9^~Ajc4(&R)>>_{R7Y3Thx!+&cJllzoAGI}^Inb8o?&le4jpI}D@Gr~f zgFYRw`(txn*3;$zEkSQOwBTU}c|PmJ$wfdhv_^wz0p}{`ac!E7(w2E!YsZHH*PGh+ z$&Z!8lmV?fyhES%31!wp_3HJ%PkwVpW<&XzZSTa+cq9;#LA`JR@T~GC01#8?8(-!cd}9t;zea zdA07k)2n=5&y49GXUH{;${ic`f4o`lpU3fC8FbZ0Woz8; za_1TP8vu{fdwyerbg>=opR@Smkat_h(p0XA=#RFFe!o!q>CT*(aX!nC4k{v^X@DCk zn{r8~96@$SELx`FLpbFdO7fC&Dd?)k%KX#%31MUAMw0sozo1%2{iq5n^Vyd9btb`0 z4wpB;FQq&*en33Yo5s6o@|KrZX8ho_@7>2S@CIIwxc3c*oBT3D#6wz1E0|bLP39$y zmK~xR7k}n7vuQX4S2Ar1i!Iz)aM zgT@C56;L79x{`h$c(&{>(COX|P?Pii0KJWG)^B>EdhXilUt-;OKQ^+-g8V1VY}+s+ zSQyTAx1fuR@ri#9rOarNoVA>1F?prhz`g z*H;eSHNRU;KvxaA-WvJt;`y7SVRY)ZnIo!Z2ItK2)ecN%L+fSy=x@=Y%LxwbEL6DQ zGSh%Q9BDc;SZ7>su2IWnBX&IIkILJHm=lH@4A;S)t*_`F4yY?1uit1$zm(TeZ^{_L_K zpBodBFof8xboLRU*0Z+v<&9iFWbn)NjC4$^nsjN~+4}x5VKKOHK!DmXu;ouS-=cL{ z8tHO}Q7LEi-G|J?(p}+$7L0zn&$sD@Z*C&)+7GUS`4l<&$WBgh6na9`19qWtz(A3s@F-PqK~9ypDQ+z9str3W4cuY3BNiX1#%#lX2| z64=IA$!jp)cU&m}wd@(?d!4kfC(mA7?6mAUo=6xOtsB0fL1mqC$CZEhmi5DYz5*1?||ozCl~eSVvEu`Sf+~0b>n;Hh@?l&L;iyY!kEu z6fJ|s{27vt&XEz!m&tB9ps`VxLY&T`Dy*h&vw{_D(JHpksWl$tX1)c3#fTj5#i#2l zzNgO$C=T?KK+u#L**emO@X}h1ztV-ze9vCzN;UDL{nOJ@+i%$0C{5YmoDftl=Yn`r z3Fgs5Yhi3;^w*u3SK+LM^S$r1|6B_$_I=4#CuD|gpjg}k#AQd~JZzAuA)v2pRzGyx z^@PR;d=k9p+o-A4Yk;oQS2=XPdf`RgWw$F%(Z8>}lUEcv;CQm?Wn%U{2r}0J7|gsl1{;47EsCHxOfx%#hcbp; zBOF*H!TTrv`)q#7w9r9&%a5)90q$~xwESYauH0d%5L|p|Z`hpv3XE9;w|d^jYhCzG zbA;QiPphXH(Kl-%9oTyI2Cv1q3H_ft@0i(K$S`m2)6o?3q}^~c)pT;7TqJlwWM&gXjj z8s58kN!ii8^*XgE-xStX!AkeH1mi=)-?qKHghwC3{4h=!1zlCn;37_pD*T+p9nQ)a z3Qm%=Y1XxF0M`|_y{1?`@s}_Y;P*fE@hVlYw{Y!6^)+9%5ad${`vLR6h(zpSe! z3`(#A{=gj22BlS-!R!L|muMLD&hSDwrfG89DaWRFA4xJYxA)>XGl985I`=hh_yafw z)`=98_XdF64g$BLSdeVG>XQr|)22N_=gYYnPMFs}cY+%)E*xl3!xkr9k1AasM8xW) zgxcxR>NQ6wJLjLTFQG_#j%uVmXahCOTlaU>&R=iLWSGu+$?6k|76f2XYSBH49A1K{CQrWjnIq+7VX)0aa zt2#yw=G#o@w-^8=2$0|%^#y_lhzQVo1P!6^kR`l8RTLpQMYtm1#Rs%=gVq+2y47@wq zi>`B9eE<8eBPxJ628B6!bEcjBysYMFK>UizL3w*R;NJ2y1nBFe<&vrZ9rV&>vHK$J z+RqvU#1pPYebcy|q;IL>Q`vkBt02$79aUW5=FC*7-XYw=Z{5d9l%$+Apm`Q$9CpSX z5C4`I&F!|}j^gkk<@3r$KDEJ&bL#+|NpH5_#VBP3EX5y zPg|MzLnAQKLC`9f0nDOdOi0Ck&u5c;&ZT2%N)dAE^o?P2&j5&SgGnB7q*I`BJ>p5D zJ5$W1#ValMV^fbqDauz%dD(>ksT>lmS?kkN+?fLACcXf)%T4`w)F7cP=@v8Z8R4qm z-O2NQ+pzVI)A_G22HA?YUpu=Io5Oj*;j4j`@>MUv$mp+63CctG;EX;iD%fU_xC2K) zSPzV*2k4L5!2WNm+W)COiXe%PFu%U;y))=80uuanSrTj2Gd*#QQ3r0^Leyk_X|95@7F4IXzqgc0aY4;6)X*ssO(2%jx-g=4`z}@e8 z_&4GsS;h`0i}8(352Zqep}CvT9p$^%>0`LA==D1M^NK(aT3Ralx|E1!;i+dq@+y7H z+eg3P!B8%Ln%`#l8D=p3GNP@_7Y}3wQgzS_O(@}$4rJNcFIg8xqI}D zfD&16Dh^3-fcPZ^ZG$=B)Irq2@!jj%v(J6SePo@V(J*nq8(irYHkjM{?BUySMor~Y z0Q}=XshS4R5p&ktMv5{|6umD}8am1#y{yW;U^nmn@vVlu|4XAk>v<~MA&MTH;{R?; zfF=UARZCe0SRJB2K(-|&8er2)IMA#$-wIPphnrYj5t*=Tl`4+7{azHRVx1*eR-Zkb ztqvNye_!u$02r%Pt1g3r%jzmGmyGxWv~7TU*yXs9?GX7)znL|EYdyIl$(o)A$`-MK zk?u3?ZLy84&K`>hY^w$cj&xtdaxU>#_8VM2VjR7L_wz9Wz$gPQAt6Z_wnH7m`vW?h zvQ7hYyUHL?BnA(DI-U=hr#6D*Nd(u!?g{`eL@WIJ@_8htV11Bp>R$Yii^VS+*@Rz7 zG4Td5+;m`Hz-ga^uZzm;1I4n^>;s(_xw?r;K2u6CWvLO9VwO^JiT@vw!_DuF=x-Or zwFmyYnE&H;0Ko!`@j0N|P(dINE?!srFAzstPX>9Jm)2eNQBpdmPg%Wusw*``?c*zF z<@KOXYqaEYr#fqpitH$NfHxPj6HhaXZ@@nCZK~C+%ru>uXjE1?B7M zw@DW~^_tne9bdLhx6QYa6VrGPuqZko`u&gCcPl5CP3r(w3(40L+$O z`e2F&wwt~&X$QZb?NU(e?vKU;l=7~3g&_Z-W(E+M=7PFPDSfa#t7yG@rTyXr5kDB( zd9fshdzp8meM|VQO`R5??31y*K5QRK@9#ZqH8N&D65`i3vIeLp^n1Ywdol-g$?(K6 z{eg=kd-X*8;QQ7nrpyIvftU&U18Xds2MzBKc!A8^=TodvFyi-hu$puCP85DZ;F z#R7&hd1K|ggRZA$)kuM&TKQBD*h4@r9!&l-*!B3t>yz!ySeQoD631JLE6P^Uu$C0w zrU)E6hDTb}4Eyi{Gq!-V(XSAPKaPXC?adnm*!lh*nlD#O~hSp_=x1%JL zv)5Jpp^(vm?LO@OQ@R*jej_C(OFiOTvG^h;ONZ%wW`1Uo^GWJU*H*_Wwp73Xfm0#Y zV)|cw$U*r}4lage46%~9l8`sRJN(tC9R^ho$&zJDpKHlwq7Q^01;)Ywld1@bKWKjh zaV{V*K?FPxFBv!nW==t)2F65j>aQs~>9g5%=3#Iy-Zs#%hlrW}OUd480Uv)O<~?i- z?6bKZMW#}V)tix5xvwUZT~zGve~avY{+61Y$&ehES@L`T(LsY^@ZBDO#4ztGWJGLU znTh3XD{jxw+Fjs9iQ(EmQCm~%zqwMA-&tSddg6~-v6iZFt_MU4jg9YRhAl@|ng1Rg zgk|sCfXcZ2J$0tKsIh-`yFSKt;cV+vH@ zJ_CB*878!gBc+4b*4?#dq)sQ;FVWiQIG)$PL56l|Wg^&aoUr5=&foaVe zSI+G7&uzYaKM;g;NrCidgO`(skoPpeHml;Re*4um8>J)K&DfX5)8AsgH97%rsE(cM zv+KZi7=u!4fR`=49CLDh4B@N>zSU0qWPjH|oi-D%{L}t9`_e)1(C3x&)-y2sAf~D2 zg|#Hbveqq}z0>^QY=UlOc2)a6y-WVudTq*{sH@RSyxXuIi-Tb$PeF!o^*W#0V~i`d z31Nb^On=X4XL;2)OefK@GcW0>y$WQ}IUfcQHy9{5;G5X~a~`2j-NRuoC?_=&l1eCp zp?DCey{i$x8K2Gv4U1>~W1heyo)cF7hy8A3feX4EE+4@x0Caon5`O*MYHVa1W!X|{ zr8sEZNHJThENwbbi@Ena{sG6>wiUh-W)l$G~-od{5l(7tbQ=Oo-W?MYMV zF?GmwcA#c{YCaj)V=Y6$V2nV!4(h(B`n_@lh4l7*Y=;fKUZ~6`_>Cg)KC0#6tOU760LlR>6lSFJb5p`fbM- zZ&{aJjg7o{rWpHE(fih?k9)&OfAMg(Fo2`JFW?QYOAvXaLcy*wYr^H($9UD^2uVbn ze!Gunl-**tD_+a292e}Ucj<0pO%$O&xZRhIMV5xYXk?iLKW%Kv<*LDK;R^Ijb{QCq z(}x0`5Pv+@FhLd#MGEkUVfG!V-!;k#O!^VREc>QTVmCJtY&nY~YWi>AOtaJBr$@1| zdYs;DA2jB_X+hE|0{s&I2(EHof)tzl5TiiQ9CZW1b=ug(Z z1bz-+GADq<5@amzpXb3;N~khm1O$@shLoFUywFa#uJq-&N$>jB1^?WG8baZ@zL%-8 zxjMi9t|50KQ&U? zk=*+!p#&JMs%R9N%%hA&3>C#BGuvnPuea|NjCQJpVFkHr{LoEq&)quMxPs*1pc?lp zOR*|ySEr5db-ge^VXr?4CS|HuCMfA{KNObxrm4ZfT#C~rpLjXh`CXtI*#W&z42`Gp zT~5cTUsc|b>-*`qj=A^ylT~jZ(|D$~kj_JMm1H}-R=&+wn-vi#5*)3!L9N>s)XG53Ka7icPM?O6za8KL`-sYpfA(*t^ zdKq#twED1V=J4wHYRiiROL3- z@W$J~08`%|Mw|GTy_n)%rCral*XoTvJ@kvrl(QReB*-nim~%L&jBTcg&W48pfgSPz zbHrrh5C9Ma4Mp30h8;lx_eOBXXb3bhhZv*l1(chk*Tmy3#=21`_|qDKhv42f|0(?Z zL(>dj4cDl%jSJ4nb6oR%zSy3sw613!HRDemG`dg&79*YCF3!{9prwPG3Ju>k zsva5Q`Yk9WuXqzQw;vcYwaziDl_cEXf+R396ADjK$fIF>5$-lcIQz5#{c}$*e!T9S zYztG~w|=hO?do(--;z&(b6$zsAv>D;T*~(%OqCkQwnd3LOD6VMFIoHvTVFoigS#p9 zFYddlrg@cOvGC3~P7Mt6L!NIK*HIrf%a}kvbIwZ~71hA*eTzkabwkk?l($E`;55Eg z=C`+PX~>QxUY$0$vCe<(rVe11y~b=dU2hU8Kh5T}N5CHA#cO_A!w&J@QUM2qu+~LAN*pJ`Qyvel-{qn#@2UyX@Zh z7JZr;Arrg6xU^-NZN5Pqfa?#G`gO034I4;`t~qg~ou{k5tYnxLptm71uVf1Wu!CB2 z{EziD8ck)mGe-05u_v2_bze2dUp7wo&$H)a9>PjNO_$I{he}KvfG*;rI>#svR0JXy|msquyU$7kl4srVQ)R;7=#h-whlKVDX*ld zt<$MBbN%T5sXw`TlXAD1t2*z!=EJMP^ov6SsmY_ZwF?>PYQw}jcgpHyP`F%2h*q~Q z`w8n;-z4I4L}F)2<~PQPyrZ%>0dy<=U5W{c$^Le#P$j)=MbJU}(%&L4*4I5YPD;;c zZ}WJXz1{&^$l0@)hYbTJm=G3)weIaHZQWF9eSPrW zT-x-o=GQCT0OOghm0|@B0xQtAD`|$&j_?&CZ^?}JMz%3fp3=j(*f1eIAL9A^MuX-~GJ_fnR@ zP!tjD#pNMm>+5^;2kYSn*^1VA(?QAK<}HO4w?`3<=LArRrRg?(S-qAErtEaHsCy+_ z$53s{W=iu+RT5eo|o-4BvqYT!0g=dI<-A8lH{U3b#$=@+}na0 zu1j6H`XBpP{HkYKz@lA>L1ALr z5vMOSBoH;UML#xK9C5b=vsWUFX5rf+wZlf^m5_EXFYy<%WTM9h3w0@-o$$I4IaN=N=Iv+88)All^!P7g1pc88g-Jjq8SqcSMI2j=OIsR>LkL~7#IsT){uCak}rU`Z_~ zieRm>Wp3q}OS4yVdfbMC^W>rrwg@qkv&Ib8wTZh)R+ius4WCX^Rr9jsQz<1x6j@)k zrEy=kbosA;w)FMaEseZq%+0Vq)}e~DoGzJ<@J0p`?Ro;LpK#p+kA{qRBw5j-^$n|i z2WBSvNE((b3NN&5@LZF{nD(s5V>noIa4|duX$c-;Fqkp%&>I z+cU0b(62v#$+M`W+&+c$7g93N)9+Q2zqDc&wS0H~julLZIDgV|l4DDgR(TAQ1OaEt z1|K6VbEiWI5-~Bly-KTkrjC>&+MQan(8d?av^e zFchGezs@CGyHw9go9no7qI$OLPJ3NohG7R^@>H= zhpq02LGAq83X(ouO-s`q5oo7KAv!^DwQ}HaA7?_IXtpj(e$2h3isR)tIUBV+sjL#( zrsJ5)s_3L@T8xqW>jEp1D&V3|KBUg}xDx*P^Ib0$%%#V(IB(({`HY<(y~iZfr%^NV zvymf@e7Lyw_=*APke2RHZY8^>rH~pMX12vS(gPBrY@m5ykZ14yqyC>IYt^cMts^SN zA!(kP?~`gsORRP+MkHlBvN76-<%)V~xB{0=L@NkP$k)!Nw%cA7ur#DEyh%Rozp$)m z82&M*$*N6}Gkh&%47nq!{KL4rr7PyJk>BJ7WC#;T%wNLnJ1q=;2TCbr|BxM5kV?Fv zTVqr$>G!=4WXC7z>kdCEKlNGMSAmURwdzp+cRo6c=VRKlg!S&!!|p*U%aaUvi*1N! z{$k5RX*9i8u0}BK(|;`K?o@D0yT{^~91zZImEwvIs2p>#OJd~Glyi#abh2xuK%i{3 z4vFyy8KmJz{(`+Ntq8_+{2~!kwCN7!%xKh;gA^XCwiwBu40F`cM*Dihg^^{P{ZSTT z7k9I%?mP!4LEZK2x+nx+u;?H7G9qLK#!k2upZ%kul=o$4{b~2&9U8lbgcl=6aNru2 zzeHx>`L}$bozwvHxh}cYW3-{Z1iQ|2CocF_3{0L0`OO zENT|BkU{-Uw@&5?`c4n~8~G3j9E|Js18;WYbN z(>vR(vomb@8AhP`Q<>zcumcnyU3xYT*|$@}{^_o~lgYL9U`$X5_{2v{ zd+2Jn#tpjQt=AODp)wUZO#l4AkOzBSPuowL*VK%Nq3q{OLicTmvuD34i&yZ3-m3jy z7W1OM6+AwW4iC@abs7;du;l;qZ_Eit->8x1RhVKh6#bDt;s5dz4#yWZV1tvT(@Pi6 zFGR<#Z!WtSI@0+cuzzCU6aRl7u{+5YeBOWI>Xoo7_E!R8OOxRmY>iE^?P}o6mLYSf z=MoNWJvbVf7yHQh0QSo3{4q!RNbZXA*F!_odlomq)vETHyxfqdjKoKldVLy_dqg&U)HxHOXWWc_GAE5AADF}TO^W!tw+qP*>4U|;_;`K z1}=7^I=vKEuTsfWz)0}-A7+m#NJOJ?Z&*-R~|(&Q%G1ss}@iJOdV*92byRHzU_jB8c_xbd$ozqaxY zppSK{rv3zbDvx_@?b(rJt!i&>*Qmx!CM8qUyw&)gni?bx^9=AU?-`{Y|K&&j+1WA# zaPz?L%K;hoKYmN|MsjT-EbC#a$ee%d%DF4QUbz@slUxw%00r2K+yKS@^22}If>pEk z|6ZqmMr!T-hiN-b$1sx-INvYY5?rwnlC>`@VYvOXqu>a?Zbrn;aeRF80kjcr0VqMxM0?qVO@+E=W|0P zG|`5Ev*QjJL!Mw4v|FELA1Lsn3(vnw3DApGz_>zS>C1+K7XXmDmiYSXQGEg9|I070 z{kjKVffnb)iN5huINYs^60+a__{T4<0)ot+!Sl7m(e6>+DDxs+#XsbV<3hL|7FqN4 zNWP$piQ~@zxCYO5;In2y$f)VSkzRwzj2#c+Pq=YU@%JuyYeXlnZobegid+Z1d(|UV zAjO|iRMXinQ4&yzFN>Y^ATNmQt@(54aJ{_+;!`J+7jX+0inEUp%EZFox;IECki=HH zaBrXVIs-8s@ZxG$4T_R4#rs=kvZKz*0I_S*!ynpad(ho124W36YFFP%7{aQkWHA$j z3g(W|Zd$w%{c8KfBL-4&>^tP{akn+4o7tMV!4~EXYl!a@2i8ZIHI9ZpW5C!6d_fJAgE(Ld;=-* z*Y#v6nEx$gUtb$kY^GYXpP=DpyZCy(y^f6ONC#wAWW<`yA1(K78xGHCckrHC(xOF^IGw zo69Z5fsk`I8(*9Lo)q>7uVBum+DV*2)YjHFKD0Y$oMev`Fm)~bQ5Bb*CNB$~)n0M( z&0`NZ=)@`=h#UUf1nubm2Per zrqPvB;gx6|ao}c?U3=?j*Ora$ESMfB7?xVdoW&QJQi%n+3EA&)2v*6k|3rABW<(%6 z%b?CFV))a=6TeITa}sBZ(`dcYynE(BZB|!9QHfU!(z`V&2>|a z`K}Cd9Ue1%HuHvD(n8Nbc>3j1B9k4i)nXexDJ>?j~MHE3ryfUIqTqt_O zx-izCLZ2?XO-N`#LXR0QnQxxRUzo1mvzH-mb>$5d$cfdbC;}xAriC3^;7te|J99ev z3DS-lYPEZyd@3Ci7cCcG!V3?%7_;u$htED9;Ge^)z+6FL<9pq|kJd3dZAaRYO-uR` zGZ~ePCS7i}wB}3buNpKOl6$SqYUw~z<>%0c@SPeGY%A>w!zXVrr$aa%ZGvj!r_rL# zO!~%do+Ng-uXtSQ)7OsZ^KnKrD$N9QU?I>N|zAbSF0X5|3N$q=eP43W#QI>aeK*)`=oxh}2R9xd3GNyTf zIG&W+O8ZL}ThYYe{LvNe%zvRzyjt<;@ZLH*j7xeQxBF5sc}^^y9ihQvjXs@YZM`iTosLIs; zUg!5@hZVaP8Xa9HxSDJu6oxiyT1S%`A&x|t6=t16`kuh<&ttSut;673$NJB3hV%2K zO8!>4h|^eMRt`FA#V_${k3Pw2tUXk^5d#Bn0VIvVknuDkx3c$-CXbvVb7jTyxeWwU z{-1d{P)KS+`jbF#C+A3<30PQQZL6vy&Yy>>vP(f-fF8F&h8`iwTJ!aBH~sy%M;g`F zsYOJv>CeCe2jXbBNX*sZntJIA@wTGNo>`Fn@^(MB?N2za1s(+!sd~@s&v|QB zo=<-;fxJ!!Rb35XhHY{*Y-&k5EeVh#i>kpp05fS-gnE9?DKc}pqSw3{;+P^T>sjS> zOM~w=*8%bZ=fS<(Lc(D?U-epohnl$fw=R`_p=kc91!w*lNoAd+_cW>1Xhy&X^&;TD zy27X)H*Jw2Qu0G!@Vg%!C!^OrqnryF$LS*YX%LDCe8ENm6H!vVQcZn}!fbUe=Mh3f z4f4E&XO@_4UWm-uDinA$bFmPSUT3C<57QPUFqLu=Lg{9JPVCDJ+`c^1ZMs*?ZDNFC zog{~#2mJood?262hiiJPd26(*m2M$eU8^v@WJ3+CRG58!%#k~1X{+PhRpd%F7ye|% zv7p9pw}klP)rTQOC$oYZU3e9uMF4Rh(IE)|5b+`rhf6+ZE$m~I0=svtWZM>JvJ3h} z2toXQ-e=aX@{SF=X4t;QGPhrFkJ?FGv%mJp6qV?mg6ak6X<`T6CKA82LB zh`)e^e&OO7oiG70!?~64iI+vdWtB_6qa^JmmxNs6*^r8Woe1l)3$qxETGA@qiCr_i zAcv5hz{6+ri*AxqL5;OUUv$5DXtebW>jyvMC0SLi+BaqzFP{}Q&6x0R-Mu+ zg`vDU?v*vUI<{?~EL`g*GJ~)?TQ-J!mfVIon*%dqA7MjMf{p8Pc3q^*#ipclJ1+5` zAt7$TaC9*d3;A!rRbEViQ7&nt%_Hf(+Qsvxs?q121d$*h_z0AQRnBQH zZ+2|HY)#%8Efg;v=~C7{(j7Hho`W9acVtBm$Heux75NYo?ymh_OTdr<4mNGN#C>U* z22MX~5%>ncU_)f~Du{b*_jd{RQFaLjk=#02^`QxCh*^m$+h+gh;!p+s zv)dpM^ovYgzM2y(5kxwX_aOnv?!ySyO^gIwsn{SNv8Jbgrwk-VWry0!h}Y@BN@nES z7k8s6OQ)3CFA*VpArg_=GrwlPDB5Y#bglaw_7AWFE4N|t#WmeVj!D^O#DyqeTv&n8 zw|6JSwZQTKgrHK584AwTMqm7%s8(OQxt(=W4H+jXym)0q{y)WUH#iu2ec0!Q73VZs zixn;11D%)N$Xo8V`YA!xW2srwGK_w@Y^u;Q*Ik4WDzxF9zg_h)0_Z}%_unDWVj>WV zWQA#K(LAtpl*a@NPg9BgZ5YARz%@xO}Slhr^jDg)DBp zyJIc_1bIijK`0-#P(IOay&%aZSxW6jO4k*)f<*-{D>=^aq zV@S1A*foEBV~MJD{6DUxsH83^WLO(!0XF@&6cQtwHd-6cO05l}q!Mdsu;^9~$Ho2- zzP-5LmXx@#duftl(-l6(4@nT9Q_$j-R9EEgzVkpRUN^w+7Q%Z#81Z9Z*e9wZ=Ap>$ zMh7j~muWKQ%O7XhbJCH{LlnE>5)@X?u zn~l(P&@jLH-n8_op@lr4;A!Yb52u6jwh-bco-by7Cta znPlV2qhxc>Ibm8gauKabXC`0i=NVZr5%#eQbs7oyt!+UMRGb%s49pjknf3$&Z9fT_ zZe04si>?%JB173^+vvj^^hL+vl5Dr{59&>|kXeqc94c*azF~ zM3GZ(Mw&vd6ov=E(WW~1gAp!HQaB>ftU?Plft7MCT|G~ygsl+xKW2EA^#@r}c$QRM z?CB8kvZao`{&pNXE8hO8CdO{fs;OBJqYM5fS>U>-hBJ^dJBD5ayC@?DDgZGs6zyPP z3NxM0TQOseXrbQFlJKfof{*&t)ry{~vymN-Wch*H$SD61jxxb2!kL8Gt~)rQ8|{5D z(337m$bAB3QC@tb@wphjiV9xc#T+I9j^Y^!>tcgR7$WiE?Et3mR!TAY>eS zyu?zuZK|6b82J4BU)-4F&}dMqjbhTA3wp|ZVsVTFnS@wAo%&^?FsZ?3Xi^gidA6Ti=qAkLTU8%?96r9=d`gbLhgu4VW|-fl0csR8jt_~gPdB9|@I z5WqP_9%AwJ%E_me5i!W{;T+9utwOq`qJnsuvQ^B4Z_T=5Lp>u6urso*PCsqJ-^tNd zhUni%k$+orU_9Fau?pqRGr#(_7r^LTbwwNObwh#Bxg(h0#-+onIW@Q50ogn?>~2f0 zm~Q}XRqWUt*u3r9w#V^E-!JI#GFc$RIfU^CY{)E)l|2Z zk7t#78CawyK9Ei?Dq?;bo5ryahZf;;Nw>1+?Uy(gh&lJ$v5K2udb_HF61<6jV#Bz9 z!CVJL17uP1a?3%dV!b!d2_J?3vacM9LdToGI4?7 zKR*tm+?j*VFHg)!myf2P2n26Q|E9)hy*-7Mgx#3|zo)qJEVu5#_bW(7;^O>}!wmZ# zn`+m2+8a^;#AjavQ0WUJS-DmF$d?4OaXZ*J8P!-?Um4_@unm?a6lGE`S2AF!Y&Mkn zwI@rP^YKk(pIsbEhoH&Q;JYo|NM_;OaUim&r4lj*GSHc$Dh%&rw-nNRRgnO$k{}+s zoiFk%^;R5ft6M5$QrH5ErH0DR92>+AV0=R)yW|kLuD`&Rdv4};)ICKT@2+qSD_MF~QFT@yEJpUL)43L z4LJUP78p>d1?*GA|LaN zvJ?Cb1d@()=@A#0ss020p&`qAv9Sx$i{_H<=WL=4H*&E@H>YE8<*h9|u`r?nS$k-I zOHnkLy*zzH*^YPK#-Nf^r~ho8UndxPjmjBkn{rwUsJX3Xn8_r$K|Ae z&$Ixt#t~#NCMz&k7V<`WN8gSPtD%;1i=sU}>BrG}z8MP&l7~|EW1pGZaEtCE^`+j} zADk(QaP+54$4Ni9U@#U0gm{zUpOtFg5FIBC=P9a^=r0>!95jE%z_+U`pD!Gr?q<|A z6**2`MjSCU+Dn0WnQnjcq{4`F^T<(9-$3Tha@`D~JD%lZFo_0~dStMjqlA>ZNyRM% z;tj?q_b*=2)8`az2WblsSo|MWT;AB0xR!2e;5KVKZ3)56p1zBv6P1cc1rNz z*d47)KoCAY3BkgD=I_i%8;!XXnyi>sJBZ9o3F6SynDPXNaTopuETk9Rx{t?PA7F<^ zl882yl;HGpj^wdN*cxL9A~}hJKeR?+ZpNi!_9^k}!+;GvG4rjqXx6lLhjVZOH{+yo z4k9VSbg$W*kc&}CWso~INSlGZ^P}gYJYld@I`_k%XY5mlhugc`+wYIN@@m-oL3I%r zj-ZvP2;%vp(ID;jQT1*_cs3Fd|xg&{&X`_R6YCM8~_WrQ^) z;O%4Q&=&ZByy59#f^N{lO5+xRp@~{EARxub3(tPgoDCdq7l0 zFf-FA9QP26CvY5b3K(EMeC56E{o&f8H?fwtRo%LZ-Kl(*Vk(q*&SL$K?9$Y25JD49 z4{Jkg1qdW2K0&xlmXq+m4=C9a$;L|?lVQnIFqVV$WTNzZGU+V)q|Gugtt=N%juGX; z`@Q_xd~oXqq#4+_kK4g__<&^I*Y7wNlLun*;&~{6nhiu?;(JJ(kn+lR7q)`xnemxj zs~%k_t*4FjkG|VdsS%Myl3ez*w}E;4=xKBI6jP zhU;jG*qM>ROZmNyUsEq9fN?D$B;6+|cOUjY_pfILiCm~Y;CcN?6r}HIFX~+(uMu(! zWVJv}bgK)`4V~JBu@s!Mo@5?Ld$r3fxM2F0)zipvlfI@m5@^gnB9dJaV}2h?!pE}t}T_Br0C4PzVWVP z3@L+AYd7uvX4S-Tu$TXb=Q?4RXuA-J!RY;5F#e;LCe~(Q_YNEwy)B&r2ib=K1(fr~ z#E#kJxb#L#=V*zt7XsIGA_=q2PzS#-xRiem#4kaFX266Y>I{EA%%Z5qFYHE^P9byH z=Gi|1xtxT*kDu#}3U?9=SOIhM<=Lg;xP=YUR^(Rkq=O~Lf3{t6rT^n^A-rKrD0bji zFQt3BImN;LKla`{s>y2)8>OeGwTD)53Mf;oR@p1z%^i06EF-S1oXu64h)ZvDe0 zwAt_8zrBCc^E~^FY4A_-=U=LcTwq1agL>AYty7nRU}M1=L1T=d6=%F&S|=Ubx_#}n z5!YR2fTEY2j;K^LiaWmJ#2z^I-zo_Ve`%R{$jt}MgJ%xzKfCFD z$^@J`9UQozqyp@FA5fFCz(c@E>;$K`Ke_nE{&dsG(Cy~$*Olw@ z_G?|tch6l7=aC{8C~T)`Q8Z<3##v__sM0(7Ic#9nQh9?vJmBWsdV6Zy>8_4k=X|r7 zUz2NBR8F-&cSvCR!LcLqu;h|C)@Fn|LoSRrYoloD1Z#^4yJh&I-9*sG+}N5o4pWt@ zJM*Gh(9(G}k2E_c8+p$YR7bDzdh5_2F__DbZ|rZmb4`rir*%PsuKF5miADk89j6H( zY@pHr-VYFa@E?NB0q6eWr)l?D$b>r}xV&aPXA^6}SX$N$B1`c>J4fm^o?mZ%cfN#~o};noC|#43=eY2YM0UOIN#TG#(!>u~#RDt3i+ zYw+cw-Pa-5fo9fk&>dv5dlnHPCz7LsEO|&(Rq&(H)G`L0ta~J*^s1VXKb<(l+5w{e zyR-QSC~?if)?Ng;VbRu-``wSEVnu1p_!ijLK-@wtYizf0dNQH_%X+{xWF(LY`U;Q5zw!V+TQQu!NIu8h=zwqM~8 zJ$}3WG9x|h?19i5);2J05IVWZ(9~%LBbeZjdi~hFakjEl4L^;uRdr;vi1<{;GL?Oj zc+(0W!aw*h=5T_!6t<~+CF9;_S^^-W(DHn$Z3%kL?PiIS59_S}m#;}+@16HTPybGO z@I_JnOF!i?&{O~y${s0R>am~hG3zxshq^QufDFn*jb_IVWgQ{$V7CF{0}iCGK`bg) zrGa`@F>>_NpxZV|m2=H-tfM;G>N5_Li(&3U_=i{{0A19&s&8HQ8FYw7t)G zBijtRkx?esp9?$)z9jwmWK2sy!~DK-Vq>hOLvJE@S!DVsq7rlv4yG7f5iaQL0VII9 zZ|8<{aV?K)J6|2z5qS!LkE_$xz~?1n&Kq2H)AYS|oNX3V8tEt&>h9hE^YboY+C&-}Tn zw1UBBHLXRor*|Cq_-|lC{xGl047)i3!S;d!tN2Wv9QMf0BxQjn<0dFB0PLwL!L^Js z7Y#!&DF*5%=+(F&je6wOz_CHV#et?|$%QJwt7--TLh*$`T=vc$KJSw9uZ1~fe;f=z z%>_N#h*s}<6J&yn(VV3p%!D`NgZ#(LkdKLg=eSxwG6qroEqEdEZp zqV)%m6l#QwY9ndH>RLce*ER-m^eNxEXKv3VpPil1$%ov#WW zEO2!J2g42QibkHJRs^ZJc;G*$f&DZxXD#MSZ5Rj>U$Ac(o9`B1-%R23`)x88v=~Px zGDnlP!Sgq~RrxBlTBc)9Gk?cAUDuC;GwXbK#Jj!E6{LoUID2q9Jx_gVRKN!rulpc& z*9!Jul~J|3bL8%~HyM;3&C?n#+3TVv8NSSs2ta?BMbsRq=o?6OX)%mWteE=D)wu;p zMKe*E9W^`sximlqm}BZC?Y|)Rr>M8=0tk!m_wZGX`WNcQ{bBX-dFGT&O(sy2x#^>% z3_OsTNn|~xGjk`CJa~<%2H|Tn*`YT{ZC7;T$Fs>T#jA*44CJgG4aejaxH`EXPXNz- zd<&jo?&#m`s5+`H7qmc8{a#q|LB|YE%z^8F%5ZGHF!JCu zw(fJlham>_9;wnaHv~3%_nj^de!?nnf=pxke;+B0>$XX^u76 zly~*6^uyka@om1p7D$NP}T*-RkY@r zxcalY*KszH0$w+cnn%7BDXi;m|AlgeFso0SQo1^um6ktogVD<(ic5{n8U54khsOe- z=JQi5ctD+BXc7V({C306xu*wHirBYk_M&j6xYgY^a*j*1c-rXN!mIFLVl2#~Mke4B zqR+6Xxg&6@*Ak~RT8wSyN`ZcsIA+U_=q)YrIJQZjfs)Xd8g zqA1AKKfrL6Ikr2OG#cAw#nFvD9-cOhkvW@%*PfZ5y!b0A(OUV%KuV~KN*OI<{=Wdh2iZ7?H z*5;_SUi5lPapSe%=BP4!P1%DOru^1TQD<#%tUsnSIKo?eD%pP|FP0?#svj*fFRIt} z;~6SARb%R7c8n=U4MP*nd?R>!NfCguhech8ZV*Fkx@~>sJ;}r`*f|gWx)adQS2lwm zm{IbH0IpQ-_1c>#{j&b5UcZ#>JCsTz#?$Kykgg-e-D9J5=(Zf^O!lkIj9k1@ z(h&SRIw0*jJCZ_{OXZ{9zR2O=_FjaVBbocy)89GPtM@ts_@&(XaI zH&V;U&7ME-92(CWsZ$?jAZ;JY>R*BKN9|!jMV&<>UsQ!9hhvivBTuNL+5(@RCv(2G z$w@YpkqOQuzE9T=$0d@H2B6cN<074dm*yAI%c#6C1 z?TZFPzfpoH<+^^fGxJQB2rlp`$bHKGex~Q{X2cRlBwyktixL|l`QDh|Oc|Z;bw^ZGfkiNsY3zLC#8YDFhh=e|^!F<(sbwGyo z=+#6SHAG3e_1j1#=HT4eRG7J8y~GJtYC~4zry9TvRMH&^_KJUadK}+sMBa$ zyo4*dG8^*f)sYQEXI|Vk-xzQJQdv7G_d)VH17hCACYMoz>GNoX#q&wdk2u99-;s+{ z;u)P?n?fFD7RW53y1oZ)=#hSFOZx<8Iw7po29sY|H(6Bd>Yiisov)ZrNPSJ+ zbiMYk#?30slFYL2*kZRgY}Z3lISw}$a{ShYt+BL-~8;2DrzcYK7r*Uk-h0VPCnch?Rgxz+j z1({NYE=|%*7D60xK_@0Qco6g{$%r3VpM|=(Lw9fXofqnXaG927>OaOf>>sdZRVGaN zP-e6iwBz7xDtrH_Yb=f2k!(k|0$$(tD`|H4ElTNwO9ZGBd!uFT_nd)pANVlqR4Ps> z;Ey?H=5P;xZkg)FC~Xf1w|`FA*c)fEfwlq^UFXmekC&Os)lADrwl}ZGHG$ssWh+2@ zHF{)CQjb3C9oM4$4nPiT z=L-8EHoZGT9%cv;*1l>#&OLSxiFV}Fysd!)AQog4Jl0``NR}VP`-^_|eDUBpd$560 zV;NH0O#?;m#<~&OX9v;`pKlP%>S7a4Gf|Ztq)ojE-OApF5PIZVT>SA0W;e^pETY!S z6PqU8^!j0Ky>2f!MJQB-x+c>UGUh(w`Pk1!kcbhaw`UFS)Lvu!+Zk?p|FX-sVADh@ z3|d;zSp&})OPu)^Jb(jIq6USKA80vQ%6W6z`!(MYwKwBp^S0))eG}}-YZOn_>DRxB zyTNx0Mjr%ey2nzZPXQptIFNv-3odhY#kWTRE>`RbZPp0e5^j7Dwk5u3o$W>Dj`(%9 zDRV{@k)SL9t|`^-kP>40I zi zt~e(wnoSd_lm2VzZKne{bDx11htRiKgQ~1TSA9_-_g{oCFWi1xp`dFn_qVywR@^d} z)%S{i)s@q3LqL_P^=daLJA!#1Jn#=7PXaKjy4?gPEWxq&iwm6s%tPe%WcOc9z&C6P zMck_%SEY$*@wnq7bQY=X^Uf=`!p4*DtTq?-+$DE-i}k=IKU#<=r%qk3-XFIyP=rNQ z*Gn9eoh6&UR*d3<)xppWSq>tYi-h4_POu3GRn%R5UPcd)X|wNAgIpwj|5WF{iuuSf zXNjM7=9iMhe8t8#gO%oD;gv6NiPXuHNp0--;6ZKhVg*foU>*Nb1~mfp@HN1_y#kUP zu+7x#?fWKp$|V5LhWUL4yahBbd)_O%!6C{{R^QPO1N;Y61LRx)AKuVw+$d4WMnO(3 zpA~SZ2Le1ek`8bN+F^_}DF5&@@`UvOKCSXKis4rUD2$h}{(;$Kbg?fMM4!ZKNx9?n$V{% zX(|3d-0wDfe%8Cg_#n_0(5_DG8E?zLv0<7XA{*2w7t)`-uMO2ALG_g*;0+*P z9)f{_W-Bl8j^VW(j0R*z?Qg9IITDn0*-UKR%1p%XSoKMK3&eE5vombUJlMev2dW|> zTjUWZnAP<#L3~Ah70vm8w>VpvqAQ(b!DYUK?tWkuq%$JB^`@|h{4=#mZ*kNH5XS)o z&nk;ff?3`C4cvgaG}8GUto~ok#rqVh!iT6)#@eF!Sk=?3DJi+7z|F=$Z}&kX1d=GY zcMRZ}#CJ^*vke5WRy&C*5qoXw_BBeX-^QmI!daZ#oU(d`1aUk=@wH!!|D;VjQA9U3pZ&|uJb2IqhR605;zM7Kg)*~@&m@J%q`4ux zubat#w7il&o7t7+ICU-8m(yxS3K0p)K*dkc)L!Ca;?%;!9%7zO@?d{;UG5H7(keNoOrL z-#OAwo(w5=1#D)#T|mgIQMkz10YCcy6n5$~0)&%6JICi=Ty&nsSv^JU zEAJn$7HWMAy!I7l%6P3zHHa`ak7;?ME(BBbc~^C3z&C4d_KjgbC2eBw`zaJVkmOfg zKPHki=E13s7}6#J8-$qVCsZfN?kylTZ^pHKiwpXr#XXRO=ocQZP$%N3UDMxZIYo&F zeoPR}CJfB?cWqHeE_E#hzgE_WkiMi+AlO1HN@|T@+Q51s&y^5d_(rwd8heR-i@rzt z&=q+1pFjcGlDv;TlmMZ8To@)A+$h0-qxN9JZ#jzk}nJeC3M_)6FlRGo#rg}989?91VrOmSoxV{ z4!eC%78{XXHz5IM746%T?;MSu`4@PM3Phdk zrNr5w>LC|D@O6cX?~0<0WDuLKEg|Qh&F^aA)gmgUkOcg7d(|a!^l$_MG)q2gC;&M^ zm?N%FER))ESHa)-tzc=Ge+I*-Q~ z30@Cd#7_(0!zgA86cv#@e6a0rAFh-T<7cEOwS$$NQ^(^2BC}-TdTN$NHC-Rwy^YshK`0w`$9x zG2+^kvEn{U%v3jk>>6DlK5unPXVTg@*~i*XQSFUN@4v8stIPc~S|WIvm;qZde6A_vxC(J|xjB`^15ofEe1l~Usjr8}CSOz85Jzg4xrZ}IWhR`? z0;H$(7^muaNc+`akpRsC=nkD*mvt=wudI4ju+47cME1Pfmm4XYHe^uT5;MfhK(u)U z2lo80v+zRGiB>d$NOa<$Sz_}k*=%h45X%vuk$0b{^;F8S1_~X<**+yBf?VUCgWC&m zq0ez4DJ@Dvukx#70!9nCd)`on-X#?jW;we87!otKsSZSS z&KuGW@~9xn-}c{;#oMb4crt{mwtF5behu7Ek?@x@ z0I9@boc(^m5y7H8ujA3n!&>Cmn&Ljb5lO;h6A~w##f5sk#prCvyW;rvH zv0G-xm$V-aLaZ7UZDpi_HGu36DnZaT^}e1O>A5@aJD>ZVV_J7?^X}$^eT2KO*}KZ! z&Lf8UfqlU(9sq*tX=2m6R;%|ags%TCIH5prIt|j<;UF;FjH)~~x9wpjY9Lv4Z|`s4 z{Ni>PzIs3DPtvd2#Fr=b7=7{V!dIkSfqRxbyAZf$3B+a3>gD&hu6uG}=BuxMJ)N?0 zebbLm&8u(QtFEJumYxHgMd89MhqL%k$d%&^T|zSN)lrCPZB~}H&LK~6!=b^kJauj@>8$u#t72i~P;SaDyCQ}UUKKC?vdZFeRXuK7 zj`sg-gvxvMZ62CAWP&-2FU*0}Q_63S^SHA{=ikT^lR@p?;n2nvT4C|T2(y@gWk~x1$!HK_>;Y5rE4?tHESBp)zIDDp5n^6>^P6% zqM1^IS*Hq!lFR>TVI^QeA@ITf>4#>HR)n_W?3p9oIjL3kjE%<27B&HFySb^{_s}3U zT{G=gFl{ZZtvV2_d7myjf_cpAjIealK`+bIZ*X$$CD*6^pu*a@NdrfE%g zX)UcSDzNw94)@-qApSG~)=;WgSR}W;msf&AaVv7U~9oualCfhkjBdQ)S{20 z6wf$aUTfSBQ%1CBF>r`-rrNr1o7!ir)S?CTl8)JHU=hvmlbPVt3bbc9+c8HE-Ka`| zT|$HM{)GA~J}z_+>&xT7erR3LJgL(RlG7kNX9&%`{AT!JoPFO$@I1}(O4ft_0(??S zYrk6jaX&yaAI%_I_lO$CM>E2WRJBdCG!s2xsvcSEhGr)U$?9YB|G@`*W1q!GMlf@c zR|FWYP1B=nT7B&2H#EUSGfm*WruJJeElfK&8`_3H0LLkREa8Q!e8V{M?-Fe;#4k+$ z!gXy*{pW%Iul?Ma!Du*@E{L-?n3g9H>x*X|ulZoM3j(L4AbUta(vrqQ8maIXvhUcj6n(X$4#Tm{XKFrutSI5x2k3V3;6qx}&dnAWSt2 z;0YITqkE=Mrsls!fpN_r5=T#MZ_eP!;{{v_OY76efv9WE;*LD#bJONP5| z1gTK|I<5k~tsCYSniIxEhfqFL7H4}&SW^G_@WLVV&eBGB#;GlJePL%$pu8|x#MBy$ zLt<>-3cuKEvf+`g9e*v2^xvX%^zid>cQuHfgc}IA%@Mq^RJuWJHr9#L^p}In5 z4n%dC93;2~jwfC+l`|UfBt?Ao)0^$nV;6UQ&xvSte46!4Myku0^Ay)S#a#&*nq%K& zxhiO}F&ixfJL+K#_O6859jLys&K|R$wzJEk7vrep9`OC)A9N< zwy|`ZDyNvi3gPT6ox5NCZ50U~Y!BJ1Pey~gQW*WP zDniWdqQUaX)(kH4Z0v*~1xHe!n%$3 z>04Va5<1C4zp?J~O;T1J&9VFWM8%k-F>bRo*1^E?ve2KBD(&r=y%~38epJLHvsWDQ zuEb%I{#>dH3Z=1(jR zT*B+!*|@l{3l4?GzWwEh-)Tj`9j_TKjATX=!9i~TFGozfX#c2m@g zScO43nDv-XP9(9O(bGhYU=@gTVnmIC+i{w6!fq4>}N zoZ9A%EY7sdN#;-(Rc&&KeopZq2P&}NOH(vF2&(9Xt`)q1)|5$0wG|@C)nKaU;6=VV zd1-A0aeOjGV|oXO@zL)VoKbot^olBKYB^?~Y7eugUr@GWxMCcArV!E++Rrn+-x9<1 z>X6Mri@x>B2X7~is_=6(63o}vrEwVLJQoGNjFB7U#84bcf^F+T&7wOfh}B#)!g%k2 zTBetLT@Ui*p(NaPj@=vMP*L@|v$y~s7hL|BOZXAS*MGPr(1tMRYu(_+MVDC*>CQ^- z^-pXJiY#DbI>>8yP$Im`Cr2qo4I;A)s=4xp*x!>duv^Lv-6-rizgvcmHw{?9l|YTT z*DKdc^V%cgJzE1#(mjpn-xnQcxhZom4-Z$zq32@-W-x3cQoJ5L=vORA0Z-!|ZzN2O!;p`u{vOhR6;d5!h)It;!w_-1vZWgeXn^Rm1g9-7U6WyX6 z#_r$HR+V;b>=9w@a?-ldHyHJ-BTM)C*tj^@yvE+5fSBR?FV*x7&TZ1-K)UlEAc*>C ziqntLb!YnR;qe>&Lpo`kc12Nt3vY0oH3%~`#_PB^AYWZ)=M~AEYR!5u)B4bKL?ebT z>M}%`=p7kk=%Kvvg)^0#aOjfkq2E@d<87aZ^BaQ+=be~QD~@2IJQs(X#yk*eOyyyp zoU37S4)(}=QVr90lJ(3~D4O-Wvm!i*QaNOak4c&{8h*pyKQF7b6{ZW*>nuZFsPg6{ z2e#a+N320R$p$Q)`)_Ji&W0{=MVI*YGO$@s1b(BPU@xLpC~SoHgQ0a6~5u0B1 zz<71}*8-cstY9|U+ule)3&F?|c&AL|f&7QTY`@S67H`9v1f;*S!Yo>L!++4mRZ+`U zR*=Kb=0^2P*bT1=4w-SNZC_3;4{CbFTd|qyKY34Y*du9?zD-MU0$P@u&aP!cFJ0@|0$(y_4QXAj_GiP2N56vA?|#7dsD7h9>S znM2|UjV7dYWf9pUFI;5fioTXmKiE3nHgh2k!Bi3mEN7Z(_V;nt1E0kcDnV)v)}rWm z0`xFa!E1M~zv8`zDIH8pyeV&QcpZ{niBZkzIN?h85c+Y*-f+x#>tyrAs)Dw%87@Y7 zv~@uBs=wFzWvU4Y=BAL3jPu5(X#N9h1=oIL;J{JQ^n~h~{jv0(+Mn$2|Ma1p)Y|zA zrwZe*a0{Clut+zRb)@&s(gLSeP*Pd~fn*UIY;VL>78K10qMzi$+YFf9)62Qtoi+T* zRHe?)nQgcvB*rqtLEcYFf9OxDbFUmtYNxDZ7waM#Y8{gj&i_$YCY`f@7x%H;1fr%_ zIg4uvErW~iDv47Ib#vEO5toPco0_-}^3lZ|6RMS7tuTm5W6*FD|ERsOm%KM>*GA*Z zF)Bv-uq~^xv;U&q5QCWRSKNfNut<3k8IDEL6xHBb!EhM(kf>?QF^9Q=IE#`-LnU_Rsg*vJi1Wzw&=v3-kAs z{?BM7|1bApv#2pCGGe&rXWXL3QY%%vZK34QI^hdT&loO=+w85VDZ4UktJmb|8o%8l zHDl;XwOt~uv!`wNiwA{~KUTv49sm#{P20WSJqfxuiCyIx_c29j9eHFSyvz+8+hu-z z@Eb}BvcYE9E)v~}MTRp9=aO!4zQL_n;)2$hEWGq$8yD4ncyR4POwiizoCNjwuL0L` zn8^*>@0@hdxgW@U#MOtTl+tPnP32LbE>{j1k8&e%`_cXYrCK}-_+Zna!K{mm*LdUk ztB}Fa!3lB%%|7-ood-BiTA#Uc1DIykqx!7k;lH{pVp7@~Ud8TD%KgMRz~w0Qgo1jc zpQ-)Fv~r>rJ)9c1xpVp`!L_od_f-7!UI)lbi}>L2kX+X)It zY-Rm8m0=NLB*PDl$DOpdT$Y+K+(|y$KdpM$Rgn~fhdR-y4W8n7)0qlZM-f_l;*Oqc zqvL}k;MJ_@_&_1jP>jB_nk%W@9FZ#Jjb83@;4EYIgM9OQ^>>bj0t`8{#AJEEiriG# zv$tz-s*^iER0>*QMi6>|+N{MhCAJ@bo9!D#PZ;k8-!YuqKze|3jb9Hi0g8^=is5LF zdqp!HH1;a4>EzyJm(7U%3~d-LiSnbrXy*Qnpj^R(;VK;HX!k;}ZM(LCstY-pg0HHF ztzx8;PUY}i3KhA=goYb3o$`g&{KO=A#sPv=!x%vwAAfr#gkPB>JhYMGMGIZZm5BO; zA`WA(LUHlvCE7NxB(_aYZkS%V`(*L(G+CkAnI7y(L5iv{hAS%%lw#Q`yTo`?c?HWW zw3Zc#Fjn-J4fvbY>0oqKYhs;fsyuB(P__F!x>%c?!-WpN8gDyE*tILi;F; z%GjyNX;>>L^4`^27q)qVk_MIo4q-WGAvbIdD1GZ+S%(~DUv@tw4Cxx3s5U(-5}BLJ zLc@!+5kla3FZg5P5N5*La-ZWAJIT)gU-*{e?{yuwhI?Ibvnq&Y55e|KCTw0ts5cGqEB4GG!e!O&ZY$74)vPxLhRR z3OmM;siid4*4rO?S1sU$`2U`5rU@woyuO(=*Fy5X%@Z9$Rrw>OIi@UL4t^^pO231uX&}BI44P z;r&QehD9@80|a?<~F5(Rvfvo*jqtc^JMS$DT@v(LtmY z1jZz(yjpR%=_{sFvc?EFi|!wGYhO}O>BqHm)!T6W@LPGP~0OXyMc!eOe zEk5GmqkJ+a)}urUF~=pT!YTms6;|2P?RxyJHtcQ9z)A(m>j`e>r2)UtmoP)0#<#)L zwcy(YQk37cUCE1efFSU~eV;>e0JrnupoXilLbPe3M^aGbLg{>>x9IsX>!=RO8Ht%Z zE3kmfrQ+X^yo1?&!E8IvK^U|x{gv^VNz<7dlhR{MZ6SAs&Sl@F%DlKTz;U5!H^dNX z+)>YZRAOx$YCcx^TlKd&={30`mr5CUm%I-o9?W2&54{lX4rb6xQU~!xE7iL9P!3Vn zk^}nap^UiL#9bEY!aA_)Ui`e$!?Kct{;u6M+V*d2jPV2~+IcieuGeTsa+8`2)AQIW7QK zChJyh`U;Zs5pa79a=scsUPzQ%Ge;>iqp!)8ow2D^9o}Qywv#u_7iWB6{5VG4siV7 z*){2hn-rcEepuLB$?B^4nzOiDs6NiDi3@kGCDOTKjEy!`n)H}SPxq~y!!?C0mDp0H zB_8R7#vl?MNpMC9DNg8laByakBA8P7>q3Ijlw%^#xSje7#?Vrm=Y1s8(mJx>Jb(}S z=%89#A%B11Z!5izt6HUYL$$kXIBhnKQo8W=aNkS+LE2}Fx*L!i0bud5+o^a-7;Csk zhT*>L6jkEPccQQ>=?06M^*!oHbn#=J_<#EIWHbBJwx$I8oTaf?2Q_}Zi1q?ZoRzvB zTtxJBCKnI?8W~m6|6m&b=Ev;gxKF2l-;q26eBqNB7Ej;WwYiEw8WP?R9<9_?m=K>& zu-D=w-c z2o-!BS}yw8!*Ds*I6#-+OjCf1ddKylsR_ELsEtqf&EmcGYs=flXzlDVb`x)?tIIAX z)rtsfONb%9ob%_`cUg13fvt*djNf*z9=0;82_48LE12zsE4+qZ`C+Z)s+zK7k~GdJ z-|>k*k)DyCarMw$Q~vi;6qfPA^A!cRaDY%&FRou3le-7uFn1!OGardP&;id%LGr6*Q5yMLU9Y z!X9d9SJu89Yz-F8?YXlWWpg%n(0xeJKF}O*n%F-GbFt=2c&(EpWxpNmYF2{&VD70L z64M0z6Ei{P;minH42a#Y@f551RRD>71b~XCg=E24vgwH3aOVwNTL(MTT9DTnf3$CT zuh3DxLwH&EO=zj0d-8Ga8Vt4cUSC9fIMZ}m(N89^*wfHnSK5J&nQqisa=plh;^tJ53@kiIB z$1U(!)hDPo=#$=agliX)erK{PF$B$CUJS7Q=_}o6^m?WBCH;ufA8~$L+KhH>?U_pq zv|p{D<)rC3$;y1372)BM{k7jqXVD={-dE%Ty=gqu88t{RVC@zK0ie}S*06$KB|gbC z=CFVBYLkhS?o8xza*!2g?Io5ALoubT0yVNQ{3ONh2)ia^GS;=!1Q%9OT04OL(cYK~ z-|6a)#9czB7ZcOZ2QmkwT%FXOAdu%4L6B;fcz-tbe5m5?=XmOrPIpuO^!KU=nLv=( zPuat4l7B{R|ItBgxYq1WUF1;+z@gm=Y0h^=17b1%=L`4pBci*bL>2U1w;*B920$)A3u;Ys>_FcaM2)dP2hBfQx|L9ZOO=v)FA` zL^hUOpSy-@E2-nKEK`MbmV~@b0UT58a;{AfxgNGwkbRC&i9Wu(97ZuCFLFI+ey%~B}xl69Zc=Hm!lK! zTG4B`lGQXtVg_O0cixc4_Vt)A#(^IvH#$+Rm zsLzUy3$loe*HvGub_!EsTFQx?xzHZf=Wu^oCzfpb`(<r09DlJS0tF<4ilCxT}oGK{iBv-V?r<7kH19CjtiH zBN-U>&&qqE-{DEoF-B#OdIW^k5aN$?uzCgaIfSBBNn9#=nYJ9pgfI?mG{(2E}CJ~);Q(RiWme~TwbvODaYU(>;((NjM9JOV)qMPd`elo zL&!mQxG1`U7h*<6w?^=jT?FIUj_Fa+&-0TrPza%62`R@WxP^D91G9br!gY*I(`m9C zri;;l56=LU8yjz13@|0%42DDJ!N@+bcON0$rfyFUpx!y1Ad3y`Ca z^uhf|PY=fT(jWapN(G^(AZp6?<>jh!mM=fcnN}}J=TF3Kl41gk(5~pF0lmt%s#g;E zCfy^0mRB5z7+w}}x>d7s9^qG#gLf$x!D#tQT6#W|2?x(t8laC>_@x(oLb5H>5Raf& zUA+7Ql>$cNAUvB7i3}`>z#%&rq#nWRSVG{i1P)_c6}{Bqh6m@1OaTIufH<9`!T^>Q zJy`^OM|i?k3hb8k1lFzN&~_-JN-7nT=aK<=l$LPnw9q)`is^)^U+ksveovKXp`_Jt zfH3qsZH$URScn0EHI|NsJ`K`w-e}pb^+oxeoOwIf-lICH-AU5h58Nww4rV#&O8-ha zrY?3HMEM}@SOu@Na(+^@;+r~Z8BqzT!L*jm+$LcMkT_rF&hoZL({kRa@gro2pf~9A zn9JpXz=wtRZIA$#hp_H>c5zby`J)CAoSdQYq(oY8~pmrxsAhcn==I4@?wt18)Tl7pMW z;Lm60gW~qoT85~ankI?;ILS}*lU*RTd<5P*Z8QgKQFOddcL3_!iG0%;`(i#&+GIIr zit?%kY}h??m1GGK)+yFTl%?iXIYz$~jO^)s6XYWnI^wpR<>AJsx%xp38ST(~EcX1o zC`#CG!Ml>+md9oQ85KCr@8HA}7{HN&()OcpL(AX?R4=^@yRj-7spp!h2x$tJ^luYh zW<48yfT(bV?Z`=wQ32>Fq2{mG{zPwdA#IO(L*h<+teeCBY4a>H zZFpGN^}4QuGmlF#eVd!QYPg&~9Jj@oV5;QqzOzyXps0wZ$n4O-$?3;Nd<49yv||{i zm-XyzwUr|j)kko0KyOtk3cc8tmu;Un4U^!oLfZU^un9~y0 zY`dAcah_Z-$k=egcmalbFEm&basO^5|L&jvw^4>`=;8WZ)X4BpXfvR02C_-@J4e)F z)vcw_rsha*`_;u*f1k$4Y7YdK-p`QsKMBUMC`S}v-8FlD3KfBuDEQ4CbW=ekCkGjOT{5h#ES>XBf?G0fK zK%qaz1hq)^zX+}ih2Nk!Pfx9Vc|T`K*EUXHOj_=xh@7-D zepWazWM@CX@_=S|5d-)Q1bbPV@h6)6AJY!Y26(wEU+^*?Ufe{0c?(3o`L!_bQUx|B ztQUPgd_G(HrX7hpIp2%Wt4vooM@H9f&Cy%Z&>R`+Sso$69SiIEk=CF??C%8<8GA@v z&tyo4L*mw#_zi)%UEd1{u}uxzWH!s>4hK_(i2DT28Dn|cjG#Mf z(5^D7;F+`EU~USc3BC=eYne(9Ler^MMRNB7PIn)rM!Nrt)^e#7`+A_sS6M~`F|FbboKTKUg;x<*+*0)vv7h z7*x;336u&bIYT%-EqGXzJ}hSMuFl+8x`%`HE+(uRO!9^QruW7vGp$FKuX z#|%>vMm>`#v&y8n_4hi9-V9q_i882^GP`F5yB|69Airj2#BX(RSj?ua2Bj339glT* zu-oF}{E82z;7srN%HOo-97N<`IN<6??aF zBzwVq5pY1%*AW6iDJ)-UDrW-DzxT(5Ul-4~Fn9Ng_LC2*c;Z0M-=`3gV5XT(Jd0$S z=#VU_R4zxk#iO~_k@nJJPqDY6><6^hqwO)X5tEijJLn4k3&})|#4}0dX>VX6yH?c0 zSX0^QF|>qVw`&a|Ey1TUf=kWmB9G~qFXWbzUjRnP1Gi3AncCmp|8XL75%$IyKT5_J z^s+=5Z~!+aqP^5cu>Ho(#3 zK!j1lt>ijhnnSr~ocDo`1Cv1eY~r4jTTovv`RM2&YD#awK-5}fi%=*!x~^vz&9sz) z_nRWuPaV6M+nBP(YY>srLag(^Ect6{m*q>s*F$%LCu_CaiayeX9co5z%v1dRCpMjg z7d(fp?dUErm2covy2>2hjSOBhZiKP52>440hK5}UCBpoadzgKmY~Ml%p~-S+9*~?m zAI+4k&1M9=r^~n>8DUg{gE4{5fEhLspAW}y4<;cZvLQ;#cHVd|zwaMjPbaNvYwHR8VlZ@jX_gS|Bb1uZgW_XK-A^&o zuY=pmE~*in?i{OVrX+VEICpvg923xNV6ZT~g^AxWPV@!>p|+1XG(4@Jmnw+c+h{% z_eim6P)BPxy9K-wxc3x}IVoz)Bd_Wq*z|+~vzPLp{DW&sD}L-Q6ikv=P4zL0-XV^G zsD&Zr{tI?|y!kd_5Sqqm;ChWUP={#z(-o|PUSi9pi~faZ?}9R~7@vz-QhdxQmr5hM^hs9-wRgNoSozYy+Bm~hnCVM)dc&h9lmfb$tTUBm}8+wWcdRXJC zP%oa;aZk1mLWBqyY$@N?(J9*bR)tYSIkYLWz@@?K3rR%_bff?h(^&{mC$51jjiZI+ z@=lR!Z%FZ+kase}OnwzpIdJ9naCzj}JG$I{jxwkzpHCg1nwD+CZ$#F|~(UFM3+m@CiWVsiX`Sl{DxSwsb?} zkqVaMa&F4_ksLzru(bMH4!d?acXHC-$dE%PAM`I~*1*z*nJ_}O9C9!QF^7BMORbSt z!bP>_Iq5=$=-{)!_UZ9t{^2RPm3PpeEOaqLKp3j2-pH8~#&`xXta>_hoU=#R131at zA_7xwZ$RTYhAeEfmfAKTh`zgr1!n(&tI@i$;FVfhYZg;_i`9?--F;tFYdr>cEZ(PU ztqbt~pFZG1whXrMWffBUOvLG^=<{bzhO7UDJQbq7W4HCXt?ShPvbWc^^o;-c%sR`X z9;ZUi1V(FH9zmUo*8FB)_{pF%+Lk`&PDcl?1Mgkis%`nxnR7wG(d)Kt-(jt7`Qy3h zs6%JY??s0HjEp!F5xs7gw&h-A81np)pHBvY5uMJ&oC`eTdj4d*w&nga5kX)StL-~? zt<(I`wsbic7JcUYI!l+ZlhJ3K&IBS)pV4-7)P6tHxE}74&z6Ectp7tL7zRj_`_PK)n7nXM>ZZaeEcuhJCKI8CWb$(cD}psP1ya=Gv_jnd54@0@ea9u zFF!wjpD)z{g+xutR6LRJ^-y^{4HJ!EVer6nDEP$YZbQPsY6X{4`lqMh`gkF^@ zT?jQaM^vPXG-*M)bm<)|6a^s=dUrwzgeE2Qa-Ph)_j%9E-uwIcu=(IM7Y-);*IG~c z-S@Nd&u@dm?eUKAU%&g4UJOyH)D%r^Q1;^7o!>4$gFkAyX#?Z0AFHZsYMTH3wKcd9 z2-94Q$b+v*lz}^e|81KV_OF*C z|A8PA;(vb?Dj|aZ^T$Z6|NcJw*L!9X>wmn@%l)Xo{nZEie$>BygUiqEWB>ka3jX)S z{`Rc?_c8wO27z15{(m`VMy4eM#{*&-5;HpFv0kXxNU6Yejqdbzu9v>J7+O6T4T7bWa{k$eJ`)6?=RAOOS`1q zi82B9p1a+%+_KXKw0HgyGoNtw_oBN8SC4OPrJMHVnVL+Dv)}mexn05uzjEw|Q7ame*pcaBoL-^qJky5lJ!+ z?s+F?Efw8TZ9Z(x&|h=7m@)pP4%;;Q<(4$31Y-Tk^A>^I5Bh$5@#J-)IJ+PPm#!8l zw+_{1uJ--*&oHb@v_RN)^ucF%x5_<;>o1KlR0&3FG=6>gI#>+5!6jgf#6qBD-Mqu6 zVd{uUv7hjIwX<%tH_7d1&?3m&;9b5rW0-VJy_{L}R5IxkOFxQeh)`Dddz{*?DIn>e zh^GGM3r6Zy6Qz=i^{ZBHeQ8S!z5Qo&*yPa@6=(R#+Zo!@$-#xiL zH$h0+2^ws`8GVBaD>_II|Hvl% zy1_)L=Zr2&SF4870_TZybF;PqRr#7hAIM9FVIieRr|Q}k&!^1z&k1B>MT*D2rfm@m{qw-t$Y>#OE*hPiMW3 zGBC*IMXA8;KF%ZoDbL9N`nywW>l39GWe>S{G7{$;Kfiz0a=Ad9A5C!o^Eu0G_3;H7 z!GfK}`HfYZ#+|t*2LrS!Y@%tijC6H-OS(CATG+gLrizUFwhYdXtx%jI#f#R6T*5b> z)V6#1-LSz^1hzkyy1654=O%*$1qF!?MY^j$$E0~b{2a&8ugTkVaM*8arpla>J@Wb- zHs3ZkW@kc$W7vaDX*4)-UMkMieHU{Mi1MVc@8Q{B=}Vk_Sx@U*8)BYbbFs zbq7u>bh0-NXhy9LMCFm!+?lz(brJiC-uCFw5PRxTzrwDdxSvmO`D}os9(57nWkj$- z>)ix{%;6fJl4jePg_F5L?Vbzi7;ba#H5-5F)YGyxS$;w4#+854mHWDFH)j_=sPRQp znHiewEeq{o1$OsxA3o0}DpIsk5IFqO(2*NMsFrIv$FXIhQ?1J7-+R9Pp*Hk}n7jWg zg@EkA<^Q^UmWnm{Q51rvoLvI$9$p+(&Kq$49Uh;7^=2-!4?-&~wy^oWS@v{cFsyNR zfTvd+sS*7Q9ci;3<;S1;ZCONFqeJp*hZ1s0bX@99g4`Vw{b)zc5dJ^o_Ix}o^*?ejr zezV8K++{-|ZmUj{^BJ1@Zx3I`^$6YFFf@hoK*a>i`G4B7Elf0TbS}4lYM}$KnfPPw zTRJ8)adRXrE6k#+wuHIzQQkq+QF=tUJ_zOavGAAY?buFhnm5D2FwhDNBfjDCrxK7| zrzNpkyni%)y|H<`OGQ<%cB`D8YUI`2y8V4ij)13V4MYJ<)SU<}=GBsLM|Y>R({lZ@ zNNY-Iz#sz%C0uK#aIv4FlQ}BUL2HP8Rbz7WxaXaTLAG<*O&y86x2suy-;(3Oc-(l5 z*TpTQ)~}u;u@a^FQI6k6HH%gB^-9K{4?5Ye{YjIpPI@Dp0MGCBSIve?sWK7MMuz@f z-Euok6-LweGVl)rs}1uPVBJ-Yv)@^beuC z=UlFz_Wq$|E#nbZHoyr9J>Zd3B$L9rnb6l7~)ityL?GJs7Z*r>C{iX zem^lQtiqgPZNHLnV|L-NA5{|x!ILiUJ|{WZ5sR!7R%5Sc^rK#mR50C8@Nq;m_&YWm zb-En$3Z9>}v8>;Gi$R(%N?nQZLr>hq!)f?Q2~VOi$or1&e)(h~zpS@4aAg`_X)L$h|Fp8f|tjz)kcci@(MC9rnG$ zIXO{MpK%%MzV-Hx>cHP2@2}nYEFoiX(CUQ58U?IB^;Q|${U0$oOaI^X$A2?RD9ZwV^8R(9katv zM%5&Ti}<;fKN;ypugczD`Y~VDzGR?uZqKMaAS?g4TcoN@(z_+1Sa$yPu8>r*lEFeuGRg=ua7fa$DveDhxoj!nNFjQ;Fga zjTE$lKfM1<(_GHg`s6cl25!IoZ(5%Roy>N76SlM5v>`@f6E|9z1O=}N1s)5=*bwEK zHrqZZ$Vc`BhfEU8+E2s~Wj=@*g@KWlM7 z%kiw{Vy1M?wWy1?TnXjL#fJsrQE`$AkR~-*W0?|T`Cl-*hV1@I@m!(xs*M$uVdcZw zVDn+`PmHE@9$pt&Z|F1_x;BNN${vC9~f z>b44*ds^z92b(O3HM}SqRuPZr_<;S+8E*3n!D=b5yjFic^7<6qR&keIR*98wtt4_M z{It;cV^>!#c4ODlG3!oz$LlsfSGJLA%=EmLmfx<1kG(kr7Yl=PX_+;KZXRCSRL(p~ z>4W|hp%K_y-s2s6xYXvkrmf=Pr^Aso>2zZkj2Xrlr=8sG6U=JQ>tJck z*YNN8BzR=0*Ep7&2zYi~Kc0zPLP-5{rBYYjU=6=uJt}@SWzBPzk&5PvNnT$dx{wdb z*$enHMrU{M&I&2m^Hp+|MAqU;7MZoyik&aYpHHh5G}S)+#0h6$zsGXLIbznw(Qp*0@eJ2Ts@knLX8X#)pPYBtZmgWqp4Hk!Npmbb~$ z=M!_kic@iHe4p-6nU+r6ib{yX(jU8!bUv_)%%OL7lF;-LUQyvsuh`)xMmL6xv$uz) z8evD$`yZut3w8x8upvF(q_n3wjOIslw>2IdQqGi!-~99O(Z}sLO~;Y)sEI`>`~&gG zupv2S{0+}99paM8?vQxYE9-1zye{#`Xphze6^CVgG!&Em?T_2I%?Ar2ZrkhR1hY(H zbCM)Rj0y1wG=XDxxZm%H^6Ys6X1#Wr-A8q| z1ZD19y_fc)aP*RC5NwkE#E9|0UR68Z)5xb+k|HL{CPGzu)u#5Z7W>wTYMJ9#76x25 ze?=zL3bd;>ir~)=O)*`Tu2h@kQ!aG4e2t1}1y9VEo79Us6D6kfT-?gb zd!+rscLYha&iKxNobRmQ1@osi5|fg}16ih6V;xH-t@X!`+zUqp_JhTlD>8a+Ut_Bu zLMgN~W><7wYwsTMPU9Lll(x~lz*rwm)9B+yTsm|y-eZ6!A{)v`3zM6bhRv^fN@$fJJTkT*5yE z^4s>d;->rh!G%kd{Iru?b_;78g9E738s^k$cSYB)s|1x-=^bcQ=6%&{nrG|z@CCXD z=eoIZeTD&4)%bxIbU74LzK`6OAQX*%Mt3Kn%-gq@91cFap|-zV7la`e5lA;l%XG>q zq1VihXI2)n{pd+(!Jzn(m@TVuPR$W*ENxm7nwKv%XvF2=A0Iu=QIzn@6u02}${cm#7b&*wvrUC|0Qn>-zM(*)(4Bg|kmVkkRkjXRF=Sq8vcA@b)?p z9rYz7rju2fq`EVhmLPM?FF0(z;ddV;R3+PG^yTjtru3>XKTvaHsqHGP+#fFI!KdQ?>YTybk+& zQ2mhY(an7fo!$9dwdE7ah29w)YKu~l?DV!WUcVb1bn?62dYC24zw+@)%xi&BH-p8h z0<2llc~Z*BU=7u=ZwY4li*V}93LVDJeFpN#)jpFn_HAh_Q*TicBp)D2Q@@{ z21~;%x6s9Li3KqP8yG-dkcwzGg|hBkO*TVEM_@uD@orSE#PKs3CIw=Xlg;YzS+}F2PF! z{^-dQdQ5dWExn`^*^PL6S^aX1h_`hq{rT4gqV~OReI(V5i4^1JO12yQq04*;KfTvS z*!EQhhD<+b?XH*~ESOnL3r`7#X2+mz?M?SYOB)-+XBWdp?I4%OpBH@1JX)-qpk&L+ z&K$%vJNvDre*ZGpkxOHw#Z!Gye8@V%+ZC$W^Lfm6 zFE02WO+Yb~iL5uMa6w`wUD}ZuT8=4h)29w^+8>*J$v{VDDz~cCVKbH1(PEGCD+=}>GnvJpS zr7~MNs^%Y+Sindf&nNCb{BPGxBSA$ZJFP=vJ(=K%g}0U5!u_b*;EW^lWQK7Gwc$t+ zZeIcdG3MLSbivg1*F>pl4*xop&cTim_q^$xv!?YbS$%I*l6>*RIWAeQ*r_DoC zze(Y-RqWi7yB+BoV40vr+INdm#7gg$Ku1U|lFO}m0|0sQs;8V(i@VQQyG*3-@*npo zb&Y!6V@|myTlk}DS(K(kl914}9RfQpCe5$c>Z5`#HgSD3067$p$so{5&=pg7so9cy z`)!{2js&u%<}f#Js*cg}&SF#0>lbGk$F_fL_4<8#|4IASAj-W14P40L9uyB;P5N@K zHukbgDN;7J@5A^y`;UHA7LQsOS3k>W@kLj&n=v;ch$wX>&yw3=MH{+YIItg9)DlG< z8H)VkAVmaerucq;qq9tVB4P6)>6Mw-T?$3fFqP%&FFxiAiFt6UoQvXZTq|H(l%i&< z7(k7dcY0l?(SWKV;LI7NSefx#Lgpr_NoT}EHVm1)TeBx%`|Z47d(~`|;$hK`TYB@R zh=73jnlsWM}x+?~$UR0D>TR1#5Jr-du zrY}b;G{hsW5iVnaZ5`ne)N~E7FYFd^%z{>VW%6=`v$5qu+B1c*MJa^Mw*fN`J{|){ zbHC>y^4u$lt*0Ik3+y|P``_Y9gzP6Dht|6aQ->{w9TzllGc{V*dyrVeoPX%0HRkU= za777r#~()&WVSrwT1ix&5L>92TkN{8^mktK;qZgqq7-b#f#wUQ`wM`1(8Wf$?}aB_ zFt6-s3uK#cwv*bVRY!?PhioK2Y~O+M*onr5;KE4*&qWD4!)IFAW1E4g^-w>znvchu zSwli5R-VH9Zi#7Qqel_I_~`ac^w#RjZe7CQY(AXpT6wa`z-ol0_03LcGVs3(yFA;n zjI@6|*NCoAtTtml?os+GnLe-h;4LODRn)2m z-I`rQ9~d6EX=gY48wpQ~f2f$?tgD=~+8=0D7 zSaHpFF>+*oD&9;2K+@i8=mqkch^=uqr)y6X?Ne(HrDn-rw>Ef~SNFD`&nx{RDr*_* zcSKSWFv{1L(Gs_Cl+wF0ku#IeUA&zlmp3j(mH(Aou-{Pf%0-;L~>cTzV6^xmM zd)!<$qxn(5&I?M9LFRH7ge_^Lz$_pj$Bgf-*7-G?T-lA+A7}sgmIJ*TPSWv5h=oKJ zeOMOdOrx8EY0|m0x9-v*WUd>ecR@Ab^Tem2x-Mat_Z zRUmm)NM>nwt_B7j=nMF64(`67AKI@#lzb_m^*=;HWzW1Uj_M>Zg>Pn?RQ6!{22u78 zb;8vb1rf4krX7%`^4kk)OBxG~e)kVzafY^e`Rnm+!N5RNx`RZB!)BSL}s_V_W^p|gs-(MKgN#@Q~)eKQx z;HMCsov7KF!prMVCWmEn5(wf5(A7F-h#`kT|k z|Bhsy?Ee98Ig9jY)7JeK4`^<1ETy94ayMeL+p$2pRk1`7m%NYY?y2UVRtXZ@AB2&b ziEB#^IXPnyJU#}t&b1X6o(5M(p#=-(>h;=64J3IxuYR8#6MU&4d}U97fp+$hQqqaq;e#IF5jay)F7k z604o*PpP}OFq*)NtRp6t=@Bl0y^>4DRccY+czAFl1ytsf(R!AysO_GG+jqmbUgj=3 zn&cCBOj0W^(8z;q4{Z+tz z&%`_}cGghzxX1FU89n)NI_X5=M}D8}LY-J+-=n$_+mI7H^iyqedlj2+Ai-&qRKj=n z(%y3EkHLVnp z;dRB;`0Q;vJfG6&1E?%*hH@@&zYy(MF!{nMA(wBK?&8qOky9sw59(*L@s& zJWFYJpN=uCW^qvN7f4fD`Cbb`%(^r$ZvKNztx!T~9X;rNH~Gdgd*S!K+GJ4=V8D%T z(v1}x?6OTc`M)NSQVL1NB=ZbibYR5Rvswz|ace zSr)zt>S-i7(WSlh!}nAs-$T?uL1(9Rs=V3KTyM>{oH{j25R=ytq}Z-H>Xb(D#~x~^ zAXvfW_$Rxk6?5)rk}WNB-yK^9SCY|LywN)#1q1ALzG%dQ@Qa5n&qe$`!xlEJz*lBH ze|0mg)OTR>tA}Ph_Vucu?f>NA{kr3X! z)p{+*u))Y}DW$tSw7Ev0NYtuipTR@;;};LF5+b?UZVl9z{RXhe=2S&n(xst*`1mGb zT>bsS_lm12RTjPH?07lW#Fh_8tfWv!yhX*VtiYAh#`S2|i9Tqr?K~wy1t(sgj>;Ki zA9dT9YP$WgL%E7g^4pSHRyZ?Ue+}igjK?*n6^B*k-Ah>m^DRZu@D`;dVzvIROFou+ z+zmu1wK;1lR(xN6+!G6**Okrd1Vrqjxz)$-b>grx26r+>cE1u;mLM|Q5Ek?GbqqnD zp;(lHRCm)@_lE8-%%)og0{y70A&-q8qO#Xyoj!^Bq=#x2Go>_+0HWHkB`jlrHz$~( z$&sF24l3)-oy!FhM*3I~m&(?NQ7;%$MvtpD$G>D*6;+nn7rwe#_`ZKBzVmuO!k`T^ z^hxalD0iFv#S61%Wfxbn#`DS}!d*agQ9XPPyfMNt5VDMFA0~I%r?t&ej4=Bdr^O5< ze?f4|+L&hn2Un$IOZD$Zy&_6g%}@?{IJ5z2jt2Ds%A>s}&wojN;l(VXa(1U}X!y0N zuohe2H`!~#oNBwV8kTNZl!eVYmzRg}oE(W0OF+G2^j(BYG2{pOuQk^USXV3`ONd{@ znLo{!{>s^QJ?)Atb$yB(h#qmCN`V?jjYZ_HI#!QTZC-BpOWSl^u3FefyCUt1YNfSy zxes3kq#y0pF`Fk`PVY9yeuWn63%%2UtooiO=KUd4kna_bG8>uwq-d9OG#ut@vyVKd zS&9!X6>-_vA=A*+73P( zb(+bXB=Z(Cra&|x>^yhN_ZX3l-JvdLlf$54=wsfvnCTo`RwoUmyeTro8tx482Dvl< z+5CIMa`dohf0IRmMzg$&v1=sx%%qIgEpDr0MZWC0 zI}mF3PEy(Fe;&JPsE=jRt?SweP%UVDRP`ZiHk)f@*wU(#Q#7FffLhc!=BkjX_K>+isY+ z(4;nr;PXERRixMv9f)m3(1`2Ku}j9^v~K{ItmZ3yo3`ub>A{j_org`7?(0v-f0qh0 zC9JWO9Pvb05*-vqbMkaW`ZMd+ap#3AFC?s?HI~ng62o)?drOHe+e>^!M%Gp#6(Hys z^s)*EO58+fG9RpVCB10->qw7Lu^3vKMuqf6IQo%QTJ9gi!D%<@RkeVP$@@3PFxs22 zh3QKLB5ZNRgjtWL)J-lVf1|HMaw?p0M_nN|Rpeg`y^B7YwqCv#TDSR5Ya*$9m}sA@ zNMa3=@Wz|y$`*{pngrEXFCA(+jAvCk*&YOB3R3^KOO5ni%Mn!;%zWT4uqq!DU*sSt zf8%Qz2e;4^Z{x`>pH*!{J2ab}UdO`qYZ+?QGg8ljHJC>VdgcxIh*BU6@}pOY9>)Ev z6HTO)TFS_QdF|bO7O8NtxtG+%r9=gg9Uzz!kNa!=&*Nm2c5y}fXDce+rkBq>*Fd!Q z4Pm{nDOi0>cJp$a$x)u!rlYrPJ+PlHEyY;+5nUFln_mVYQ`g4=nf>t41#zD5g% z;=wEVMX7->>DflNtf^1*5RhK$`SlUz{^ZpVan&JJa}3rMts5pPT1(Tgn{K^X{|MW6 zKn+9Qs&<-Z94FOn8^&wy?yP#)5xyZ*MH?z;+s-0ID*(?hapR>!izC8zdVnLl z)c#FR8RawwJ@JIRqwwj z1#`<}&>^=5UCXy4$0qdF+^Yc=2f{Ox$Mab-c>#oSo3v}5nrJ;ZsRBiWtZ-J_g-LPy z-m*3Z5DwXUK#3DQGup)VGs~h>+fZnk^w-brZ&F{Hp{MojY72ZQqgVn%@SRg(4z<#| z%GhDI!mPnK@A$gC^fc(ri6vvN-)Lnz^=23gsYTh}BRE%@VDp574+;V`CK{W^V#fhg zQ>aP9hHGihD(?dRJ39?B08`+8Qh~Q)(az^itfmp4{cZ@>z8QnO6;y@R*pJQKGAw0P zDj7eigt;cZ@=|IUO4C=}tiqC#PKfsOO#H-Zr7lZWX0OLOzgjS7&{R1z@1kY>cT)=p z@AjvY227{7?FO?Cw$lE1NNWSwm_qwH`zWqb!VH`DoJA%NX{Yr+t_mj}80vV8pp=OH z#sF%&`Pu+^ZNB0no?W{-xRbq@?eJ|7W#qnWr-*3Zaal0d#~N;S+o?_Yf*y2ezFXyj z$n}`I-O+AUG-ba)t)?bbQ&X28_{tpNi|XzShE-XTb<&84<2pDXJ1>^X^-^oEEI$T> zlTAv21>~PFUKko>X>C)X0Zgf{Pd`e{8Xq__?Q4dm)$fZ>KK2|}HU#a{HA;q8<3{=U z-fR?ZU}A#ymdy_XD&Q`A?+f3A2owp$Y!kIo5lj_+so6rkfxR8R23Rc{P=4BEEBJhN zE&}h^qe=w<6|&3&w}s%Srr3myolTaYFv<{uz3ffb`#BXf(7rXgF%D-tJp{e=Un?^{ zG+Ak(QU8&&&nlXWD1UdaxXQG_FpVhHrmPqZfHmC@fl}BOTZtOS5YA=^UbzHYcYrXJ-bZDwEh`9hA*3KL8cpm!9I! zC-k@e_dU>^q;{U|EWi+zyF?H?`gaVEAgYPu?CwRnak7uk(t~)61dBJWAOR_=fw2lM@nr!CwTF997t%~6%iRIFnVX}LM-tUe!RdO z6!r+Tj=?k$e)aVsnFP`EBZ7*8#*K%6K(OZPFtp?t%^rUW>gnc_t|=XgXX1l07x0-& z;@7;0B0@#YIni*;(}^z7!Oo#$A>DNNbt;(ehe^4PK;d zlkR+U&K1o7{K?sTL7Z@@`^U0+k(vLKPlFaRcrYH}SFd>0}{gHK3`8N)B!)$-K3D3%6{FLu55{B-(W+hC-^SEL-wgh_|(4{KxdQg7D>e6vdf zyTGP~t<1g=TRvgfRj056#2%o9dzv)AIi-k7^1*qjfs>!Mrr1OW8>O0GV*o7??Ymq_DH@hsDvP#uvftv-8ec2ZHq4~1Y&Exs*Wx=1;6}d@7e6E^Wmpj5XxOW1 zU(HlP7{-03%N-9zrfH6n4n9~rfPaK>x@uFmAOubQ4+Wp}gp3^0I-BZWgdOJGcHUX< zh6ku`^D&t_mS$cgFmZk0yhvH0H?TJg^xr`9{sE& z%f+a#FZ{%oKEc7P733y+r=DUC)Mx^`oKR!mQJDV7TE(}C<9K)FtXeBfHi=a^JhbDE zIY8gD&K*KmD}Z^E#baC_ZT$ogM%Pi6kuq-w3kj}e^kk?a#p*)@_mxur2)(<>SA9Vf zszG>LyIN_NeLV3f3v)jHmBBRE<9!i^#*sX1gB7HgB`Qq!z}2QgpixF^TX|rr`I{s8 zJ`H3;ZihoXyUEd~^4!anYrLAP3F5Ma?{#*ctn|(C9E(}^d-IuHtM|(t!Gr;c-Av}B z2rQNn&3O)7dr?SN(jsRYBHuiAa?|0W_L;u~m(u;4=fSAr_#QJ&TybvAZQeTYSfD>6 zG}({&&3~~PQWQkri&MLxv+~Iud~yY$sL60IAa~Vgz#Dt_x5)6<@h5sxUSgy@%hrWW zxw;yH0S&n_)PLbII1W7dY+E+^XdrZ91@!;UuZvQ;Oi;4b{lcl_fsA6D4qE?+@6;NY zF?ygFQ?RqX4fVZPS)>YyQ5|Au>Y*s4@5;Z9jSy@c%?GcJp1M~ljHldf{k4lZ48k&H zL?|(P)J%NCxx{lV7?dA+LS`rwZOVSniOB`oNer{-difNqn6Z6kydQkXz0 zX{q+H(MnufQ+R|)8I=^AbVP82n!J0_^@L0Bern^f6BNugE$N3elSNd#Yqi5%z)1#BZG8zw1oX^3zAydL1E{QB0x@E^6oYhI zwpISN+3ibu7f~(&lC&hq=}NC5ej{1PETz7=vf@-9YaLqS<3gv~z#XrJ0Jpr)1@KGCa_9<~H|54^%W`>)$3hH? z)-!&8W_CNw-PN!f9qAp)Nt6j)Q z*216EuEoBKv`dPDHFF(*_m0U`h1#S_{Cajd+Vr}5n6QP%G@Oce%XWciY~xI&X$N=xW?(*Ruh-qx z0Tk%}ZyAVEAe*fcSBe9Fg^o3(mrU;s7pp@p`2-)@<=uuQjFGh!{zrV%=!OshHoSEz zX9LSgBRH2%4dBsPC$b+W^BAP2JE+DUGY=sqLiM4PGL1;9Uq!qX97KV6qR4v{bm05W zSF6-$09>@>T~Y5a^_UCg>{b~IG?@s!y2-(xS^(Zz71mfg87IoKaaG^XU$n}1&nza8 ztxY(8Gbqp9&H#R6C~6Q17+a|b>0@cHbc3l3u`XRdyvR0t1A)Zq(Yw0x4aJx+_cafS z;5Y02O;2eA?bz+1rD|oc=VuYM+CdZ&UJBpeeGjsG*{buqeP+Fi2YLZJ&tZXaqgN6Y zjhEg(>5gBPy@_JYK~aT2r~ZnyqW|6FAJN&ajWEGY+g0Kfu7 zmGo>u=)#p^q%l#dQvA_SveYtY);#{Fk!e0(1&uMl-^Zs?Bqty;%PT9ES1a{xHN2L! zb=6U|J$&xcf-x|%Nb>z|pmbXcx-AH<(#8y4x2)-#Z=n#}{##e zEWq}HI0Z!er;=%fe=yIK6v$i(uPJO*kP}c)_H1>5qbCgT_mOA=*q*a^J@p!1RqQB2 z^6)6dOwX;9!UiASnH=b(87akNV#HGdH}4(3BRKA?M~b#ff-FftW;d7~uGS+> z+!p*Q{Hg9mQndact*i@!%VXj*I?|m9jBz84QM@EEexqMl#KVQ*%}NiVcS09f!IrTM6dFxr$BK@ciu-V0}g0bTIWV8T@b>T&A{xAGZoGq#OBZtD;*pStc>NXCp-2wr0 zCyq!KwV|Kmlq{=t|J5ja+Z?v{2tgZL$}?TP93Lt2&y)8BTzMd0aI3edvPraVbw&El zsMwW5^{yHfJ;({(Dqe8zw5Gn?VvA#0NKG(jT$CE57rpXYny0!4FakhBOyK53n z5VkrC;tS>PooA`@<}bU0Gspl-77nw8hCtsO@yO&))sm@O61I~PKyu4N<%i?aNZ2A1 znxbU#T9`8b)OdKJWA(rTxGloA8!3Cwuf=mJrp(2v2; ze=Fa8J>2Jq>$LoB_uL9%RbE0`+IpH^%HH_O8J29U*$i<-(4YId{*O|ybKm+`bS(B? z_ywvE5uA^3{m6^1jBI3Yzp9ebUsS1x>=8<8;r@X9k)rt>>0 zk8lwSl_c)m1!iU=L&L8e>wmMVrbLJgp{fForyz)n<@{P-Cy3rZ!6LzuI@-FVdWEHV zFL(3I5AdUuxks>jX$s9UkR55tAgRVNhg*@ENA37|uO-(8!{aMNw`-6M|{F&1XFg7E-o$s5HdWl*n39{#9>ULh78`4Q*~cdDK5_R4Gt`Qsw~{iQ)Z4@t<{2idTDhRCs7?SfF29*tJ{8jraME%8gn ze2wmD?8F8ndZ|3*`LXQa0pq_c*gVAUSQ~`0$))V6Ba4IQ>rt5Ks!O_KZHRR%FaQoD z9<-cc_=aT_Yig6`S~y(Q1;(X+96LD&65Pc=PIBhHr{L0a^&9b|bxT}^A9;?zIC22i zJB|j0+8%~=3+w){>ymQc%mN%e{T$1!UtN0fm&p9JvKm$^5!`6`iiI7H<2jNW*-piW zGf79tmP>}2VumJyN0y7SC*qE^Smh5RUVbia*aHK+qhA_WjP37jm{orKzl-N<*t!a1 zA2>V9-km&+JI!b9f|yP!>J5<<-IVP;-;VPJ8&^f+polQDJh$;f=g0xZBRT}aUa|;e zI?Vg|DF#djYe+vth?4$8(_wH$!-Jc7YCx2}c;bs+g(tABqz)&L{;$ofQJ*2py3Eme3h`vaUz z{TbO6mB5umImVq-og-d9Z9Z-*kTYsjipo3nherkmwH+NSCo22AM$6Hp%k1}9k3QLm zAymJ}4Fa2t^T(E>lA{H-p5J#j4TTz9v5Qhc`V4AmpTI#-G-;&gS{oh5*iTBSFEQSF zGeE@*Y8WmWF%$cAOgXs7aauT&h%Y)V7?wQ3T}lyA>A{_EwsyCJ zL53anF1(XrQMhtC1Wfb#vW?h=Y}^UGfX!@odgBVeHb@THnk{A|)-|yimJ249oqyb} zDvZ3W5eM~Pd@(x4vnBZ;=^&FtM09N*rKt~;b2b{W7Niv3+AYQtzfKCutjjhc4c-wwZ23(P-MO10`FV6 zZmjPiBFI>mOuAtrQbe8Bj3bSlJY?)TW!WK z6PtbqJyy>#A3`0@6Nu6P^4>A#5qc?Q^LufOEt6k*$gQ+EOmby_LuxSrkP-R)(Vskx zY`*3UyaY}E+Jo=0n5V5k;0*6e5a^e@IaKW=ss+x=RMoD6`zy3%%^Y+mo>fp{D zi2#x&X9>9vSg0lP4qE8sxmNi5i6xZa!fPnvu*;I@PmTG6cY#=NKJm3NUevC-?vCam z(+)H1mbT!tf=u6M3Hs9^*J#GP3$u>Ga!aNA#1C*Q!30-VHT3n;>x3=Kd5Mu{NrnkTEz-XOz#>!%>~#tr>1R+HZYBh z(TimP-Hlup{%bAq2vq^8^xM-pFj!+;$LqSLD&I1(vau;MtoBw=9;ou-20g!B--0P`P!(Icl50-JIsX(sewU?I^Srr=w6d-o-dJVyS{J%| z!n&EE?^hb#CR@%THZk=rJEtXKo+BrRdhkt}-(K0K{1m1s zAjh+^xF7Wl8vewIfR!eBoJ)GWkGFkyMujgzxuK%sGd@J=x?;JRA98PE-x+B;=CzeW zPZmsZV|Fmy7BLpSFy6IPCYeNHO$YhPX2{9VV@~yf!LyOt7AWgu*<-ZcPktAAw@)Ea zDBSBlK*1RogBNm!_>zf^W|Fy!qDjc&>uLf#7Uf8=zuChEdva>>j%0Ih?WKqXEWVdEX#G5Y1Zx(!(`Y-Q+lKAZ!(>H0~*OBG2Ib-By(p4ccq zD3*|aaPjK?LwCv66`XD%5?qbH;Gmj&$k`Q15AMQuQ#IfOC;R$KuMn~lU*vmA1UQXk zR#&+6qxzYeovLN>XJ&r1-}--d!B4h&FmpzpIRH;3I^Oj6OfIr>W{Q~yTpVyLveOQa zeVu*W3y;P-$$dsi)%1cGG~|0lr#!_ zSBXQ<2mSXV(2X=m?hRYOlg$SoVxuC<5aR9kj>=){N>)IEc%q}cR-~8iK z1i-~SiJL3Y+z}c&0h@Nn1l!*qS-Nh1bAC9vxcYS}NLYZJA&$x!fQ(m2MoW%~5!%#9 zDX@X+T%F0@2Z9Fg0PY3BNhFH{ZUsNlDAMa> z|8k#!^b4!SOaePs6(=I6>&ays#;?q}b?WH&ns3V_Lt`y4+*Sb?+EtciZzz)_`=@(#n$3nk=0f0wiHUy(3f-aYc>vk13tJDA{BRs*4fM#~phKQ!3?Ut;c-orGt5d z@x$LUEw~&5mGG#=Y+FZdAis_?0K}pY^42z7r*wIg7Dm|R^*#&=%PT*s(YUi~t!&s9$mrK}f6@S5ygn2L&^T@9BZlJVy_B^e;~JM=|Pqfp>X;1OGCD#!iSGJl=pYo3>&c z(49Gw-aW-7sL@-In72O6FaHn)ZyMmtmePE)QI(XKPEE5nFeojFCX!T-%+@(=UyLw< z-81jGmMe6M12mE9i)05>KioGi5x}Ej@dck2nAlEo4BcfAd!r_47( z@cp5S7-+-XJHEJD>_eVtvCVZ&q@*B^4dpGX4P|=$9s~`fvdajj(WCt>{d2JQ!gxeC zbiYZfSGX!2s`JW&i^llPgfrjrc#K~V)_ccy9T!N;O$aAYlTqWIf5@U~(u2YjJAOfV zSG}-BEu*QK7iH~=%=u2KcuxUf#E-;eBp2?9~bu04p58Lg?^rI2XYSI_bwUJqI8TN-8627qku zY!5>%O58DW$8~v!wA}X4`?O|rM_K_vTYH#z-7bllTf$`b_F;5L!KHdX0t_I6X$Rb{ zrsjl-1l@y4Yuvr^+>9Efdl40?cnu}T9zn8QiN4jE=fy3g7aKlFmHB3a<(5LJnCpg3 zJ`B1xihDG^tJ}U1|B8G)K&kawV5`ctrXW#{ymF%qQ=*hOU9jnKPJifr1@su{BcimF z*ZsFLXjwSuR_`2$U6y6EOS}s9p9PEptL<8BgMK8x_lAP*gxTH}Z8b*@g`lg%Wd8OO z2>l&_bP|pD3))8sfZ;uxKc3IbMw2ImM|=)+);z@PmP&!F!f-FQb4pgJMoKY`gv+vC zgm?pk>=o$5toA+F{%+4UI(-GVXSQBR5uKj^&;a~;Y#(ogoj3Fce$mgaZ7_QK|?87qOBsmk~L zjVjrUG2so>n3~IhG|0`cU-I<==Y=hcM=0LWf(KBo_|S2IdPe3b1&zeBbYw&rxO&Hx z=)$yVY6RsA#W(zdwd8p)(`G_N)cEF0kM`x`(Q|@=pqKR;)^*|lbA*TD>m^|bI?iQe zKnz5nZaM*cR@t6+4wS(^9t;K`lv!tBE}-R--N8V_RBm=;vcDQg8cHXc^2~d zph?tcXc43TWMLPTm2Xn<`vlnIz~3VIqP8du83V)^2GAO&str3OaUu}vD-Uw@Gl%|J znr_b;46zRnKG@JFBqtS=aLHEY|1kmFy7`Q1dv~kn!b6gb4{T1Dlo_o{m;{R{`t~_j zI-|N&$hwBTQHua|M}+RDu0-XbBc~~r(Q>)|y<;3;NptYfH9Y&N&Ajsg_?VPh0slY0 zcGYMv{zUQ%EDS%DJ8+Swei!0loU^6K4vd~llOM_&-%PWN-+*|>>6N87mFomx)~Cgw zvx)ksI@}20NsitE4rGIu<*oh5EaT{WkEdB4EKoaxY;l)1? z+ymTR5d$|=lud87rpH&wJsvSSgv_(HB<0Xl$9T!q+J8={e7=1btm;z`5huiv z-#Hbeuc!18&8R83zGqL)(OfHi022tLvl3%H!#!Jxg|k2=D9IM_S_6_f%ynIETW zQNMX{zo(eYAUhYUu&lb4T4%jzT-gluZ>o|Fp{l>*g<-A&>bp15U7;~Da6tg*$COl~ z+h0>WT~WCF?VljB2IX3S9(>W3-;~fwcC9J3f`A@eIQrm+?2Q}z7QY`6!A28dHlevJ zQlpo7@n?`5$QOH13cRv%rJ3kbXEj*!;1&Fg5g~Lz4>E9O?_#rN!O=~s&_^~~z9vhr zH?wngPg><;kRCMrE3EhYokF6kz?ja`1a-i7z!Y2%=g%7On@ySC583q!QU?3y01^Z+UEH1>D~f4lvZL{*-}Td!Gd6Z z_gz7KJ=~T8xH)=RdzAX3w7dFOI9jo~2hJt>CLVJg035hBIapz~HK>a_glr9Kss~c5k54Va#lE8B2W=O+U%?Rjwi$gxm*xk6quv+NDBU23QlX zv#RK~f?wWDkrK*5Rf7Qy6x$YRN>O|8-CVYwk#wDd(e%dRK(oq3c%H4fX<)Rt$!o~k zmJdaIum=7CjBlqYwzAsYegy-`i*farpi~6V0Qda3();f0h(Dqmtk6xK`O^}Ngd>%?E zynmPfvT4^T90>87Z-n(5lws$tT@bdfWRcDwQXZ4a4_a(t>ZNLFL^)gr|2Ra8=Tb$f zWI^j)NzgL*y6pkGLQbJJYFb(8ZsFEJn4S&6yPenMC_`s1%Ui6?u9jD?+=bEl=;0<} z;d$;C!viqb(08;}#@6mxK$!^Xd$YTfap?KZX6w-Xfm8fS$>524cq#<8DY5$f18~EW z7SFcz!QATXIf2M<0b@QG&JBMCP%$J-8ZgdMRY7txT!e66LB#b{{mi#7f>VZ--Om-j zLixKiJb@a+Vj+vA(BWB-epY8ksZET3jG4o=G&aDQp_Wp*>H*U|xvLXFLjQ-o_Y7-t z>HdY?+pTOxMa70R8(>2eLc8K4@68q>8ubo@?P5h)?m77Vak6w&NpVs;E1HB9x0@6` zm-1%g@f}gp5%BWtPj|oR40J8IE=!kr6k??3{3i23O~tXg3_`!2LD4|h7w}FN$QKWU zRyRN0hfbTHFBvozFOM4i)1-@rDIsg$5=uLG`}=9=7g|h~%N!cKO+j*Hy?S{Y`LlG6 zAW|q!#A;ph+MuZDT-JJCI;V;%VBuSlG5a=AZ z8pY(FzvFRZP}$@{C$`r>-j?5f6xfzS~sJm!BN&QH@G4I ze^cWVpEc(BKBH|E|gc?>0T3U^@0o8-jL25zZ)*a@-SH~5S>g(&Ps+1S6t!L?eaChL+ z&4MNInxJ+QyqxrSVG*PK=;93=*%GMFaK%LbU>||@)ZFAz2l>ST#4iriuIGToG6&+po9v@%79bETnm+up8x0PUVc>;)U<7LR!HT-F zLcyy>-a$=fpdhkQDZtmaSa5`0hc*yo2$0KFppa)bohwBR)MrZz0~xF40adJESl|JG z)E~e}a4i#_hSry)*P`ia~Lt=zW(vcZp-n}ngv4GvFrK% zt6E{=hVVXi{o}Ft(Of4dSXY>|cfrTC#uv+t^|=gssJ>phSg6LHXbXb>>V=yJcxzxw zk6(9hZEXl#QrQ`1#u8D- zm7AKYrB^oVY(NnH8hHHU)`a9JiMd4B$79pNs~q(@Hv(B|{Bndq7^}5{4MX~T6lh9G}>eLk+LmD;~D_>Xpzh@(rBOK`N}56>|U_dVtbf3nhg_l7w3!_vaE zvr-~@yDx}%n~3$TH2%0_y8P(f9^^u;XmW>6_3<~2=P7@#(brz4zjci-jLjyhPrY}g$B%ZiW%}1vr#@%B(t5W>;`h|w6|PRbXQlC? z=-K7QG-Bc6NI>hv_=7!HEJB>dRwrlq?DwWy_+{GLjkwyYeLU9teMJ%74&M7s`k<W-$YuDp5AGF$Gfj-9bOw%+n~ zZH-$Vd+YD9|EC_SV+X8_edzbtjn~Fkq~*<8qwsR$YXWiH#@eE`Q0}URUrfBt;}yPr zx)A(Z6tCXWS28E+;E!D&SU9-S8mGIwW&Ld}O`%;QB~K@Y*BVWi8}GF)@2AzT{S<3k zdHka2@{Vm!&+@M8POZECu_RP-rS<0EvWgYQC&o%;*Ln`NKU!^E)?SP36&*V8ONai4 z4r`kkzrxcES)sod<)EXZZetzFUH)yq}PEWuB(2%J|9E#`nt#)M>G-K!GK{6zG5Iu_odVS17yjEsx!|3R;&}L2KKxa#Xb} zvs?c6yc<@Y-tc01FKyR&@@bVP4pnkWOR(aXyu$5qiIpdRq%5zvH(^-_?;rRj;OMo- zOMgE;dwX@>aPyVt-41V)#|BH1hBoQo4*kL&cQf?2F=PWbbzP`WGqt80blFVL%5`SXV2bF*@>1qB7X;NLpHKvS(jJ4gMWNIHL~s+yqz`cP5blUrMa&DE&^y<{SWxZ-)8HJ zXn+2i*J_VLLF=84za-YxK)c=jm-ug9{mb?hnfX`XmO1gSs;!8`zaX>1iGSgBnG^pS zmKBls|Fubt{3}$-hPau!VUk5+lCg8>KUdhYVZ-Lfz4;w1tilL{DHO5lIf3?xEfd0L zu|L=OInO&(??${>=dApd9{NL$W1YM6x0%#4#12%X`GrTlm7FPsmozQ5Gkwl5Fh8kO z>S~0xVME;Y$;n9v?jZcx&2^7|wn(`w`m_9R>1||1k<;1A+pkJ_hZ=e!nzDoARClpyfw}%&zapLySdka+YhEw=k*y|Bc~+J z=DWEid=32+mS=0W2WbZ4A9lvwa_{+T_}?XM_;;6n>EgeF_ZyP_pXw5?FFEWKh9Tx$ zV2H=L#Gk}f&h&avJA>6-$R1IMiCEO^kWp0q`FjJ+$T=LTzSu{4vZhG^)?^xb!Nzgy z-5XYydhUbaJ~4UMnIU^D#w=oxy)fgk6gDzxMV;LpW{Z3#><*C^ITiFu5p{PN`GY@} z#_BXTPHt9kDORz$;YsZoYs>8;iiP6mJ67{z8#Z*+!j6wGqn6s!9(ta_1#k4%CQoJ9 zql`!v*11m3+{1#S@X_#5Tw(=gp^*cVnV2fh@Jg?NF(0oc4>!0?jzIkEmysv-dqA)2 z+)n)<6%5nIxl_*kq9JnO%ABkHEQ>lA5Qa$;2ygV{ngzJa29s~b>wQPK z&b@?KSJL)|;k`neySp26yM$FA@!+}*babRjq*?*Pn%=@`PoXH@xK*Yek-zA!pluletE0=lVvX?{yTV$0vB{2oyya+KDQ{kd zd1u+t_M9kMwpiZyvq+Q@)>&=w6q!$k4_XtQ8|2E_D>p2|*f9A- z+(sYh1;+Laf%8ifY-wpRWlfDrU=!$a=AU>4apIYhMC(2a{IA-HTo*E_*s?v(O`$ z2*vV@c4KkD&Ub+{Ob<(K6p)+>9}=*q72^!^gsei&o@02BWQYa-b^LILVM>loUv7EC z!Fh7JO#!lAidrgvF9N2|8XpV|g<%aSC8Ui4{-K8^3s|xjta~m@;g!Ug*N8OqLK`k< zow!~UO=mM>SDNW5!5yiKbtc}pX}r_|!9ABwpD-@KZ;T~wA%{KN5sEo9SChCnhChm| zc!zPxN=Z4xcuzZ(+B>aNO;onUDv(ok)=SHJ0((hsNX%n%ElWJKS(r>hh?uex3L_)) zQc>zb_!|AJBXc#?0#0^cxOC?`^liCvMmT&(^NIG@1)Zjn>m8U**4Lx8>-6MPK*yR{ z4|@Y20QbUMxiz(ar^g5O(z{B>on{8Z^=)w6BM`EVQMmKSl7KgQ)JQYqCb)Z_4mEUO z!qXow-!^%c9?_&u7o|k((sx`4A=JSx6f#G?Scuv%!l5wy zyV)&GJLHZtUMi|XO{YBgVXYs#;fs|vtahMQLTdnH>C2{8@ps1~P*yJFE?3!_Y5W;p z=DkF;Q+#3MVa7cP?;yh-M4dZ|b(8oENXpgOmp-ULMlsE%Fkg!0u9o{C8n8xXZQy2M zcGmnHkR@S}J=GViddHLmoMuks_%LUEB9dC=%xfi5FLPk4pjIRvj>n@c@@oNcn~paV z+#$j@3>XuMFBBdxaH6V2gJLc-tR2U;uk#fzu_M`Y{v|HIRqL@SD@*Qjpnp)gz%(Sv z%2D2oCx~Nnh~Hv&NYjHRq7Ig;-=R@Z0u74R<0xb!v?+}XGLj^eNAEMi8ifd+lb?-a zcLtjeu`eXPl}R67f|LE>D4I5SrbXQ*+2(PL>yzo{xKzW(-UGrP1ZXF3|H6Wq(7aWW;NG0*kZBpkaG9`Se!46Ac%Iw0QmJwlujkG>g zWTD}dpX014+(z7FS0U|$7RO>eyVq>_j(zWMN#F~`7<=awCMqHo7MC!NA*^mRJ>7?> zI}QLAWTJa#ZLl27;x_B+p~5$6ppzwU#RTbEo%`U!h0nm6*E!dECTy1LWaA3d>{YNU zPF6%!g_s+~FLABPZ;WBpotu1;9LD1zNCEOGJ%Q^5M-uQY^=R8^q;WN}D(uSJ zb*yS-3kAa7WK7W|Z5XEKDU`&B`2}l*@vk&aTO1zv4LYU~>BZ;FMRgZuTR$vzaXnHH zWxRhlk_Ts;$S#;y;EuUb>w3HCrRO6wvSl4>-mtUgn#2x;lZ;`Y(nx7^<}5W2hQn_$ ztWK21cAQJ$srx#b3{D@%c@L(kOD{Afz9M^;i$WCUqOo~{0_()}1e>^;&l-|nl*5dz zfqCY_bKB5rQRmNJFjaUu&|F~SJdOHu1cFb(n9Sp*Iz`7Q3MjJ= zV2piShr5>8Aa6yJ4}C=&s%2vYBOPK(&{Plq~AzVlzm#>(|Ctc&Pm*=%*)@zCtYrAGb^y|`Ze89DRm+=%Xtc?>$7 zq1%K*J~U%1N9t=cYQ#5G`F~%5-$eB}`^kH226Ol?Di+)@#v%Wd%IYWy|SLyaT z$8a_BLv9_gGU|m`aMHti`-a`U{B)ZgEgH_7Hw<*|&n4t3a5~oQiVcT+&QUGJL$WED z2wW%7SzTX*ua8`Hf_JfWUqo{>?w{h1_soU9*~Ab!y*jm@|C znT({ifUQb5Hl-&i775t*^3M$w_Un?>PQoJ6pN?mBZxs8^s8)1g0>#5aU5#9>w+BKa zA0dbuCNskD=B4BJa^dR(9=m@N_`&)H$2VxS&Gr5edMWAle%q(hf1dpQ{J_Phe`KU= z(ir^b^U-~~3_3E7yt#JkfmPy|nZA})ctWOLfn`f)X{Vk|LC+2SRQvdYnb{O?tK@1j zi+sZe8wqL*5!%bX!%j&*p9QQx%0zSA`brqCP;laX!Yggpuo z^AGx}ei{x7(bIz!T8Iaq|FFuk4WpG4sykzJEU!jC0-typx)FjjsLM-orPDqdPxkqa z;PX3AwApOUibx-8fy0cZlB|s1I8BYTE%u_O-cN9F@#NsMvZ1FZle4oh##;J;WMVMp zZjDSwcWVS$lH2pLO!$TWBe9Gm8|SD5IB6pL0CMWEA>tI#0$reiW)JfT%9^rdk)$j;o6PPd=IuWyV&8YH0;~qAGJ{>CytZ`D?G7c%83l3U9-=+|e#iZWT)glTH7K0%+^7sM& zd#x}tF~bOQ$Y88sArQ=T!E|CIIs)yIuIv-T9rD#^UHqNd#32)~hZ$3PT9b(2ZY9WONXeP6ZREwDI znd2*%ob>rK34c0wu#g`!KhGnPE(oj2eFxzRa;|+hOeZQCKDl1^R?vz5+%VUJvi%(d zcidl@=h7OTui^hehuUZgVOb1{8lP$t!7)2=jO_>g$n-$hIgUSCVd6GDsQSLJz~qFK zI@g-f7hIKyADwuk(`|4JGg1(r?}&RQ3CSjPxHmR8hLG()R(_$a#fKX@R<&SXHK@)f zs>1Z0RjmxET%sSYeQB~FuZ8^^(%87}eXT7k4;wzZ~-q(HINS z#qK&`vjij#FhH}Cjizkcnm+J-5Rb`8E{MrG!w?C$cC2wMLg#htmtMGKo zUtI#YeBq0M1(2AI+Uvr;W^AdC%iq{ZrhQo^S?>RfWH8(0G}euFIvxmoj=dVdj@1IC z4I4gfU|;2!)9NZM*amW6$FBWN-K|Cd_7>8WkL@0_>s+m!0o4FS>qx#`Be_};0!S)S zh5v3kB^lKynI3Vp(f@pA#>m9TG2iR3)gpoo8{XVwGXh71VwYFR@q3jJw#k*UK@#7G zk868b!M2wl*3Bzpl_WPiN%M6iR~Q@X_lvPx*h!|dZ^3TORq@GZBN@92T{TYL*UX!M z$fk$-0yN#X)@k(gI_}>J`Hh39%bu5ygsX*$uJx?fUB|TeW~uWd-7+FT+fJ`_y7?#j za3`RuqREgv2tb@$YpEZ%#&z@hU}60zH9om|k#=mir=5&d!rEvTE!HuuVvXd9wN5w1 z*O3%nB`L>7GG*Ns?O2hY;_JWU=fCt^+svRfk{9={6UYA5&GcRKiZ!q)bZ?V#wU z1G|iU3}3SUJz)>TE!fVSE()=8qJnz+ZmcND!QV=9V6Ep($hrkwM=F}sCBro1{hqEdf5TpO z_wvXp!KAfe+?3h%d#ry&MKpe^$nCYB7o*uladv8rt$Wrw?Q~zZ{|%Ho<<$4vdW^YZ`KSjpD_#?0k@ySY4#cS|7Lf z?AzmXcZI8e|K=*&H77eivd^FE#wu6)*GAj$Y#k%k0M?g{^F`~}sEx*|+zBbxL z$93~Rx#U@7*Ikd+EMrqQJ5F|%Mfk2tt1#O@aCXrdJG@Nt4C=R^$v)bC_R$1B(qEN@ z>Epj>p4iB?fZPx4oKU~Ex|3`Y{mGe~6D81Et4YaB`;JWYUs!j3ZEkKvc0t_DSd*Pi zYn|#RS9d9Eu0~hH;M?tEDUkUg+HYKEE0S{yf3`ZgPfEW5;Q!nlt@&z=$cBky%TzjQ zgC)wnH_K}Q>112?UHr1pRn z=uF)D-R;#h+X^q9hTzneLW;8eYdtr78*-6dg6{*yY=12INS<8s$nLqY&26VjT{bs} z<^COXjZ^>f^k0$sSEK#~lr=>77f=5+C;zWm(~BE?u8o>zANz0M<#cQjk z`6YTAXr8^X7+juv;zH(u8{vn^uZ!7)aZr9n?B%O2x`U>x0#8hjR) zZ8SHoqaAIa%d90iP-RGuMJce}c-xtlaX9Dw){oVL1`%&{=>DFo(^+R0z-=`XK|*i^ zIqG{0A;G%>_I*Qpag6xt?}-nJypGN+@`LlJykYXhH{3IgmRy1e=0yJ{^&D5NW=;X; z4*w1xIoHyVe6B8}kA{1yZ-?AKj+rDL**XjfPCQ8W96ZLrjY1jzp6C%oa|3i@3*QWNT8 zufo6>FEl$nJOeZPOr+XzJnUJqrH#|HSO-_c`EhQ9AOu0QNYE^sfY)q6q>_J%)Z(=D z2>5)h==r>S`v!14;$?;CWqu<4V8YMHQeIiP8mwBOPhRX66R^f5%*qC3N7&x=+1nv7 zX3;Aq*~Yc6lUSomDM{Br74G#}NYaNkHoO&kN5962af0*Mg!{rqP(g3 zf;i!=nDhJzEtORx@ArYtV!r^VHK?9@L+Yqdanh`En0Z97!Nd%)24bS6(yL%#k8{=U zbxZ{li?7v>BKXlVhI8%JdW^Vj3;PJMC|*iop!~d|_6eO71l2um+@&E~uuEG{uOSIB zGN%zZI=~S4H7na*E4aHZ7Bc!0ac=Qm#6qwbexX&nr6tsuk@r(*)vi}n&>WUd#?BVH zP5FWg{42I&oM98qWB#&@`9nkx!=dmCO?5I-Bx9@YjN-iUqoUw!EbdIWT}!TQj|@J) zi&%x2dX7a3vBqY53#1C@DG7e3@UCOLh*VgrQNab~bZVZm%Bc_-ltp0%#wA!5tB@x2 z6!05@3+!}*C}F;-*51>mVZjDrbNW6JaxKkkf;BwM<`@CNa&oi=ViL6@M6;%HD1kt@ zXF?5m(*A+l2P%B-9-2_N@J6mN4AI(3oiikaODr8M&ZS;(9!n$Aujw%Ra^lYL+y4|Y z&9Sd*SB$Z8W<+&2jo}T0Sa$TE9;4TJBpy``19^Lmoa=H2%;d`)9mvPQ!NICS4jSg> zg?4oWvn|R#)7ipJvH4T!w84GLHII8}k*;74k<|7M`WNPb}az>(UpDm>(M#N8$L>37+P8<62gZW8rZ} zROKm2Y27k-aCY21Jl=ld3ojrTf(@q5Tyr7&UM)0l0yv~}p;*CBa3{p@W)W)&=as6KPWtSY~W zyw9SdLOu_GT5x6Q85^k)FI7r4xhVTBl!ZxiutvS!}sRcSW=@gsSXln0GAZ3xGC zc3+X7-qVJEPct7WkY?~fx=MGgp)3ABl)!9<+&$}N-C+y!2(6iF?{>Imb9D-+v6ye` zm{+C)NK&}p|Istun;$af_Ds=(GNy2MzL%jUM-Lh^N4I^QZrzSzJ`y@pJ<`USj&F75 zl1KjhPA`7tEuas~(${m&roDC}i2Y}PmZv;EVBaQHHT2m=!C@ZqwhS8-jA*J6jGIWs zfP;52)M$CR(c;*R_%gNdh@AVu+;vf&+^|%2h2ZZ7cS9uuZ0_Hw&+JOKmRUR(^$&7XxSqBQogpL=-h|z!+Bc z+#SI>A3UC3t=gb$fCd*+R5^PJ^Eo^SBKh;!0RSylKb?1ye4QGBu)ELf4Zi1p0%qnk z0V_!YJNFhd{Ub(UA^9JYQRHEl?wJ-1)?*nOVZ?CM*2;B~ghx$A+!&9kkzFtjeApaR zbBizRz&HFK9O|I1#%u>b{`lQQxGzr|LQHLB(*on;ot28UnHb1Edn1Ay7-tR}?~^oIhqMo?>zFJ@as z34KG`*0wl+F+Y9hj+TD5zLNNUs-Hct6wuZq7(<}CQASHJ?dLKc_UA?gSkG3eFBh$u z#a-l9>jM|ex~;FofqI2}opY#Bb=JI&hFIEwvw|qi;Q+Tezy7=@009LPt?KspCitn& z(Mm0`Lelj8#k8X${O{-XdKSz-vr!`I?{c#%fK!%p zD%E9dqqtUv5yAFu} zTXUyImP%}bH}KExMM&lj^VSxOl=;|n(=dIi^)Vy*dK66(wG(kEot4^&u{dDVr6r=U~Ch-SCEpl1lpC+3+*Cp-8cKR3~gC+ zQ4t3+v$b7RI42hU?hbP+a5*5w#kxA$cx3IJD)*nnE;JX;B*z)6o)gB(Cr^FWg>Ubl z%%R6xX^2VQ$6XIETre0G)1ij@tL58TZIROHJkyj?M?CRZ>OqOXqW|?(ukqIN^Lep1H6PXCBUowz=G+I|wSWb!)Wi?3O3n$;@s|RCTVQm;S5)>)1-IIO`;70``=?_H*AH6%r3#0er<{Wbzz}?0a z-1I&X|2(KiMB%6OR9N$-KtGpATkBIWV0CgA3q}kwJ{S83yjPSKQO)iZ$TQ**NRY|zs9!9P2%5{`K%d94YrOJ*+SdSGFJ`t zc!j9=)1il6c=&V!Pf)gS@l{~2XRbzdQL0N~aUvYp83bDPan1L9A7g5Tx-vs_s7pX^ zA0-|Lktj(^xE6YbaH2H(yjB9x+6hP0=Ql>^+^5_!j3aoLe(#TBw;a54`##zlW)x*^ zlY0rX9g02k#^7bQ`i>!APG*an>||C6*=vV{}3O=ZpNsazjF zUGq_X)^&afp86HkK@HpFlo|I?YL88gAKg__(p?Zj^Nqb)Pz~zj-a?Ayj-PV@af36z zOjJ-_@#%uOhVUs~Qz%NC)T<#T4N4Fh6Y%Dvyf!qwW`3)y6SDDBI=sL~+2X;UgJ0y( zo}1L^`@qW8Zc-ubAzp26m_L+yA{a2|*@T6Pkp{)`$A1>R-3a)| zCqb*P(TU@ig~D{VvLeND=bU8=uf*Kd@WBuR#o+VRopW&z1lqZRDM9Y$9!lnt2tUmX z>@mS?lt`{-K9yi8_1pL%ZZyXpl(D?g8_S2835nb8KA`V3RT&%b$o_t-d*G%#sD394 z0k&Jv95_Z-d(NLECrxryKj=v{Izju#bn=N|PyffeBX7+M+v*!kqM+><5A5Nlt$W0W zcJ`hN#_ppWIVFgSso1{-k+b9vdnyTP&F|g3tpnM7NlUyfA?8d&f2PdHM{Nz#Z*)p} zOLFeUNUqtd|4n*DK%U_&fpy)rKdNZ&lCAulYs|_=$0p?Ff)Ll=M_}Jd;=_(& zb>LbF+P{}3`;DU*o!+E0pv%w&jy*;(K~4nSQ|NgbMy$C@P!#z2oM0Lx5E8TRmYJ5Ydk9N@1}D zBmLiY@K-o8L0l@kX5Pyxq+M#>e!`NlyIR2Yq$TNY(LJTB$hSFdy%~dAx8=R(1BbQoRk|HMXdM= zl3xxkO)5l|YqJLVdb&?FG%zCk3fxtMFj$-O@QTG;LXfLNnpSpQu&uIUzm(o|JN7el z2uRfZ@^VxHrMIFB(M`VgO#jy9Wb5IAJq*;q7v|ip5NACPg_Ohyx_z@qO;>Ro?_O09 zUI6#ND_^i`mWe%RU+-5Ja1YtV3pS)9V4rWOF&&5W4m0fYfB}%}u|hLfm>v~X{gfpq&v_y83GxP9RwO~G>D5%Y$y;Q-wqm789zR{Z= zePq)E@p;JDczcHo62Sy_P6!i-yuV7PCf@y@W`d45%c#i%$b>z*uZQy|V{w!MCMJlm2R6BSpk?`s00E5C7Nsi780h;zkw8 zzfas>dH?AXvm2)-ZXaD!U+6X@`(m0eUdA&Q3&DInKEkuJG~JOL(4%bAd4>0%GWkyX zzYQIk9gkF8cvNK^b{J-BW96In6QvMN&79-%u9{45s?6+E#j_X=R3^7T169N+shfV! z(>bXt>B=Y}YTxPW#`CWet-TY%O4alCV50tL^4Vf26<0RD7vD|4ICAQ$yo`u%7l|Pt zCu!H?;LNqdu+Z$qtH|oMI2{GTiGRA9Ols!u_RM}!Xc@>5G4^&s`C!KP3khg6Lu^_S zZRkZ>V&7%R@wu!$%q%R}2>#rGm!{(?lHq&Ej!~N07M>7tNAkNaxR=YB>qfoJoGEkx zEEeuFHkJo3`BdVyl_`xanEV;%TOWgS`{Z9r8qY&ScX!vfybw5$t1PZ6J+slZBBP@6 zlS+!{Q_4&Awo&(3rAccXE!J<|GRlcUjb*;vXL(73GUdUYoLi3zDREq`wZ0he9TWb5 zpd1{D{G$@Sc@Uy`C2b&6L8<3hcM9v%HYxGRqDNI{BbIo*jpUfxl!5Ss%W{lKI@}*6 zNb;VqVUCGj$s9k8qP&>re;1T@mheflwTH)hG*78uq&ZB-na9}i+TUfqQHgLBvMb%3mt8y60J)NBS+0P4;QU!&+a9`sm%qM(*9l7B z<=Q+lewdYU6 zeFz?A^}EtlWJ9%X<*CuPcd5$SZ#Q(raPKpn5vR)7T^%J*^oS8@Lo+%G6_u4+48P;P z6f1XYG|N-R)aBcGyyL#!PL9T6K2#FD^UX|}8U9eLII8KWM${1%6xqy^5rQQ5D0)Zu zMvMg-?Z*t{TA}eH!|r@$wIMuE8T=<9d;s`-Ida%XBX*!7(dN1_RGKB!>QdY~`AQJp z*xe;qH5GX)7*+2;>PaoJ*C|}o7)5X*y|sD7N!}i^lM+V=?JpAUkf}IjN?-D)A63TD zLJpXN6oQhK=zL!d5Y;z?3{zQ!OZ(*J6XP=XLFaE>L5989w@X{#oDb!CWrTwCdBW&F z3gVUL`|C?43Y>bJB8xBM2FF5NJ1CJVn?0V)6k{KJ&y!INF@;7*Sk#tkL@AnD8y_iK zIha-+T6Fjrf6DX~b<;5%j~D*h-pYIXS<-}k;Ok6h>Yf}Gb*7$}bX1FtWW@Bwu}V}l zIOh&XK<)gF3jKlyc}K)p`e6;m8>c&0r!yuM&f4`9dfe#R;`4AK_>_c;pi+%2#3kj6 zYF-U`f}6?eIMYc_*7G3d&bui#N5KdeY=SFan3?t1=`_>4LhKW8397G3jqfDh#%>h3 zVT95ZhTULME{!+H+PzTp4%Q#Yh1sBv&p>Xg=if>Q{N~VQ{#MEc0`RX4U9cnufT5pn zN0m)`bSLQGrtp6lW*oMob?4`6+qm3XJo&Ej8!svOc9a=_g^o}C&p9Ga>mzUX(YDxD z5Z)GX2yJ~kz#*h;|5=lG4R=`j$|+;k@#2kKgtK->?L#yF=C4=zv_+|qk=@NrPWsl^ z`vX1M)Z*F)1zcvKMof7qQ%Qv!>f3`|L!2Ir7Fp4WlQycW0DQ>#KLPj%Hrs)IYM$IHD%*(O@s-7e^~%kWA@J;zOExU)?|>KO_0RGGfvpL@xA9nO1HTx7N#)Enr#0oWU3^u(UWy1pk;t!YFf zaa)k<4W8br0C@&q{><}X3`?t0Fhh!Uka8&$2#ZjPYyC%9H8bh`A#)a$>!;CklgEwU zU_4OL3m>@)F8}n8LL#fBNUc#!?SsBNI^BLcH$@38t&bWA&E+Wlgpis>nkVAr?NHZ^ zCkcamU+#@Nn7Y1K_4IjR(Gdlw7XkT<^MX*4;^yu*KNk{8i5MD_A?LJ384>&h4L{Jp zwa*a}6YboM`>k7}okxopHDUPw&c2ShdBLl_bmv9?;K%nC4SfRco$MbSw^k_#cKPxC z^!D^^axP|7g}Fp2VP?*jqgMkR^JAflU2**305)X1cG(}0J6)`NXO6gEgNvK?GY4-v ze>@;8EDV?d_-CMS{+DKVHUZ2e1~^ zHGC*1G=McI*b)8>*mV=5#(Y_nKqh?*o3A&pi*i7s@G*8P#Im?7E|&Vhy3@VwrM1f( z)Y<)vLf_Y@YUAj0*r>|N+ILRz?_}+k*ddWxmK1?L^b5mGM<)hP5eD;TX;Pn!+JWTO zGH+d$Z-^m({V>KU()2Q_5jlHFLq7i}~4zquR$b)KmZVQgF5s z*1t2=mr7L__KLgQz5o9_;uhukz)o$Ld7`V;oiD$Vi<$0oF zTkWa1tPw8s#A7A9`mt(>q?cL(hzpi*k)CWwaePYOq}r#4<)`%yy4o;B=Smc0%&E0- z^Y3o~)_GXpo8UQhzg1>zQN*&@=H^ItrbCo)x^?cUnQ5t5F)qVIpwq#88k1$JiaI{D zv{2ai#$+d=?%}<8z?#;7cowJBe!~cEK}*cY@KNnpJR3ECIQ|$g`!99_!*|f{HR^$8 zyjt_DN-C#e+E=sMZRH(VvDV~($S$69@O$*4E$92c`zV6XbUO$^txxwi?WNpFNnyOJ zbTL+ma1tUon)iG^VZVFI+aqcp6d14n3lEFE<V`wZC|l9h;ttG_v-6w1eHGipwWouw6o<8Wd0OVFVNHw#6OcTb3fCaq%v2d;QwfG z>U#5*>nH(L;5MB!XVNp{3fgR1VoF-Lp@u-7w;DdKXn=|1dYD4_xJGc<21GPWAB$jxW}3J>e$=Yj*LD!xq0Ar82; z^zGzxJ{ZWu${eA>h@&aY>&?Wvg&xLOwU69%^Zb6@0<|7*#aA4XsHp@_eD7j?S|IK% zB1RJ*$#5@Bc%fso)2P4YQIhlATie@!$v!u5iZP{p4FXZ+DF}uQ&+TjCc#)A0*Ch#3 zadAGd_i^T7{sh`-C8P9n37n$9AM|tgwJ0*3m!1_sUGq#7LF6uZZp|wE&LdTta>Asb zoPM9Tga%n;e;=oeUH_S4CE6+911o1H6({GieQy|^kjM}_M60_ir<8C__zX*HBC&xYFF8R-&qX@1&| zPuKU+EU4?d{0<;X-r@^o*;p7bxZkVng>26!IlbFOR*L|8 z6FDd`$wB0%6uvA>aP^J#ID@k${Txnh9N3cZdEC|HzMA8dvgXy^fz78r$|ziN5;(B< zBRqvcGzMaNh@zjl#n2Hr1QMy#xLDcD(Oxp}KQ(xu=9ijfrHM%`zYNeDS|9fNK-spr zz0D4_uipJ5C2rTsoE;v|?3TCd*We78|L=VC?s7D)F&ys_RMO1i`#K+1!?E>M9TaL; z$+UFe>sq0Kg^q%#$#YzQm2P~Mzj>fm-y!Sh!6)qvJl6MyTe+gHUW+kyKvorT3c8#m z9IGiX!?e`|@Oq zdusC+z2n~f)upaG4}6u&qI{3bjf2+$rrSCHTfoF@;ks{a0D>bLL5ijKs&)w@KNGpQ z7Fo09yVd z5N_Vuei}jdud$&*r+dp)BK~HKEillwSDnmoF7Oehp6|%=*#bBZ^yF*<aE-LUVs!vr$})3ft^3z-1(`0RI^89ddZCEAs5^m?u%080R7P%i;~_8Xa- z6_lVWWt|Y`Lr#!_zR~**as}dYqDxkruXkUs*FWj_&E5a2gVU<_V`QN_ZM0LHU;LadLcL|UB{C_017jdExRgaN5}-ug&fCAD>&Jk=8oR5)YNNqb~)>QIwlaUSwgnn2B$jL58DAn}t#eBnhRl z*nB_Q<>YpHU|hiiO~Xt3nhKVgC!lI9?qV4eM+b3Z;P)tca_7qwfr&=|ON&Az+X@(0UXeNl z7(Sdm*MRx{BF0C)=rL`c`VQA{Y02;T2E6ECIof@t&2>SU`ewjxrQ8zqxJ#QN>n66+ z=L(srtCC(EAZe~So-J=jO3|e4Fpc{XUQaK-eCGCM z;6LKgmP!i@-Oy8F2M}5ZVCB7Vb)Iw3UXWDTEw$t-jg1R9RAy>abIIIyV7hYU&6h`> z+SihRtP8L@!AYXMP*mK%!wBIiad?=-*MnM_K(GAZS#1(FC; z2v0pEVUx~&V_N{|CS^D7@0dygh}#nPbi}6hbq=RuU{Y7UY0a}G6pAyiVzNjRA&u|e zd&zudYRfET88)hRl^u=kH)f+`K=TbkZbC+>V*cQ{5Im{NTGVM7Z{BX` z+O3=QN2N2pZg;unbvC-BA{_P7NJmypUzE(PDk zKLZM8m%eY*e6mTb;Ee6Z;g65VGn_>>B1X9<$+C$aSlycgx-x*lQAYh8n1$jxhs%sQ z1)*ZNf?3T_`@6XqF$}Rs?6YEz}{J|8wxuQq|x+U0$cG zPfwa3CUo!}RhN?_Uw_ue?@jw1FcB-^8*G=j{gQZO z{*&Z*qr6#ZWco;KQX~1t77b1`NIxhP-{qc}~H@x)~W^+A?Ii z?AA94O4M&M_Dd;7({W!Q-xLyRujIWw6z{;vLB0p6id;T=LQ)E-)_0-*wCPdGl+K-M zjl2kleh%jrgXsRMkTW&f{IUCYK0=AHAKSq z0gML#u4L7Ir%odfgt|)^fYFl$W&C`ZI7>-OfsCQ<0j!=Lu&f}Pk_CWUBashLTh@4c z;`*F|#{F+Vf#JYC@wZGr$1SB~4A-a6oHJ1dZsaR|=Ye=1Oi)@CNU{ZiTmpz+-|2lL z{nTng%j|&#(cG_?U=4I#3QtJCSx~#Pr}++(w#DdbmG@F~ZrLx)FB`k<<6oIN14hpk z3>QX>o3LGtc-(6}QxG8r#wx!MEysNMgv=aQI8L6BfEmr;MHVB?G`N1AAeYl#pF-^> zA9=C&h4L9FbN6HO#!i*{Wmo_7Fk}waBZazOHkm*8T=?~QnSbwH%v{sn2U8!)%aZQg zNgs*C8y4D*dz5m(u8PUYONRyRLlOotqAf4Fp8M_#lR;_i9$YEW?HKtFNYbtjx}pM4 zdL9%3usHkK?GDuRQfPhG*fHELTMxvww1SCk1ELlY| zR!Pn|6d8(8Wa_R$+dikyJ@39xZ@f3&-w!>yp@piw*Pd&xIp_Wr{ddy_c*+YyK@d0qsf&{6Rt|lk>Xh7 z){W%;hkbd7LSPqeFiC{1^H>Nx=z^K-*z=bpjD^7#QC!S@astmSgpar4hmUD)G10&c_~Y z_SC_lu{dIhnv#Ahi0n)_=4oY?QOw=)JX+=c2@YP=44kTSa5ei_EJdEi%j-89Q4ca* zS4o7jK_lk{zh%wC;;qT&4IjS6KVU?EYi8QUY)GRsyY2_2oz4YPUJ|*LOOI~>dze0ceww; zEhYt2xm&bt9?a~lRiie!ou0mCd!v^~YE0AlTZSo?$Y*O=l7z4bgR6j1hK_Bn*xlh9 zp{;>hhF(7x?T4zZZZnmhd&i4r<@OXKMR4JYg5)o8vrgt({g|W$~%5lTydD-Ibes zQY4lwl;?!U5&8YdH66kd(uA|qRIfx@yp);C2B0|fezYFS*EItQAiN4rXCQ+RQlet3dbU|NvJ~C}E-9ha&+0G@mLbeh0RWWA^ z#QXf6Cl@J2uG`gQ8_4f&8;c*dZ*h-i8yhVH9n{r7R%^GFA7-^&_phYoO9394Sor9x zx?qGw6n|uEff%WMNzDm|q6BaxfXIs|Sa6Fn$OLh^euiuL?Elp=ns$_LRSzWwuwlhM zIYbG>xm3mW772_^B3Mwao1o8CJ^?v`C0)JrVd=b(@wzrw1+Co;hc=;lXhfkKwug#c zT;30?KS&XI%d2U+9>C?cn3h`3Ks6 zVJBW2S1#~4bFHEeJjTZZ2m`<EnX$1kwup_7X zQ_Z4It7YeaNZ5ka0y3Y_NF3^0Zct6WzLgG2_W{gAn|0fzjpb#cl(Ig%?P&4F#`XJZ zZMBFlH5aewX+CquHZOmZ-B%_BhZbaZG%^$Qi9jeg)JtWC>?yIqDf(&sAI-bXE^f^_ zG$dlH7YslDp(K?_$IHO_OWThB{hxze7%#4a48}_pua^_#;?o}~b%?n}B5+7JE3ScR z$lJXI!!UebH(jsOqRn~4XSuU-&15#@EQ5_xoD&W!@237d;0p++txsNe=PQ!_xV}Z&vCLG**!mexJ>WXy87=Onm?Qlj1=RdTrcn)!;(^3&u2HXJ z16z-Y^k}L!RQG($g4k|Qa-|DzNJPZ&&ISlBLLiAgS64(Xji;Ifl#ioRY5l>{WkS|} z!_teJeeSV}JMjLdf1IcIJ2NQpn;dRHaKlpMlt3`^aks~Sgu{gLa7@BIJvHLgoGZQH zvm->3?e5p2?|6|(92Iq&fcZ}s9jH|p{U`{eW#6eHlgV@4;(9Cyu!)tibUqX-RfljH zzC_k;^m9$UbNn|8p>uDS%luMlFMZmXyQHewKt6Xwrh@kz_BAUS3^$SOrSrnz9x*dB zhn#&FgVCv^p4C@-cvH&arMPbe+{NmahM zl52H!+HGR|Y?5f=B|eGm>Sg5a(D%OW?Iz|AKm;Vy=;UpCLbz%NrSinATS-6q1Hsa$%Au`0SStW?hOtF<9bF9Up9oFGOzcjttgj8sG zE2Z$Eb8e|RFcuG=vI9E9w5(lSH`0y#P)XtaYt%6QhhxjoOKO;yk0bA31HXgX1S)2F z_XlNtkL&)MN246)JIp65c2a0;$J&cBJZ458&boffJD-uSCX`1N+mvQ!WM5 zCP>xK_0}S7?dfT-B$P}td+ zM_8js(QlXdKxlWV4k(7+T0d&RT-sl!WZKVwI1tV73sAbo|G6=Hw`G4>mM4e)osW@j zo4tDV-|51SyS9I2Aujy(4=ee{U;kAV`~T;c|4cCKt=r#D@&7FQUmp37ius>{+xH*L z{Qsv%4eqPQTP$(iB3)dEN78=zTX)@0PA}efoAe=)>%X35aQ|Nci1citUz6scM<|i{ zwlJO>TfYY{kRE~XUuq1#^XXUc==Wd0ZC@yM^Em$Vp;uwS@56u6kng02DJ&vS z{P-)Wp}GZ^ek60AVBkwn2A}`<#SM(tiN7N?#^E6U0c$P-UsE9vlrz0RW3nu zKk6?S*Ry{p{jzua-U0ZlW}ZAm?G<5=XeZM?D4dMF#KNz_G2Ys%#3y6L0>6E!BMEu7K`P|GLXFEVoYh2;PE)!&^1el&wupkTvKWVPM1T7R9YI>K5!p615`LntUl5l`NStBw4} zFTFnc^Kl^zq;9Lpk@odJ-$jkx&7Xf@+q3Sfc+c!b4gWf&*9t4i{BeK17Jv=if~1q3 z|FMwN*uP#2C<(A!m$UYgU%%j?dzX&y=W{0*NQ4aDwVdvM?BxSd>7TC!+&tK^u#f+< zFemmwk^j%a{7=RF)oK4#Mwb-&$WE z;%qRKw%fFu4*6+g0s&Hfpoz~9Uh=Q7yoV;-Xw{8e@PEl@99OKHOZ?8wv6G(vu?L6g zGXDh*M-gG{Bn=moR!3ixJvjLRYYsmW)c*l%YA^6H=4Y)YxlP?%57LR}EtS(k65OH) zRALgM{Xo{(|AMUQ_eISHM^WcC4?|*Hbz>ml4045M59apZR#~&g?t8`GJtJq~2O!tj zv8slbZx2{gK{yWj!4GT}{x8_qjsbZiK+LEboUEv_`4+~k677DU|I?DZ zUNVD(7b$YLLgTW^QDb)!^Sbk)A2k-@gHi>ozkdC*|MKh8CDSp^nEMgB=lS8YzQV0o zuLDH?SknXlrB$56rZJ2nK&_OyOlllIcI@8I9qURawf1W4T&i+nOh~TFYP+UyTzvqo zh!-bet&&*2Zn&|rp+3!iJCes_>#0(VP|iA$I38gjVA1wqi{7g1!+kSx&s)A{<}_T3 zi;Is^^Zl7__&xsmku&TcmoXv|8)K*9sAK&bln)U%J1cz_lHqe2Frp;#7?k{L`Th19 z+P^n5FTJSfE;b*0Op`B%px|1VroQY(>oQgXRU11;@3B6th&*j_9xJO^?z-x-JW;;d zp(8FLe7@B6GW-}4X+631qr>I4nu3pJ2G(n3eQWPZe<}EF%ZI_b)vQ!5J=QNpcXfA` zpOn-a>~zL}HbPxwufE6w`7+fM^JNG{!5QIy*q=31u>}_)gm$wGjHf(lR!8Fq5PbU3 z3MmTLz_`#kd`!qZia`kLejMFXrW5GOahDQeD$x;|5OMA|<%n{e?g6v%^Jh%O#{7^K zE6z4{bKUoIKuMr@s(N64Qx1vj9#bHxuc<>ItJrV(J_T1y9J|2bd8K{LukSzN@IFz1r zdaZdUli0?nsTxJ3>O{xwK!K}uZTY^ z1RnA&VB(nJ?gm_b=$RAysHS7tR=CsGOUF)h#*l)!*&1(_QncZyLVz{OVM^aC6Ly(* ze4C9oJ8~37XatGuN;@YQd&jlhFp%45dUUoM*$BJ$gFcmN{5o^R-sNx4VZHJ2v9Y*` z32TY5(Lf1ti=6DrU11@c-m{kdB@U0W9KRUJ(qqlxh$U)${{6s#1NhE68ku*;ZzSdA z_-Z_ZfdNLq3xA=QNUyVEwk=tNMbg{5{!s~7^|Vj;7}BeP3J$yf;Ibf>4TzMDyl7sy zk#NK(J%wm4P-8dIzp)HO+^|fqbRI>BQaC(8y4^r8)NluPpRsd$I7FcamAe-H3+Wv* ze`4xU1@e#9V%nCjRwKHQ7 znX%q&7ALk;$7qMc935C$IdTPoKyX#8zs0`B)MRP>qb_AAv2Z`Z8TO}^D-YZtQqUw4 zACoHK)0uBqv~oYhhZ-9RKKMAzUBqI(31ESC&MFuGd4a<_t%vEc+7NHJ|1GLCOi57@ z19sQfh}O{M?4}JA@wbxPLl}i^Ui;jg@BGeBnWS!UxNk!e0VXtK==>s z^q-`;H2=87_Ts!D+GEokm-LW_2D>p4ZQ~QS2#+;RX^D$G!d3RD)*LaH-#T)37eKQ= zQ96*gqvSC@u{-Zn+1q_ArD+r)QntG}Gg=#Xd?P#GpMJp(Hj(Y0o2U}TOYozFWdX3h zVj2l%=Z$)sr7P_o_hxo!9TpjDF*> zI=0$rpvn7zO+XlQ4iFTs@eDpEO*2UrrjV&WhH%9YY^vViV<@wCimQh1U&iPHI4Qd1 z_gh|T&}YglHwN4*N`Y4-*~yxrZduE7J)mqeJk*~Qa!wTkCnCQW`RGFjB&G(DJ<6qP z-E4wfTo%LA4%4JWYO*jAvf`0o{)OOUhHGQhp;>F)8db6Z z(VnDngK-b{1KTY^7lUFC9N%TNXk(l9U&o#br53Ol$doQWlQ}|GaM-d#bf&uK##-B&LHxd0!9h2>6ydi}x}*Xsaaus#S^Mwcl=e&;E(^KRtvsh4^im#0 zn3XSTEL)55xLlHhQwtu&8MoUIYJC?%?=kVwf91#DCG50NNjS^@RHN1t-E1Pk_N5sX9kgf~B7Z4IVDRfi?9gtFs zhEy(*zjt+46K>(8_76OVZ*Ok3Cly3|rD^^|l}Ae2;aOwLc5~m5{c#lbb!|0#?i{w; z8bTNEM18K9h%$c%7_W{ldLT|ZO|}0R!ih$r%uSsJsQxUyQd->D^!segqiWmH-Q~f< z(FnTkSH=B)TU=z|)EO(uDzA$-_sENbIb%l53Xj5PBO64S#2SB#H}B60iUE zjiVwB0GI82*Z4oUjykKE_?m00qMm$A&l}jp_L&ZOmn9j)QG_a{lE>7*YU{Y)U0VpU zX$}F``KI&u@ngwOVaL&O?(C~VNM*Kp5n^ad0B(PD&&+qKMtu*is-lkoEb?(EMvE}g zHP5_kaLi7WXh3ghJ&Sg#tx`r(*?*mVQ?5|D7?nyH*y}vFwiKQmh}(a6bs9+scQUN8 z`+cMRFwmJqDR{>-6-vdb9KEU!6C_tCAe{z-dcb)pkIq3CqJSK8$^Zn8MmKs_RPBQ%lRBgT(ht4y@ z4t%&@#T@6B%UijN+8(8^T(9N|=hPE^JXR-0=wcM|v}gy;s+?%v`VP2($XL*gXFSM> zNVDCY1(eyx&w5UxxM-V-?lPC;v%A0C$`q7n%)kN>FuJOAN+-RUiALE6 z$jqjh#8&t4OM3A~L!X9JHGr!V=6V@GX2orxWri&e+DDz_nFRhCqK>m0#Yd_Pm-I7gjd3aoD- zvuL2_kc4%ZZ15@-S85dreZP1fa^GAr@B3)GDA!Y9c~)&924P2s+Q!>d?l{&4{{v1a z>q#^(U60v4{vMW|rewFA%ZXjUwt53W+zHy8`{Mw22p6o39fTxd!a7>YIEpv+q zZxx|lMMWKg@+o(!{Qg3Obd&G;QAkjHci5uinM1lXKFjDQXv4(P8j-D^G7tAlS;cQ_ zK*db{@f?CHFDmv9)3yjDsB(X4?_RUo1s3WJm8c4Q&H3b>FGYhMy=5Dy9n0nGWVC8- zPgBsY)0MmRPHm|ti#CRyABRn~FA9bVp|xw`rgvXa-tG`zA9_yQOxY^5;*}`DN)Wdz zca0u=i!#aWsHI1h*-y2&u1?Q|KkdlWF>q*CA9P#3?H&EiO_W(ef+&G~fyF8r49yh{ z4rDU)x!_{)*&D>|IAWg1&Zd+0M2*4kA5UTdeg)xI#YMp=egdAX&w?o65;*fZd5hjmMA zXnQkndm`z4g+CeD6f-a5WA2F@^23D+kow_V2$2_@m%l3BC%=UqO*<%@I?p!T2$RCY|0U%K-XuX4?L z3$0)cssr7}8^WNJ5(CAfmar*K{SKP=19A>COPU7m{6GLzZolGt*tG~JJfPe3%P1mR zz$%>*kL~9s^jKz}#Q8ZE<70q2I?eYXmNWS++9rW1)@}DwUA^}yz#&B;Ql)2v2t;VQ z4S=6&AtMjd@TgkvfhdlI5Ro@ur4zs7V+04B8BI!tk8k zc}&Y|!oBoG?8U=g=T-Ez9vR9Pyx_%(@{ap&0UCv5V0rv-x^DPt{;J*OCke;kP$)_4 zwEcsh9hQyrtE>Me1~*fxBed6tR4%bfFj!{+Pf5U=VAfYH;eNL4;1Xozv;K20Q&nPz>zU)q zritDzH*X!F211HATIerzO4E>-cw?k8Z>H$-N_L`%6NRrn`<(1JXH}5Q>U6u&Jg?GY z7>=jgSwx~(zDB!8hB0jwFQ(2pJOb9bE_~g`4H8Yi)-Yn zB&T-9YZl!5{7l6v!F`LsQpG;iXj~vJFMn{F5c8uKpjSP&^mm_plMs)I^QRh-x*-C6S2m08Im9Gsn8fWU6 zh~j!}D(7Hx8{PqTp)PsU;H>ze7V!U8`NGsFLYme=Z8G>e>YS+aV!}&0;rm}a?>&cp zpWv(Ku`wQrB0tNnbrs$KEs^)ZpQo4=bt~L90L(>EyW8;lUIf$~pw9vnKEst4|31cL zBJz69&f-f6a`86_{+Hh!G8^d5DxQc+eVMniFetIkzMnZoDNdZF+`e?kZe^*FtG2TF zkXs*XL+L_Asd0TU(1z@FV&u~4q%$0Mx3>o2LfLby=(0ish1lY!Kc;+5pn~|(c@MjD zQv=lj*SwC=QfJV)eH$mPmH+m1=r+5pDDK%2POUO$+eg7W$;Uobp8oBc%MVRWM*TcC z4FYA8cdo$-K)4a>I-E&fWUx_V7tr&8TRxAD^dOF^yiTjOr}aRVZ8pDaFFzN2E4o)c zJ1sL%KAX{kY)N$?ZjF-S;>Sg`OO-=lbiP2kjJtq;xhIB?BC>9SLm2F~rz*wEIe3lL z+U*jGDwF#{${vq5kaw2Dn^fJEb%fT&(rD~Fvt$$~7VcrQ0Thum%7%B49XzS2gGx+BYk#L<2{72eW5cA4lQfa| zf`DVoq6cii|Gc1LWQ;4&*%rZF;ACykx5vM`5@w5mv!bAWt0PFu?9s?kWIy@+qWh&` z;P^f$OQs#vY&n_pY4u*Bk}KoILkUUyF0Pymx^^X@D1K!i>5%7u<)76-cb;F6J9XX1 zpW)f>3=FAHp1^-iamXDie|6kz>v1ii|;QVyyxs1Q6~vs&mKv&Hx;n0_i_}aC`S@zxtlJ&*UaYB&P~dA z1Y-c-{No9B!N%?aOHC-XmWMYPN|I2k(AXNhE=_2d<=yZif2~1&?3l;yc5#MZ!KiAb z7%nBwgNIKAG56$k6m;<-k4!E3a{dnDqBD7rX8XlUT~$d~?R$6>BXiT*o*jR;1C9eW9;d zHE{+|=3_~T-20nSQmo-kG{}JAW6Pp#BZgk%t~k(r^dDEc6p`D3>z#0wOclu z;m?<@nVqF^%L04o@L{>*38F4bO|c@HRs7+>!NG#r+m+OUt}730b=uSWN*vykkd76o z%N-HkTZQDU6o07Bi~zuTy$j>JnXY`k7e=>+Ft(Id*0;QQfP=`~g9ST*MK*)s14o|v z`ue{4t@=x3VPDfkbE35GSuReU!iA|ySd)y5`!xl z))oex?Ce51?iN^t-RBn5iBMHlEicpG#46p66Yrng_f8>_VT((S#o7={gT&OmW=y`k zjKkVEr@kC{(Kx$LR#w*Hh1CNPY@Ku1B@GP?Ps?W+RO)LyhRMyfWOA^wGGj!V-aScU zeQCV0CD^DofPbqc>HLIz7;AD$!|u)&$~hAHqREgeUoUzYR3-1vt`x(bgz2#K<|cYvNn9)yKZ3(WXe=s+Y&t@ObXBBRo*5 z&d;7bYb(2|Qn9;f+08gKQtcZ_A1BOsU6kJBX!sT7kBo{)=N@6y2 zlyqF=v3;MmEKxIed#ya?DDmciuX?Damj$ms{R z9E~!*j#QlSEXsd5H#B)pt3-F4pr=!TKn{7?yo)WlXu%&RdLuTr(+lCxSpAmIg98mvWcMMW_<8H{%KG5sRGN+@Hq+8(wH||e|G~%AE}zA zm1Ah}!o-V*|Bn}RNwoq9At7KvwG&fy-_fe^*PcF$di6URjqZt)9~m9x7v=|KJE#;Z zoGhkBaWFT}^Xu(4p@^Fe8_PJ8-3rIKo+)iFbE8tXja5v8$KfI{|>V?6o-`JNIM{-}McH7;wPxD}LeDo?HV4%z;uclsZ z-@dZrOY-oM<%>U0X>E_)E^^a5&u%N}MGql{xGOw2Bj`Mep?QFv+QOf;;0*K24Kb!g zV%kojK9+COQ@Tx6{-IHPH^M2rmcGzDEgVdCi6m|KgD(IfX|K^wg|A^GQhcsNAG-QSI!btz1*ZT|j*CXDK#A(p~^VPGdY z?jxJp)w{{ppC4>t-Pu~xWPfKivjl9P6;AGh?O%}PO8jUf0b2{)-Y0(yO4z|M z2Z(lrhYKCIWM!1w0q@U7=I4a~A>-Nc4 zD$|HMf1f=P8vEI}Szf>`b2gU4u6`_mG{%xe6oq+XWXuR5rmX;T4DYzzwkCUIBu ztA(7LPqRtta#_+@F8w-%qx_Z~ZM^BAKumGBP4bzHHAX1iodrd-Ir2n&>B))p{*{tc5=vU@g z{Ale=4lc>z((ZWt{SLOf`I8_Pj%NuKKi&8DPjFvLAHyqXJ`l+|@*SXlZDol+f2LB=;KWVA{SX+2KJ5+gopb zn>w=Zv6|_JFflXWEV=ZOKyEXYcj5Wc0RaImOQ6-{8Oj3s64ytMYcB{3=FD7#jSB>B zApdOl)L~;BtuYHy5tg*6U>JvUB7j)51iFd4xrp>=4U+>i6UT)M*LuDHH9RfvINPOs zcNV&wsljNjNn_IOOs$+R6H`}=<`A5moFJeTHY=J4_I&#`2TG8XC7Av)h07BB{OZIo zw1|_UKo3YqeUm(G3vXHVO7inN04rD9PI?ReF^7N|KIDwAw|9N2P<{TmX(-^i_e{$N zPqoxL_u>2js_y#B~^`a2U@Ats$M@d#j@zkRBL z$3`yA94Jr*a%Z{nw#I|_{$sJ%w2+hcZENi)!CJ1Q5BCi&4u`#?DSo4$q1bfdKx6cK zoucaZvU0GOf1~V~zfo4F+YT*((vSZnUpc_c#AFc~C*qWaX?0wAsOLeLjhv;~k3tb) zTI0CE$pM>b+6tm>hB?+{X-pPIIb*57g^Df3L^=Bv`9az1|!Qg{aaqaqT3{I?uw zv+?2?11=v2MTlQi@4}$ zXu$oT?53m?z}Qxo4g!N_V%aOJ#Fx#+if+^1o*+ESG`3|M47R;)%hJ=S6l3;>BS|ab z2sFE){@(I6;R1{HyYNF=QVUmIdPthKL!3`Js={{WkW-Reqk7>ic=!+>1e zp9jKOvbB;seH^-*(4+HniCPsKNmD)U2Ryx|@^OETbLgsd?^k<|ZuV6Xd6y#h(psR{ zMDn@dJl|}p{mz5#OJrg1FAzaw9HUzAc5I=5LpO_Xi{1yQDQQ|mdH-M%*SpWgvAv)mi6as$Mq zC(fT_*C|v;+y=%|US6I&{!J+Ay5j{I7_TUOAy<EX$~;=FIIuP+m5 z=;`A=eE0zB2{s1Ndg*y|6Pg!@OQJfRga^An-)A5ox68YLmdfg+YG$M1&TzONhWCy! zfhJb`wO;|QDNbT=Q2^F+8ZsX_mt=VCim4ut*7$$@`gLgNUV+f*ZgWfwLRe32N-aaH zIdzojXmc(Q)%?XAquPg*ajp#4MMaWKPM$oeKuem16bf)jAu%yDXU|4Rpgan;D9@d{ z@@yDd!M5dzf1WYL)=&d3Pftyr>7H}D?Q{!Iwh?{kQ|*jWIdW=P4=HJ((Y{QnDx>p9 z&)cNpk%49`NDSO(ZfkIgT0;|D6f7ty=MZ7N4$ADalVg0=Ys&rs!C9iAw~T~2MnfJx zZah7QP#0N?ai6Nlcmvyg{xr`-Y1JhL&l8eecIyJa?Q<*eR0v@ELM~&lFDQ}c`O!JV z%VV^`JC9t!2dEAn!J3Fi!T6TS|K9FY>16-s&z~2W4e>9*!3|+n+6C7Q{CO_kEH-)Q z=Fh>qm;nH+4WK=L=G`KFa{~h*hDCME;F(k)U8deRC}O#+BTClMGAb&;`7Zz@sU1V3 zqF7h?!q_x1!6I-1Da`|c5#_iD^W@{e`jP0ES2M+GS9+8;v%6pfl5yPF-svm++YH-@i$hr#!r6mS zehnI@w~vp?*0tG@krrTOFnlfANbVbQ4>It9&trQjL6(EN;CZ6KJj#P9S)oePzeHf< zSWJ^anOb2HZhed^RU>&V{b&PO8V<8F@mjuz(T`3{IVTHgy+CDVV+yTiByGlGeC8{8 z`()~)WSNjzgx=YVJ8TB;nk}hm3zY7rYS@15+=5*tpF>#WFc;wVhaHV`Tc=?GuslJa zskpswp8?0k1vPd7I1}bMoO&e>^5H0!-UHgz z!~9%GL_{-9#pLTNia2*dq}N3mFJPe1DaAl?@S$WgG&1Ui89PnovVnm?VoIFx0VD=Y zH-_`-zGpb}%bXMRX5gF`^F+ZAT@^s82XiWlli^>tLBuxLT!W|Id702dYRThNN~ZwP;F)6#vS)HNh-em7>wMIQaxXCoInBqU zF}{vnHt`Z;j)j0p&DXEauCD3*-yqTlMJMjE7Kzn%f72WN9$t3KC%s$ARR3e52KPkkH_rJ6iD1oz4r$`731C-UefO=L zvXtz>ug_B`kN2gW;#C{PIo*OI>+95R9TPwNJ;%q$cL(no9T8zjS@otVUx%pxCnPN5 zcDLIqWgJTAqT znVD;=s}Jnoe?`Og(H_^&HxC)s;|&B-D%(phY@d2R5?-84;N4SI9*aXykB+uJxKxLU zKGj#@-rM$i8wN@$6RfO(#>j8!6yacB`TP4*=6#c(`0WSHj3N9rE2L}d8ur)(T4ee2 z$=^!Wpv`?MmNNX^)}r$0v7YtJyP4s5S&IV6FKqq;E_I8>Z_lWjrWfn!ij_0b3Wpqk z0~LPedWGR-*X*7PtCi#f`D{Yllyh-|0I$@{ZAoznyoSAjQ!mW)Kejs7xG5P(6fBL* zcTI;U_u9e3M>ak3Wk(&8y6qTx9MKL_bO?}(U+eaZ-%K;ADhnPrf`(ZwhVw zD1LpH&}#!kC(|*pIu1mSIMn68PTTZfnwkdCy;!||zF7p(!T6Fyx`cCnd9PiRQdAZ} zCr5MO$z<~>5sY4o^@Ip_oOO6dlr6#ZTC?HZxN?T~o%DR^6sphFk16kP7wYtO^OVih{(k*b@<1FWr^fdeWf)?dGF5aTi z7kAg+O>}QI^hp^u#&F2z;hf3F=qs$U33Gq4Y9@RR;x?psZ3V+2FddRe_u>A};c)gf z)$A8EW9r?4^nARh=YH*{2{#OcJR3XuK`()EqSC89>cK6$PKwDbz?G1SeIC;G8}^kqGlH~cWobyzia4GxGct*0N&CiZ0Ky9>HLdi3b~ zpLGf!O(b(9&~H8v1rv)B2UB!pcz(3CJq#56iGhSVa)RNWGnVB4^5x6MEXfl0&ieW( zAd_v@-^*^#P9?p22dvNS^RolMmB|mcY%0l6f=7ZtbbmM?KZ|f$7-)y7gvZ2ZWMQ~H zT^(p#FPsH{NNDf|RX*BZ(aW>ld6Sb5y4O21v=dQTgxPM!Or6v_7uhm3xYf+1LHy58 zg6^Lss{?v88VG24m)$Ws(JLY*Rel~O_eUotOtqLqy6uu)xvhfD3r#1KMOA9-a6uCj zlOwpq0@~KOhfY8D3k*DaHT*Sp*2%~Ni?JQlV)=Y5LVp3W)E9B4 zbVzXU$k^B=D`H!k>J*AiBrsk(&s6GszT2tN&?9F#GqiHvLcoNMj?PQ9IZIEZiZ#5; zjuRR;fYU@1Rkw#J_e&7ti|wa^n>lk|iJ5(R@>_0wjIcwxY>||JC_O#>42PDJlattT z{7X{XS~-+>>qvA8BQgh9xBdGwSmUUk1vo;<9KhA6!t1jLG%PUoz#&tO6L*JVHG9+j z?D_LJQJ3J2#@qN68tylqo}M$E**Ywz1s`@@IN}Y%ZxYe!;lqp-8(dSQ(K3l1q@|_h zrc6V)&}dr5abdtp9f<+jcXPj#wDfu5>JM;l?*}WG`+-v@8V)u&4>hlMP{N>6U>%#P@V! zt};Z6)_1q1bQ-&$(7_SBw=&s`;fkx79~xSOv`L-ZT|+Fwelmy-si>gfHybexWjZ~} zz}9646A<%x6cp3#a>m>cul)eVMJ>-bG%~bYxnMTVNlF7nOTJe0-lu&(mR{6jTgy}! zK*%?32W~dN!xKS-y&oBK4=anS_B|^f!3q2>oBg2p?)qqxfi=XGR75(U)88`o7DEz@ zol_Wks^5ka!UlYuyC_*yZVGz=8;`C{LCahr=^F}$hHujtBVl$An?;OJP(c;Y&}0yX z3!qzUvr)j51Zq7zC-jHDd*@gVPC?ualX5NI1-4<}y}!Y&EnKwY1=T4PD)sv>nLt$oG5!cp`5cyMk1tmJXn?1>c}>Fhz+z zn!N}nfAHtpYHOwEdUZFJ#&6MifLMOek(E=U=f$~eJ#i#aVG{?f{M%uC8uLjo7Fjr2 zHDNJq|q<8#HxujwTS zNM|6O*wV^MsG>~IL6WT~OMFD~IPSLQIk>bW$r@eS2DxQZ`uxe03|6R#rpVmid60@= zJgx{1z|4|Jh(WH+*L&m+RH7<(IgL|zje!kAG^yAQ7cQU!aVO}`Z_Xmhk!#@s{9s_I z>YNjIb4DHlvqO_z;KBC`=F*kK-O}!6PLU8v?9)3PH372-nEFCHAZp5+)=JXTj+rTo zIyQLBA%Mk(nij6kbk4-Z05_BEwP{K6&J5S-xp5!Fa+8Kok`>y&guQgvK+$#QAU~^^h`I03#K!80mAfsSB~Lp+(VjOL#8t`L z^9AOcgI3#xyYb4^7Hyn&-sY#oRX(`6J^18pHHdcBJf)6j0)Md_i1|cD>o>{aQ5-sjded0vWF7w5U7tPVMdmu;}O`E0?Ur!&6 zxZXG|GYj~DZ_d8-8l0@nj$@L0I3;>^AvK(diz$|Qv)=2>+v}irRe*U*N zZ_e$&Gc?l}GuhrsW{$yhl6)C>LK9tH8{PQaoRxhQis_y-AqHlOqH9loZ}90HJS2O)m!y8}xdi@yYb8OqW)Z-JL(r;0rsrSpRs!$n=ne(%4s^hf zaOmA93Rd_kEIT>71x5h^>Ka7dH`N>;K7C`H$U%>I{1sYXpcoGNY_6eYM@BR6I#8p! zW7+k5NJVJnrH*sPde~?|>{4Ci)%(vfx(^&V^Csuu0g~i$cNn$1A#HI63I5nOx({uJ z@rBv?HJ_i8q1+;2+%_AU&SOLd+#C7oo!cBh&EN1K6Yxp;)~$M02&5GGyqN1m=UMd% zbn`35iR)p{si>$JPeedj^;i_A;m7JjK5dC$U__zV!T(aRQxp`H6|Gf>t& zI5_y>$kCZQptploWwH}xKks$0GfQuIc{!wTc5Ey(C}?qe!_NOMIPg4uAG6$pZbK_h zW;T2y*dwvKUe&fNw4Z_8II$nR%7dI@Te&#hN)J89+5-SFogYUbmmpwIbMw}%w6G!r zBnEU(Q8BUPYEY^QZ3eT%p1{$sKPF5urHMBV0hPapM(9yI3L$$T$x~i(Go#1a4`omjO+}YkH#huhm@qnq?Td)HBWm&B1=^5I2HH&^; z03WX^gDD;D?fde~0v&ZBfTQo~57reQV;w25fwL#4>asI-TdHUFauQ`=7SNiI@$52>BrJX8G(d$)5KP46KCUJaO@leM2OJY+nKH0)d8v!h2BsQx~YK z`P*}>6DH7Hhqh0-wgwDO3&ki5c~Q70Y|-m|+^PV-HvvWPO)Fm%a=`<9wgCBTe9BjzOh|)EolB1Oo#DUUQx4>XwT`pJB+N zm{W=t$b`|B=alCKVd1+3W+3N-;K0LJ?6$M%V9o^^ns$YoOP1da{3Mxevd@cZc9_{R z^-3KL4dMD;QU@~&=!a6Ha;EgB@J3Re^6ok#9gT$P_fN%YgP?P;49J161{Q`BsH{Mc z4e_j;H^|6d!VfX$v+g9s0F%1H^fJHR91`jPGB+QCk7uo=^~Qc&uHP!zjtN%YjeDW*;3ZJiUL?=P=KnQg+1VVvDnnvG* zE9CGdlezui7e@A&!s#PPDO0q{dAL6{vh}Sjwz9Ia;6}&*%!p-WWh*f!?n83};5S%?f zcnGY#y1Ke-d}v_-1>wF#%g;TH-^c^##Hdf5GIFkoya1N286(*4d>P_y>yK2)jM~@v z>d{66A)GusDx!W6?I@T-_;4VdouO25kP_OKKfdn?$%@!#FaA>&_)(xgQVo&<|H8S# zb8*`cSMWl*LxS|43L}%1o}!G*RU>*}NBs+fl{WU5yt2rSPIqKR2M05hGEzhQVtcUu zfX_>VD=JyK#V{_WsLy^+xFG;;^6Svhq>l)Lqa=qwWH=eL`}0!{1}@{2s#Q9D22baD)DSy-gYhS;=^n;v3_h4L)`vB2C*XonC3 ztf7%J1zuIS-&bn9xtEx9P)rO6>d3j*5CO3Pk#|bnzQ^wCC247CXrYK57I+BN9?Sf{ zv1a_pfK2dWrh_lbeu18(BXp&{VeEAl68mQd>e{ba=O%JIKc)G%CvH1xo$IeWhPVji(UBB@1@;OA& zd4);3uL06J?weC7o0;6XI@(DxuUA%izy%9Qm-knL&cbxm^a9k(#`b8M> z{r%M=9LjHRLMGLVoGKUM7SPxP%`i>Ciwhvd_xdoL3bu}CG9nlZ#t;Fjl=t{xvi#ZC z&zeMNYkC?vZU_M;Lw7O5#K^k6vjxR|Z4d=^Ob4vYNi3tLrY6Oq|Ha-{##Onm+q#y^ z4pc-Jtso*HDj^L9h=71dw*rcQbW52aAR-8&#Ka(^LAn)`4oQ)eoT4Bp9e2Fw-us+; z_gQD{@0Snjw?0hgeBXaOF`nlcqgUPzzl&C2M|e5uL>3m7ZS+FQ=eXH6u6dKxP0*BG zTmD(u`1euOny0_5+5J?=&Sd^II&WVsH=aR-nptN8VVV&){xwl&CJ;i$pFr&|U-h#j zapxM{Pt@r~^_n%_2UuB!g@t31JFp&K69mxDJEe(oyurc$?OTTF|Me}XmvxD{4gl-? z5*rW_LPJ9nqWi6cLfnw%Kl&DQa5lVsyWebJetv!#p~ji`;jRK<*Hljr4>9NI`^Jdm ziE=B^VDzg=`vj56ID`-sib3U4J>SFg5f@*9`~%+&+8-a;kiS zIP2d!jFDeC!9(gbCvL-C2nY&B9qy7r?__ke$!=H$dm9^B4sG;k;#dT4`d=T(^}jq4 zkK@?qsULafokG3=SZ;Za!Ox#l97c7cSCukXGnHSE%g?~#7b>!!_1(`J(mtLwH;&`L zj?c^}XcYl}azS^qeky>Vn25AS*oMJv#OwXfU*!2;zDN3l;kmf{pU!X`FlX8yT-d8~hvF ze}2)LRUGNRzZChuJPQLFSc#7Cbd^PvjH;H83keC0Bx`0~tI+<}cdWc6dUyZ(eGp{( znPm|7!S*$5xW-BuFyScoWju(+3as%QE9E-keO7n;FT-cN&+6YQwEm{;-=2te?*H<)C$9Yb?3ym(UMDR5Z})oj)2n-!GX3v1;GZt+-#)ORnVcx8 z!)f{T*W?+lkJ%c})#7H?-xbKo&UIdxy}NtUcK0adf>K`(-_f*r8^!xMrw+*)7cX~f z%7rWC=mZDY(X4;W79qSZLOtJU^ZL8W_YSpZ$Er7(8C6H1-~HfbVh?|1&+q?rQ~uwz zc=$tGu;!pVhbCWTeFa;vSaDqQaPGzT)mt2%hzd8v_eCG-k{fkd_j>vB%j@s1{$V&Y zUoSXGk{7a#MwNV>Y*2IBH8l2}idQxjb1LmGA**jEHvX3ZOZe4yOBk@#ztzoEU-$`; zaBCAt<8iAHPX&_pm7XDTw|-^9r(njTk6LXKh@M{OIdbzxNhu@ptbv+!Q)igfbo6YD zzmI1OHOI&1Mp@29jo|O8hgOf;Pncfg@V`#k>hJxx-MO4_J%1%u?`)6u9!k39%6n$w z+!6St7_oS9ej3k_D+Qr8=9iNxS_awvH0-H7eExMc`s&iS{_FR%|K;~TBhXTgK1*w> zk@4l2YW8V54z?F^tS?WhbH>T57T(#ZH_Fi+dG`hCKgVNZ?RmPbEN!bF262G3etLm( zTVQnUWH6h@)gK?7d5CuDe@+{u|LY>JzSVy__<{RFT!-8YV;FL{X{1R?-SbAf9vRgQyNDvL{zDwLOV)Tv=k47G zSJ4*ZU-?lv!2^cB)mq|>jEGf`x!jtCMV!4mQj+W2=g*%%Sk$NEGP?SZr# z{f~C_RcL+iFPDE8%d%Sy{~?KmcfR&%U2-yRk)86z7VfI%A0=PXWBFyMSnsSzl!E&|8j$? z3$t2bxH$4Nk$x?a{2=s2>f!Dqk@uuYG-izw1vitDui(eoneo0QL@xMQEZ>A9+OC4QkA=VWc$$Lt!;+;LFtK$Fc}NaaZGJ$W4w5su8OM!sBE^xXE90?u#UmGN!E zW46)o__Y0}Loj?0ww(ha`-dc3on4!13I+gs#W3Cw0j~o^!iYwxy z?=B7PcCcW~Snp{zchX&HQ_<2V*9s~B@ay*$NJWm)JJ;&(^twvHPRpf#TB7)dT{}*; zY1Dbf)RD3_*9|^NYfqI(q~{IPj_4gqzuNrjOi8Jhg{wx=?8{(ugD`CDS)RvVk=^8;7K{mb56ZVQbfT47B4shA9Xx6bJYpP8GM-cplYEp+TO z=QYdn;O?M=&NuZH6H8W0etIIy^7f`GgV{#lGmdN?a`U3Ge1t ziK?<>L46gW{G^s**e%{g3H|-T0zxK!9gQCqI`)N|O4ZqV9XR-Vb4``(d=_(&;KS)G zCeJ4{QFaRyS&ek}R!u)qq!?!UEARF+239t9JJcyn^EIpybC_78G%O3-d2jUYtnmTB|}~(Ki05f-94QA{4zwBdr$> zIGiYipVX)$O}5QCW)#C!yrnySz{|2|bkef(V1okwcS!at6hRQva$;hNQl zCOk=F@95(sMzh&0)z@Uh@&914RF%lDMtryw<4K8U_#7L#U=VZ8mDN|oq9gl?MZdg& z6W_C|#~xkhRF50JpT?gyOyyNXk5+9-J?S%_Yytupn`s5OgL8Npj=jCVF&2*cb$v;V zYunInGe7Cu`wh{?6-5*~G z*iO!1iE2nu=qXW{`*DlKjglw2Y0O(<*U>4fNJZSczS2~B@z@_Mw^~y2v-?pT9IR1C zV_v$bTw5b1(p>qD1n~_EYKU(@Kk_fTopS^6R5QjVyU_RE;$2+f}7p+$Aom)o72*Yn8l z4d|iOKD+RaYg>`@w|6-YYHOpX5ABJ*ySjEdsdLcBIJQa`R z_tUPVe^_VDFA?k@e$rtAIldG3EB?YPWmYVW&4O3qmGwye!#_`1WkM%-szwE={ z%n-#c-$z!udg<&1>Nxq$-rL$IZZF>sqobqa9;7K+S6}Xxl`N*x%0_Om4_X^!x6N~64v%ttBd{4zY2}2F%SF5WZ^M7!>(jBx`c)4c&qxx z)fZSaZrX$ncFrjoDg+_T;|u*yrzSNX9k+7L> z??Yh`Qwkyoud^Qv8b4$U)3{Nl%~`aD3hA0#U8LqV3Nch<;Z2KnQQ1PLro1!Z{9wVHHcH(s;tBVqt zkKL@S+P|;J!|$Bo)^JaMuT54~Gv^tKlJ&CQGgfMnTxPXnuluT)>A$`EF1P#yzf!KK zRNVY+srbZodF5YM5sP22lGIP1QMi`c+1F_FW+rh4UpJTKepTMwyRaMN{m0JK%*f zLWE)(^Jh5G_XN`#^0AcJFFAxlvZQFovZk!)$rtsfKHlZHeAl$_*onlkH`}`Z9EmRp zlQfC>-8lP-hgoOhV7{YWM=15xIyBrwYbLHgU20ex5{@wty|b5^PrI;7+!Iht5a{>O zVLI5%t~%1uDB>(1w7oM@_1VhZnz(Hy{E{EgzLVfm#HZD~nF98r?V7}o;TZKO%q3`4 zM5ihR`At~a(Pk~Y(paO5O4vOaO z)h4{x@-_GV_WY8!ie@QmLBDbC;v1w_sfI9Hd|dmHZx&3f|6;plD3y$6wy&_Lwa#W9 zqGg5ufqUSU?|#L9Q39P;FQgQdIknSG0_%?jns%I9d2HwF+ku=)ziYkS+k43~m)~;i zvJK0VsB`6QCQ&LM*G_dgaaH}o1hemh(j`j?vsbtIM;aLV;l#KYNZxi;{Kl8mbxRC; z1|l4g*0psuf9X<7P0Gvin^tu{MtsL&Dy@C@(j3Ckz`77N+N4&u;X+Ciqr-B`1NrIw zIK!_rswM8w%C@C7JqS8xn`PW?vpMjPS-S9+5y9!8wU{*f39H@G`O8c{ZCEg^X`1Jw z7j;0zbXY(J?V$*jVIZu3vUizHaC2-pwPk zExqr$lP{Oc@Ya!(3bYtAG)EroxpL}v>D-AAzzTZTc%u!s@zi2{mC1%L66r&R0irRg zqc_jwZ8@ZQF4a7hCS%(citbFc#AGk=aBj}-EA#E%cV(O6j8a;_8+)a%tFVT87FlVnqFzMK|Enw6|Ut}HzyT(|G_0B1w-Apk}~~;72fWX z6$?|822$Fcn45pDE;$mp z>!e}X_qNW0!mB~{2YlRQTg*QIZAgWY-6yz6x3oj+b&qEZxDIHL$Bt8(ov zl1AxiI?)G_7wl+Z=?Kl4{gND>%FVfj&s7U9y9CtwcCeW8 zAHAO1=O*x?k@Scvshru*0{6nS)h0wPEc&GG+YsZrSma5i=91ndYf= z{|mRGiqCSTu9ui5pBUD85ffl8z&>9Gq;YlHOkS|80a8mtst5uC+a@S{hUfOfV0Yd3 zUk5_dKZxrjM%Sihik5x<4{U^oMSFbaO3n$d24V?jKTT`5Jg*;952wxHK!}V1*91$+ z=l;zMZna z%20X~9pO}QhwtLjM3 z+aFSgIF6;aEgNmvu%YBMV}eoLP|mOthgtuFub=7;uD;s5-wAF!pM8qtiK|b36!YPt z^CjPz_7^4Q%8x${(zN^Vofmiy;Jho{3Fv?X4o=K8BQX)}Qu9p(9*&zF+wPvNKMK6? z>BGBjLQ%QV$6XAxz~Ry(<6l{tMA1@cs6)*wld6tcmdo3`^G%|v&p$JyUTWk6E@#?q zGtBk(!?r_gbBgFwHqm;vkg%++z>E;+h1C1licigQexg!+bUU^j8|J`d^R%@j3gZ%0 zLUp6%hu+f*#2(*Yh&bjm-){8vV?w*lN}Q**i}!lDGG3e|tGcEt9x)fSc^!3*Ymw&R z)Fht$Hzn+cljzp_^G)#w5V?}dl=gIN&4^$kW!Hs-3mG;F#;MP&sdkU%U9yc0FEuqik0Km!Ex>72dC+rbC}RjOc({R z_!m9#s+*hC$_k95=7uw8Procs5Dsr}eD?>2VjtToTYN5$?3Y4F^VD_jCI;v<-vf5! zbI!~=tlK%zNwKvV;j?inJAJOk_Xb3mk|VovxwMUKoNGg+xY(}ey~{mY6sqJ_0C4%D z`az)QCF{#v99m`kff+nTW5|DCS0rhm^k`~k+vt4J>`a{RY%MHqD-%C(Fl-pu^N zyMMkJMstrf4_ zO$XjZEd_D+_s*h|#q`Z|uX{r8>jv zu1!hIYc73=(=OU(Gd$ppzFP@D(yMEBRyGl?b0 zEJahJ8VWd9o{7eEha}Rt`p#aDpjK0U>8zKrUP~@~vf+~!fFGIC@50a1@{TtS4i(vh z4kkKW9tp?M1S;jEV)p{g3cuY7N^zJ=)4Yu!t1>lJU&sFSfA&tu`aiVx=oLzDnG89s zBd(b!clzhl@OKX@ak^9~=LZGa{-DbFxHs`as3&kWxbIP^P#6$ulQ+3$o28l$h)-!=@3($?_-+?Y#P-f^-6=V<*Zf9;H#3rM}vBQgZE? zSA0b6hi%Tkny#OqLJ%P0K%1z+E9`qaMM{OG=e8W+#@^;^83&mWM z9}KTrHN&6m|Adh2?;FF{60ocd(?_24e=ruXO|Uoho(==1=6(0&^=Y2AzWX*=?>7Ep z(7(NsWUyDwkR&Q~c0?jH|E^DMuC2n$m&dy4RYObsyXlT{>J?~3Xt%_4COB-X*{H9- z?XvoXw-ferH{G9~fByh;sI}_O@&Mp*)M#p764Xf4Yej>T6@|Y`oX_e$TEc#rrZ7%b zZ1B+$-|0gww={n|wy3Q|*W%6wlH2lTG8f;~3%svAYI9`NW>^^6w!gd>`}(@MQs|Hc z#}$t!3j9WB19BZW@y}xL#l3%BU8)gr&pT1U-L`zU7wqZ?i1NbmF5BZ(8(&eKswXwV zc)u?;&#@_T>JM=8{EpsY=t_p< z>^$3J2dkMUZU^5-3aI5Vukc-!X!fT{YRieyuK@b!^{j))$%} zZ8pg9@-pEuBMpD1J#7cH81vnUJ2yy`DGIxBCo~&(UC3Z}^4T4Jy_dq5VM9WD^x%4H zTWjx52c~bWHh0kOVydoK{p2OGi3q>e>6d4el>AuZX(W)GXypSSShVS9GF4ehr}<%33s@iA^KoE~tw%P@ew!ZmXh*$pK&g_8D4x#Z*b*j0vzY9Az%& z$oTrKj_Ke4G|Mn+D+sX0ZpNFnRhk7mPRL(z|XSNpO| zSnJP@k5w>#*96dr3^@S>CG{d2hNmmdm3_3R^JspoVXgb{DimS=Sx!V13*Ag0H=0Tk z)qH<3B~g?WHVRfzbWW~LTt>>x=0swy!--{G#st2poGIpA`SLx8#=CX(mjrJb#Kg-cuzR`+!nw4T9lxt$0-Ys`!ogX&AI(?W8CoL}>1Y z%AY+y%)&DwsIlS=fQqO!A>J&#K~G1&wB@eH9kq$=<;nD{MB&KtQ1#I63`*WGoz7hf zI{eJVc||j+ZGUqHb+IHCZ(7 zQeH%07TU`+HAj^7n;yYj^BhHfAobvkK$uE>yvu2sI|HV<;EL`Y1@V_Ab35PB3$PvT zn0YBj{l&)W$CS75SYGd|UOyd7q#SKKg@L@0J{_dz5k1pknLLBEdVBNyz6#!zqQH>{ zO~oV3T)%W+;1*i`Jf>#rq2D|D(dG6Q4Kah|yQlj6bZh_kIk8RccXrl3$&z(04+aXy-Jub_p*6|B;#U4dvLF2x&!A@kclxi{6~+WE9n(8IG)ftR3Q$R|e#c z{kSe_Lm8UkgNONq^MN!yZ@HG1Euql^x<$hWbu5T>f%s5oJ2uwU7>gO_)@qCZb~jSv z-{L&Ajf?lviUmzUYYXWm`S?&oYI+scU$F&(H*`A z+@Hi;eE3N#cJeM2V~(r4rjmjE(zO5t+BkId;bGO^a`k=ed1^Zwu8K_J#pZ|?+gOw9 zm~|n3`xVARjpPzv@0vpRt~OMp>L^i*9d2zA2xQH6kvzSkh{hW&bGu`_%YU9Z>jC%C z#(LOxW;)gKjobda8(a%ZBE9JItzwYp%P#I!dZ5kQcGqk~5Rl_l>JU0QL^@^-k=r^K z3oxeV#Df96{5Gd^fM_-jX$*NIxGMj<>GF9G)A^@?z+|3SF`XgM-ZHxCLfh_7HJt}w z07q45aWtMbCFAs?jOm+sjnY~EaRE~mMr%}V7%@39Oie?}PHaG>T48y;c-DBXny;DnBQq?qv4JmKAGjmq>> zUISncKh{5J5|AsEnGUP)tixm?aJNT4{=T1OdH-Fw{%Y&&;(J+|PN|VE!`kR!;a5%I zzjolYlAk`GNlbjj6Y1-@B&TJ2oJ>)&6sEsf)4W~t=e^&5_uj~K>yMqkNq)TJy-WG1zGk1d zen$j#+6$(A@kv}?y}6!9>PYG^o2r+*-h3-bM%ntxniae6i=$z=e4?VwJk_?1B6Uu6 zqFhTV!&zOU%0Ke6%$MAx12XtV)b&e>$Fn;1Y7@-uE=B~XGK5Xv@#&a~nDf+GUa>ds zJm_Zh);{m}u+7_nalI+ZYx>=N>&+fdhrQUVW+yz5Fha}gy;2%hlAK*AVsUVOCGXV1 zBIV5Kme?)>dSow~1? z=>{WCX+5pGfc}s5GU}~&Qis3antT%_&UEq`w+us{at>pUiFL1tp08}iKW=!FHe`@D z?w61(8{=Pb`Z8uJM91??@*~5K&{17c@uK<6_Vp|E`BlHs&xd~)ofl48_((yO+S%@= zZr>Hje?5H0^J1QGR>#Yh?ZShqi8PDd*K0I~>*Fm_WXb1TeoU`$b>FHroTgXouN(Cg z9e45Lac4YGDxa5C+ns726zBAIrPFvuHF5i}P3yqQ=pwm0cd3j@>-$~Hx%P+|M!~oR zV=_99jmCnSlZtFC5BICa?$X}gKGJZJtm>R8-20^4H~V{7j2>Hiuw#WOBZI*3t>^{& zu1wt}cBkez)p&+QsfVoe3lw6;QxR2}b;0N6o{dJSWr@eRk3}_E)W5zGK5~n1gEPah z85Y!mM!@!SXJ69d=WjL6FOpWi#ynzl;Z%1@lh?WJ+tTLdxsURe@Aup?-NO_DU!K_< z5nM<<(k?(>j^3#Zzpm|1i&< zXjj(T@5K62i|lBQ8V0wR-Ad*=q}OaRu)(?gRcB$RydX#Ro2&_%Lw9zx*{o48Rawu@ zn;c19x%U}2HtVz7jy3Gp8JbAU{~UCBF1leXo<$=}tzqlHmh(ghtflzIHB_P$CUpc6 z+|SQ_Ag!Hk4&bc)x7+@Yuew_I+oFl|9&!-x>AByPiN4zos9#Ce-EOn_$47o0paVXW zG9k}heq$$8qQ7tW?b!?RNm!(R{2KA(MVbNcomgtl7p@5+nuTX>uQ^{o`D~i**8`ys zWkm3X-rAdrU)h^7{|4h@pNq%f-r7O)7c;v)oeCW;6 zzBS5IBA9^t=dYb|-u_ul_O-zOU#9=RO#jJg+GmrXox>f*j(It`D)Vc3Bbhy&0zaCR zT=_m3fnB}FA_p@mS%i3xjNLX&*V;&h`KxR6^tAK#pfjLJu+9V<3J-?$gzi=m zDmMNKkRE;Xf=}ToH_KWIJusibf)iGtQt+=XxZ(B@L1mf|!Ml~8NH)W%WqY_reF)3w>W;%&3p926sJG|^unxkdiT z(fvJ7FewWnx+u%)%fUL2jXje^Sy^!X2^%yJw(3>>VLSdW^U@oNiLKcE?sHXo$4BQg z$mfmI$-CJB4HG1U5YNbN!k}e=RbOeqne}Stdr~mB>H=*DYP;Dsn+q?{0#2Be+P|_E z`wBq?=}}aPnHBfRB)wlN%$-FJbLeuClr0!5-XeX*4HQEdE##p4gb#0{J#JXrV_GNe zg|awd`~?5XuHBqv&lNSo6jj|_k(I?sW5IJHf?D8)y<6ErnjDIW7#tkj!}S_Ni@`!- z9aTKnr@mARx?ohap0t)Qb%)4*WyN;rNoJ7^VI=?YjjP*u7_yUofkMleQ94A^oo&^J zEj23t51u%k{1D93yV*}W*A}fTjfRDV6^~<_Q9JXRM%cCti|)d!9MFig^q~9O=i;7% z^L7m&p3lJ#xs{qb^6Db!3k=M^Kstd_GwyQLV@#n|-zoV>dbDdqa5dVnuKla4Fm7D8 z-}Gu7TcC+^EynOBCnmg0izX^Lj`8zi$Ofk%@Y37OQGffCHIh!ZfPni@^K{sltfhUI z6B#6Fv+Y7tA5Qx`9_cP}Klt}wwP674%tcV!A+&U$9@CtlVFNvw?@h6d5T{ve(Xucr zO#}x&nEO)8=xojrteY6H4-U>M*)E)ZwZ6C2z>i+gy8k{k&jn4b(o8a;EV?*5ZbWh# zEL`~B4qddx#YGIut&6dRnAx9@7b1kqFt|2rDl6+;>(K2w6C=LFcJ{`;Z6W*?x-b)Q zD)H;}R39Cb+b;0=hE1&?8RS;F_^HQ1{e%h~SKjbzOnkZ2iOy^d5D|FQY2Q9_kNLqy z>m%hW+{yMJjZ+XAe+T_4A&2jDE}d^33aYsB{?J=el%j`Lz^e$idA3>^8MC@MviOP< zq;)XMBbu#y>;C3x@mAF_@!QPvW#Y?pJZtJ}GsmljliUB;L>CzN*Fw8`-mo@jQZFH9*`e{V-VuoSYV94ihXEP%ImQiHyCT`2Sol^b%{nwOkfgjif?1*=7gX3z)@|Q51TCbcS z2yXN~JiT4|b4L0ZfcgG3DBUjn!DbdY#J zIaDy+#xdtm53Qlke=02{fqF#XT5AdopUD9|OyAak3Pr-Gw|65&M9YtXcY(~%}N)FY8?7!DMlATvS% ze)uc6v-(MdqTw=xL)~OkMUBArD#`57W*o!azqophJwPw^PCG83-3DyEIwo6G{6QVL zE@!v955&X_7K8SZ$RMYSwmpw2zs01y?=8-4i$;qQNv2k1f9z*(X}m|M_)(2|dCX}2jJ zVYR}2A+$1UHRwU^L_J*uKaWXXyRaWcP~~OIh@+ZzttN>IM@@%8*)HvB|C^g@s~%1} zbXZAc7Y%`?!8c9$GT?LuQK9DKSWE+h_m0Z#9^72EHeZOTr3GXyHz_F$^!M*xdHDs;G6`ulMi}Pb zHqf1i{T-8n_r$$jp)L|N!mYp!o{qiem11x* zoX}y9qtlfhYRhvN#rXC*qi48Zo4%D`Fm#0=#L!!zwaE7m4i2`YL`VW192?6K zu(XH;wBCKs+F|u5U(^UxVB@s!WWXj80XQ1d-DMha4Q)2&g>!>vy%8&cY?FI71P?={ zT>shqphz8b9S_CsOeklI2@A0N^z0n$_{bef`Hj{I*l-rtAPkms{jg+k>h}-O&7n#V zRCiAax~Roc@Sg37M?f;-y)`6gS^Y%_M_hnNTs%m0`nQC>0dWH4cNiEl!DB&4m@2+J zBWVzF+>EA3G{$l72^eaw+%GS{2Xf}jG8CW~5TuS5`Vorf+}4<4o?Q88mesk3!38(C zh5|Y;R&xrS-9h~uPK5)ka$4y+uPlQ zz>zej!iF0?37cQkC@(CzCkHh5vYPIiSS@@Lyw%=i2QcU8;;4WLL2}O=OvrYTu{XW0 zzxi+3Ka5A>BE7=4_itcyIqx#y$#DJZco6Z8Q4Cj7yk2~NvM;an0ln6u9MfABm!7+O z_gH+`Xw6wMcR!tzif`Mx1>!o6l@r9ibb_xPze&JW1=y}xw*k^2CB2~nkYK&V!L&&A z$G`xd)*Kf*;9%Z?5kXAUyH_W3dVPNA6L8=t&c1-Gil(#tY*nI4e9JcejxjvgnWTh$ zb(GV$2&paU=ldws0uf1UR2gqcp$8cC( zV2NWsGNS>y5=+HlXe+*VU~?)7grO5*`~AjWMU`4Am(LnF*KT4B+cC#H25OK%UPokj z#I*Np*`#!v4P^5VKcH0Y6y&D1(=*MD!TYl4^+Und%S8k9afgj9v1fR6-C0KXM=h|u z1jy0BX8K!&i3PI$u=wSn#db3y=~TM_>8S+ zPSoNT$wt&ksJjLH*x+LeHA***7UP)kJ=7o$8`-Pt_&jnPWMg?>UiehvJYfSYD+Kw1g!Y9j) z%h}@59ILea;e#YfH%y*QAqAA~-MxD&qgeiId&ppIxK;zx-?OIsbw&h1qvW%=X-dbT zGswjs4nD)U@gR&l5pPVWwOB3N&ttoN^IMD%_Z}MZE9Hq4Wo_uND&OxJnA?`sb)Ccd z;G?M{%C^q6ptxYNxR`^$qL=eeCkk1@7ScQggn9~`3un7RDWC8d$@ecY8--b&DfTDQ zb;BhD&JMAjmN2YRk3*ncJW;|K7!eT>$@cPWk=v4t*$SKT;$2k)Cf2dv^pF-u-i(JG z+T?HzJhT4VhzwQ7Kc0R6DfI%Jm#8^xJiE@-GwHi}7(=hamcuNhP)x-rFL8f8TDlF2#UI(U{rpD{u%U#~dsm8|;6X zI8Aq$iKUzn3KAa8Pitz%NPi(zY0_HgzHH z6j(+S1SYck9&Dl55@6JlpkZ}<=dknv%3B48jb{k3M0zKIY!H+X&nD)~eg{|@^mr94 zZH;3;jq3@%)tz&cON1o!;A`xecgNAkO(aUEa484?^^Nv`v4Y8>OK`f?fbv20!|5;> zC{KqzD8SxJ8$>?167mqtFpqg(zi78%(m9ME+NFw3WkB#O}${Q{bmm+6be6fe3!Xd%Yxt53pV1ooW2r=2-uouLVig1>@A%#$%l0vZTPqboB$Q={KToaN_#ETg-zBBs#H0uw3ogz5AWEJ(630 z+R{5Cf-p-y`B(d}?+V7Oqws>|@(1Z2?;jDwrQJAM3cqrER1DW!{^1&q)B2fr4s>Ll zGp+Mlsf|7>b)tAWtf$1&vuqe-Pvd?}6JoKjBYbn0)^p)HJ>k$xxp*>6X}5859`2J; zJFp67IcvPMn~GiGzMwNa;{4)*mK_ z-hCV?gGE-Seb0M8FwvDiIUA~i3*DP9ni$zgHQV03yoZ{{s`uU4jj*sVBxGgX5-H-) zjNG2w0~yV;0vG-jc3_d#ut#9dzDVD8PkoG%Cr`fZKHcRqS<~;CBUfRumF+ts=fgjCuM<8Gp0Tl% z&+M{4XbG*w64QJAa|rZ`BL>OjWX%lrs2>C!n*XEo7o2M$Z%M;24Mw`hg1+#}l)5U5 z{9Gz`&N|D#JmY)eFp0?f4Y&FKM%W_96EdS2Y~ZAh8Kb!+$ilsEBIy+}vFKbc;0m1i z9@u~Jo}<8l9HIONe2^#&ymE01U4z0y)WB-tAv3P>A*?w#Iz1@dn_elh9qz)DjrDG7 zUk!f7o*)N`%6)`TKjA`@j3WzJnGTC_o^G@Gj?>Yb)$mr)9o#~pml?U!M2!${gSFL* z&hAVGIdG~W>JG3Bwq^NO7yWA-#eA~J??(CVp|Jv&cI*k@$iPdi%%k02yXz3*-W7*} zg+tsoKbI}-fgP(Ts)r|>O*~}6&jtx>Qr*M^>hulU&)IErU;3uUOtwTADH)N3)zfbL z{Yd`UskqQ=So;!F6pC&$ttIXR19V;&_czNCsZYKKvc(O1FOFaOmlp;7&6@Lbi^qRM zu|}q9wOE5}WdGq2L0ATZ*)b)vV*lz_j!+W zG68B&KmibuAx7tb)Na|sd1zA15F3UAff$YPKsBN82v~S%K{yF{o}A69d~w zN{hboAj#~wACu*X5&vQXF4`DTGE(|&e#YH}uGC4DSbj;ak+FkJi7b~L4_CdA^kHIs zj#yjZL})!|ZP}`^hMR%2f#3xaBdIuNr&x zUqEEZw3deV>JEmZkkM7<-x(NE?sm|H^6}58Zq4LAEXj$%$#d(^Q=e5^t#1(iFQ}W8 zh8Pw1VXTLcMTz1IbDJNE$|Tbv8wg!mv3j?bLgc*5Y3c?Nr6Uktuzx;X+yO2rI|S!T zLkjLPvkrsOk+_PuVMhw48h{gt0qHpK^e}C32ajKRw9ah-yihNt*uuCLgysN7{-or87FXOFqebUIXttFX9G zKmFgA0t{&7jIAs1uxyDvFMPPzGel#+>K9tDb6=0v0qz)4)-kL+k#7w4q+k0q0fOdL z7c+bsN%Xl0v87(0BioD49blnuCfR_Bwyawwc0`ak!;U9zuitij^r;`+75JXsBgAQl zi%)7BRvSQ+%+Mu~(m57WC$?~ny7n}3@ZNMXjuk`CC0tL=x(Ywug+J`LSKpZo!Fi4n z874UnB7Uuxf=2x&_jKT*ftQv5Ga4VDgkw8DHH74bIl>K*+T~UmM4+-J-0VYVGk&`9 zC#&{?`=Jr{jmi_b6RRc2Pl{05ZcUMUubyXb=45}3IA>H`n(2q4_y`~ZM`9ScaOlz- zU@n8bw3V->!`|R3LaeV5QO9hVU58a!&68uyb0!?9Ye2x8Oma34Q|sRy?rZQsFdFf; z7B&V%rg&-r-NhJKVvD&}A9w+7h^UtPpF*d#H0L)jk3P zS@75ya$Q73(hyq%iI6l3y2RqYYYGt|b{;v5hd5wXMd}v96&;v7dw_N=FkT(%Od``7 zlOa@)sTg0P*rDLP=8XvGzt=pR6M6KT^7@9L)exrqLFL2L5kc{#DJ8*f5|7L57!IqD z?U$5K6e2wz1C3FP046zw-_@~vyAE)|1DmTr4Is~C8$hLc%DEQ!LLLq=Yj+*Y-yhw> zfP&3)hoXTJI)S{rh-zVSmJ=}|YF{)A5WbzPS)d|7ZCb!(BBEZ%ij82~1qB6JToa+mAw640zKd!N8AT5ZAXPr2DogV} zHc&8#pejSa>Ud%4>}xFPWKP|D0ZJr<5N;p4feknBvUm=63sl5o?8*Mq9@nOQSQ>)) z-4FYKwt4KMS;kKpD% zJHS>lfBV)BUFnpZ@*999JvRK&r_?DiMkUeJ{{zN0NYnjSjA(=Enf97L_NpWnUxmaM}9$-7zdyofjoR90ouixJ^}pPZ%xRPd5eV~GdR=V zC}=Ynnd?;^8cCRc>K#L=*L%HKE8Ta$B?bTG>uV@ci+S}R;OA)bg)@4u5xJObcNKZV z>=+SP2JXRjcFLO75-k!XlpWgu&GOC5~SMW4=62{=h*8Znk zpK(7-S}l9OQB|QXcCT?8d}^dxduGGdKX-^N0zNvt{JQG#;i`%XwhU2nYz%3$-w;CF zb@Wwx;O@WRc_{eT{ZT8>cmEDEu^O-y+miE9fMzR@V{R~1-Z>(O3sZGxI_w1Ul@fzn zQ?f(KP#?eC{t(fm=%O*|loD+|w>q?tN)HyOGq_IGj~%>aaKD?Z@&)$L=M#wsbdh5z z1U^xn2kqzLvA72!6Xm?}3jWh!U(vgYD{L`&6#KG>S4bv)E3F7MO%uhBK@#D*~(Ns1z^{W*YoB$ge;E;-jC znE#XE5lV4a>t6)gPY{8qSbvCVlC%IPHkx?op8X&eyk9f{MLEIz6#_FWzC?w3lvA@J zBA>n^d8l!9babH1$qOZ|WmS1p&bAtZ$3j}h!gCHsfazl`5nVA5m*pmoPB1xx4b0tw zfXlp-Dt}wMUV_F2NCAUjX&HkvXOx`n*Cw_r2(q5v^%O;^|8n6UOv%HWlI{@;Qyct- zfC}Bm0eY@u5p|lWfjOgP*kLFs<2;LsoO+Q;j{py`8-nI~&>xvjZnA z6Moh07mo1Lnzrt8Jh=AN)<}-k4w)1c6_50EvJ04x{l!<@z`UL^;Vo*0AFe_ai<>w=Uu%TO$;dFY&gI$3S zGtKQt6J_P+=O1)d0XwWVt`in$t}$YHx1cwXD`~GhIc88?wSGee0WjW>xmrimqAELd zchB}QuKcO%f>6zGEAbkL3yTe=Vadub9UQN7U$zO=M|QhuSyW^gChf>1%TaJ*j>Q4R z2>+S8q3GOqgh9Ig4Q}(F)RaTlHvdt<=uWAoW2e&3erUNc5fGXR1oK1^BKK6?A|@vJZO&kdGOlSmhjDElE0|mMMQ7 zm;$4ED&k}v=YFaPHwW(Ey#;cwhDV@XM|}3G$#nrqE3MOSQivS*&DsJjEh9zAGjPyX z#MKbC<)h~BZ(}`K!(XN`i3%|IU1@bK`^)_|NvU3Su0__mSJw%!f&tf`PF`mwro(XO z$^HHBD#B7w4ih;cM^9Hp;Uhc8}`*IS9VvlQUL58Fb^LOK17*V&+OBO1@@mzdYoGhoubLE z6>M=rjkV|=i@HrHyX!A6&QnShhdB<;4f5g2J@oowN*2qumj5{vBGN&?sqkbS&@V1#eU9QT5q?Ye zfilz$0qKFozPST(dx+|+p(XN^LX-{;2v0XEh-W~D;dPVV@hk5&aPE)?f10($?wBNXh>mentG(jDsj9s5#4DL<)gZ z;xB#&g&+crM5c#Bjx^kZmIEZI)Vlougbo=~;*6T&M+_;k%|*aJ2^T;u;=a5nd!&H3 zfqY?>+`&>HM9$L3kgrx&7n*toMHO)YBnrz;<>cgmkA4aGm4*xXHgf@bPwa7u%bCjP z9kitvb{N;iYNQQv4G`@{dlFs1&WyH=dsCtJ=>)AIQT=|#5Ujy79X1Nc;$%#sH@=RF ze7W%g3%9^*t_8yUePv9{x7~A=rrS%T3eXz@q)Gm>zZ3(T+h@3_Yh!j&XArLXZCZFj zf(k%yd?LzOcZO&-PTPEKiuJeE0WbzfO6*bj=Y7tPriwZB3iDyw-}4{C*1#Exi&(=) z5Q!verA!=tpGcI(eAf{+R$AWNuwj>5vQog_j5{iB@I2W}<^?}m&L&r)b_jU1wzuH; z$xZVBrypJW04^4z5 z7N0vLrgy-lPWj1S8!2NPoJFEhW{7Vaf@ttrhc`iGgn$DU>xt5epmVUcwRC`-3xHL- zuT9d~L9ky8*5&&FAx@x$qXTFMinhBMq2Y8J8b${-R=Sb4l`}zcEB{+k-ttt8INX>k zVknLk4R3%=*!gl=74Kn0uqZ$=2v}aU6ua$e3aF}IlHICn$4=uqwjx}JL;1@%v%49U za8W4Hrh^(E1AGl?&^GU>)IcvEgBpduyf+NDuysBFol&6CdiiPt00tA0w~lY)Gw*N& z?CZYR$B0%WXP6wK5hH+yHO#VD!zesR5OK;yPf8Nh?m`)^e|dgVb;C#b@$CsON~Bw@ zREv}YYc|2NP+VYF*f1SW0N|BVZ!ubEO>i@@vLlx`pIc2Xmz-qO;I_dn~j z31ZkL2*;1L+rUlG7V;8JNZi_={b?qUouTN9wgIF}y7^kHhk|fPhZe+djUGbO(Bo8y z^DWb<`tTKtO5(nox2qrm?>CC>%i?5iR#w(0CuKU{L2`uBoat;LBINboC;~>F-QLdL zrK`-_cR;V`=T;MyKq59&7AulNvwLL2PoRLfwiY0L$3C(ifzr z`_zRp0=&e))3iOb>VK+M^UPHi0o6QUdNx-0Y2y5_wPLR7NhKT&f*X9gGhhFHEfyx`Ifb@kIxSxy0JbU)}9i;+i z$x}E9XUm-|+D1_sq78^5BJ05D*Uj;`p8IY>0igpMs$3K;zC7+{m6?HLSV=)NW2^BF zfJRocDhPWT%0eKt~kR?}}f2FTtAON9gVoy&5GeZr>8K z9_Mw zNv(lQV(CUdP($7w5zgWgMPVb+`n0TDfN}spHQ$^GT#>x7Q)gpE$pEJ~Xr4mpUH#>S zUFL3HdUrSyMafw!PJ$naexD~X$6iqpiSA;}I+iK)F3HCF$6@`5(xA)+K2YihhVa5) zqKU6u!1R}&Mi(kb-PhcxBxh|xx4N?CKVeEDaGu4kQ3!o`7GS;!%V#w=5Ny$!^4xL^ z7m#Xs3b?*^PEZW#dK}sWqPlKER>TRSLgNl@049mjW_Fy6dEF*73XUO1$Dz0XKkR*Z zJeBL)z2>8&P=+#GjAfQ7qGTQ`rDz~yh-@Nc z_^l_M_j}HFdjJ1@-t+OtIi1?==YH<{y4JPUx~@CgeTWEzh|)L4^85)blWJTvadUh6 zfs!%s9gxEUir;)eun<+q1}*y(R?z}%V!`(32vbJDy~^JjtDizH1$=41r|4UU)WOUm zj?2)xA3o^D|3H~H2(+F;-;8_H-`f|7R0kdvi-ibk45kJt@1PF%(FoLyrO8g)py77J z13KiK8o|lbZhHbqDp#C_BDT^fQxemHZgK6Bo-_zCkpwp}1K#QAwOfScmLSgGS+VIL zEp$9!C~20o1x8%I``*^v&}BPaARE#GVC(FbPzMTphdCufXoKqtt}pJ zF$4r`mi#&ev|Dv+BU>s3Of2kP=YbDhP@)roXIJcPsY1}Ycy9y2heZ3aM+wQT%YWDM z>l|N?A=$R&+3kY<4$MTrsrvedC*e0(gL8*eR)E~P)Z}nx9}sIhh-L3@7ho8LCGK#+ zYl0pyKLlZAbaeD`0$ga$&4*?9TWfKkkAu!!C03k?8sk~`dJp2N*&6ue8jm4ATpk-k z6=A=dpI=twSxx`jjCPO~ES&0&%$JwYXHRE7XIzy%4aY>?$n2WN3HT_yXTQPj5|{wX z65j9-C?VD@BM5^eY=gMr;%t4ey&yX@pfVuCm*N(SXK%T-90Z_bgu>iWETW$sPu|Xh zGp9)!zy&L+b^h!Uai>~YMGVUO!w%i=Fmy#>slIFIQOAbUxE9oLgSSl%aJCB9+1IE6 zSGG$X<3_1^NBqPd$DtKDN||$}tjTyXFcoXyWW_Sf!~G)WaYDxp+grL|qzOXD@W-MR zJ&yshp6Oh+TV_b^j}IYmiL=0xUwTr?zm5Asd#~AGM^ApW)3;lvbQ^WUxtq0`>gvC0 zb;>hHchaC>Pqm&oJGO}EW%*6Be*7!VdW1u=6)2Sme`f|R&o_?zI83LXfzn)AxgS~3 zukdyI{M?VY1iUo3oVwH&6aH>-;x+@i%vQI*)ZTX`k3NF?fosew4Ju7HfMIm+2&P8W9R%z|Ke^qOiW$|~?_*EIHE+Y)SAaHSU{1mGbrR}Xu8;s&j5=D?n!?fffOg7@W5l#o$oApIQcMDodCMdv50 z6p7vp(|WkGU&7@ER;aof=4+MBi#QT+fy~uqLSkYLP$KB7QsK8iMSb--0>Z`_jlR`r zE07bUTcCB5W&g@@Co_cgCPvw}C93($4J0El9ooWs-vjvqXLP%;Ht?o%nFNuWUT%M? z0NbA+@mc4!mR?-QJ}Z0vLIqx|JrYUK0$A2chkD%RLh6gAtX#R$oGI`U5vk&aqcTdS zg+}|~Z6ShM*&=Y3CC$sQ%JSP5KP6*1@Xyg5|Fq81P$>{+O0e7s(!HD@%}hLKS7#PU zwg52U;xA3TTtrQ_sqeQ9?YGTfpX|qNoaY{+Y3nWyM7?(-Jw8=Q`LAl8lEndEFu?$r zO9(m|E^!w~bhyt6B_L5yBUdP*Cnwgm^Tx7Otg?rc;JJX-Jva}0&DqKOXxd<%`w5#{ z$G$~4bm$3*Skdqc{)IjL+fn)5g!WrrSp4^e$G7^QGp58k@)xo3e|pXq-dOgG7dN7t8`tE8Ow7w}b!pj}T$L=x@vXyYX(}weqg|`*y@1B)WeV&Vl~_ zJ>ow^qCcBie@n*y_ZI&mlK$UD{r~GM9#AIKZrRm&?Vj#^Tlhhiw+RG+0T3GuxPXHH z>I!#Q#jng?fMP^=_h(MQhZSs|17cuSXj3atCG)4+(z$UlD{eYEX|{hKeFAS{uvYZ+ zk8iP=@`M?$Zg`0os?n7JJ7i>jfYZF`aUR0%dW+Y%^sT3+Hvv34UEZ007gu>H#N~7N zl9FqJxgPa}R-woRlnTm}Pa^smmMv>N-+^uqLc;qT#E2HM$%|lBcYtqodnkAKD55(3qt8-63=2j6W5FbGz(heO+CYyt_-81i{A0 zt%va=*tXRVTp!)j+l~J&U6}iT!Vx8(vwivNW9u>}qCwMsc^!xw5X~oB2jS8JL5y}8 z%MOL``7OBdQA9>2e}vtO=rh@odiSm=iU%>y78FuYuOEMqW<#k>mznVQ42Ymk^j}7r z;(Y-NZktn?G&~jJ?-tlDnJRAuM;QILdel{SYLe^;kCCwu{hM@ScT|UwdGCA3HCRD( zp^6#hf%HL>&LPQ3SnP${UR1D|e06?r^xq!uGSLKha1I?MHm@Ysj7CjFI~U-IH!GWB z8wGg*FIh?2<=43d6AcA%7Ak)1K^`NhMSx}uWo5AuP~4oUuw%u>nxJd~pnu?#hQ=wn zi#NOeUA~hn$3j4H590dsEirQKa$)#php!FtrH}CQC~|{ZAs&oWvperAN^s>mPb^}B zs3c%|91XRVbA{b*;-Qo(X#Xx#bMWuFbEe*VCQ&{qm2lWrADc&$n#AOf0~X20pG;%( zNm)15QG%=dIJ6&`;%3Dz;cSSKKtilfl&rw&iar*(3Mz^2pWzu^6Ho&0lEZg)^rO}H zopaQme@!}k%+=#_U=5eg6VE`47jv z*ZOZu)pGlN{7LR^nIV$J8k6L|vU!{axs6)EtGD5Vm*PY$ zXJ99;uIpI0Opv6gSPDvLcUv@$2kc%Zk4|YS$94n661Om)N&%8qNZ8Ac?vR(+IG62h z>EC$=@n>C75Kjz$+*MEsMZgejLdV7rAcno``k<0W?X=P9`C5ZbczIPf+9LEK7i8ic(}WS0?S|C=xdc5&X~YuH@(;-w#f@FUv)&`aZ# zVpRf00YA42m!Lv$oH=y=1s0fSM}-p*%q@1r1bog6q9bL5;6t|n?k9eimA`R#W<|{m zv7aBeQHbwCIFHpc_MxamD;#0Nret8hhQ-G6Cq%^+vB@+ zmatHi1rdF~axPyZUWKz8h4GJPUrlJEj=_2;efrgwUIoHaP1wgB;6kNas3$i5eYnTF zJr>R(>b>>e;If9_&5wi6)NONVhMf(KamvTjv>t%>5>>z6_(s)qUMVgyMAH<} zvzh^JQraRoHg+Czv$9TszMRUnH9W_!&7Q^!${d%BZwTD9w``DJy&6;;-^t5r{h$5e+42Gnb`gh;?Q>V}R;5p` zy1I(3(qslSukY7asN6L@-XUfX?V6GvHy}!`Qg7+6a8n>UhO(Oa zh$gB7AGVEO;0Hc3KG^yO$BSG3JVB`tk^riC-yd`3woQ=)5|Me`-P40WCM^5PRFF_X zp~YpuR>uD%#}bnTKA*i^O6dmr+MZI%E1U4_hjVw3N**q}|6t6l@J`EQ7jqW00_@|w z*N^lI98YvVWSH2rU`@G_p+%@Lj#Q8{t_Y6H+bz-McXQ3E%6)BDYrTSrUS94oq^#SY zoe2xz^X*fF9&eR$6VrSZE}S=yAre-pE6D!mbuQ5RCL_?v92dVoufum$NeQo%JD?@P z#Fdi}I}6YBzuNM1E#CPpnNNSDzX<+x`VE8-SR+$G`URyjew|YMm9kqD3HM@N-rwhR zWGf}^>_63%0`@uP+^0eWCgu4YJQ&;?Zy*3X@9JxaI)~Ln4^+PEdH8Nv_=oBVUaL4m zP;cIciI2$hBh`5K{%B;ZIV2CUn1P!tD=Dj|5hzXgpA{x?;8vBifVog+b%PRC_8fFb z?{(xGMe-!ryK|GxII1dnlJNF-UUx)i87wA-aUa25odk zw~^!rR|2}C)W58~8T!KJ)FB7o2wvWMx?vA8tIKZs?AYCL&u5+TdA4$9rJJd|hXWs9 z+_C=1x;-IxmR>r;yXmuhz*zBEwEQ_Vf!#hhmee?K z-Bo>g6~!D^?7pJ6*{7_WYyZ)p=ye5zwiFSRBquGl`~ z?A*ytfDNs6d=v@DYNh}~ z7c@T+E=*O%YFnt@{zC`+`1vg@L>8Gl&rCY;FSl@#XRDHHjo){Jxpx$+@AUNyD61ec zI|J3;{$}6SLsLH2D$2{*TOH95R~zW->#JR>7A>oJnu3*v1>~XjkgmyvgobQ{kvao6 zZ(20Iw{eP;Nr?v>EIB$uFk0^SW8Y0iAw*ZL8CyeVYN@2YQI|nV{K6trOZ?(O5n632 zxfd(rpVUdhWwFIBLw7u95i?&2oD$}~4*^lALP$5=0~c-2PO~sGV-(6MG8Lq0`&NF~ zRZqp=M_v_hSkKL!uKg~(+ea%W6Zcj}4INZf)zVR?|KpDr>8H(tP6eR7t#}Abd+Pd< zbKj`Uryd-+ce%khJ}IfMCPd|vHDIoE{vEPgcJYnya9vy5)I)Om1)j5ypM~3oMDsl~ z$bArW80I6F5w9EmXXieYUig`ppP#?v;S{`YdS0)5e0}TC`No`DJU&VaQih0DTxhENrJNRpG*eb=q3e3JMDFmD~@S&M+!uUc1&*oGL}> zRV&nkYP8zrHQ-hEY5;}|?RT!uTX0D`{qFCBZ<|a*HuLdOdTRL{b~$z^@of|}%v0+Z zo#oerB5^--9uD^J=a{&-(rlflzn_j;y~|@VGdrxY?chWur6t2eES$~i)^n@qVsTh) z-(q9R%g$cAdbK9wA+#`QnL>}L>{m^o+cj$;&mGy8gBygs4>RY)W)C6s3E$qq+ysQ{ zr}ZA_-ERlq&Yj1Kc-d&EE9iKOJO!3qj9f&rGm@x(`h6Mxl~@U)2ISp1c9hs0DxE}I zLNM)#u<_%gifv=??`&K4>HMHb*Lc82nMPA`9}&5I(P&rSA=((LDCRH;>y&X)Cat>~ zHto=5s3@cGAKml3VBe zW4!;d7+c8`WlIZZ;A7!b=n6gNi6*n|XE)9v*sG?C2ng)=3W4lh6QyTFcffCDp&Be8 zelq>gvGL5(&T{Yl`r;E4moHmZV?kU~fBUv&CO<@@%|yLGSFpbQFb>7Vq{{Wr8PrVZ zA{OrJ#h@L!>?eL}`qwtm4v?*Im@0n+M@JjoRa)dWhQ_bDWyp(sQyCE6q3i8DK94|M zA+W!bgj?9vL*wJ)**{dk4e^nwiGKC!utSXz63^53dZ9bErvO7Qj(de*XaOiZ8KPB=J_lkn-zQ@hR$%N7sf4c8olL7>uG+a9MQ z+vgSV*ms!}owPefbFkJOU0GgUzV*5TVu4HU1GJ!w_CiO@)~mN9tB}5No;rAS<$51L zN{mN)evSoC+L7W^A-<%Ujn0z5mA?j}UF6)%w^y#5BB?-Sf%*E*9cx{_>kJ&5-@Yht zIynX8ly_gxj>^MpopFh;HVIX%6>sPOG zwANu?hlZR(l{6g%5Te|=kMle7^{2;Y>-bkxd~#L4x94j8Rc~+anAUN$Hr|Tf$zh55 z%h}z-!^6zXEOosn>dEdh6gkbeBgIc#HcA94oRIE^-KF-WqU*@ryod%@|1X9su06EH zik1T<`1cc{$*bnm{^Nzt%B11a`MIKgjg*v>+E}y{pLCct z%(HbmK^7#DrqF@$9uSr&tYNzxx_kB@xeup3%=b--cAv&i+(@sVZVqKnZZ&ph^ynUN z2`A(Ca+6&<`udW-XL4q~l5-o4$K-{)!E(ixhq@XJ_YkTC1q9?g{J!4oL&SG?caK{2 zHd*s|&QdQ3U{@;ASFrFvU_C^Rljw(VcJlNE zqQhhGE$_8`sA-ATGSqt?OTF6D`RsMcf)Wy%qQWi%^&ClldJ>jog=dW1WIcN=pfX|B zpD3$(%rFe+{Pg#fJn*MO(MAobr&_ZUjA~G9=UU)7_w3ozy%@jS`7ZzN-Mbf_ROY|D z>puVAPl{rt;*tyNHm!Z>=3r;`pzAD`-2#nPa*;wMiw!p;D{7)E|@kUCu|C~0ihnI}?=i}I?HTD(`RsXaVOqusc1<0UIGa4N_m^UjXbo7oIAO)o7uJn$~- ze#3=UzrL+1#0($CNAciA{EGM4 ztT5Qe=a1&nSb6uBDf=4%0W#c85N?fdwr=qxE?%}0hq0oEMsTB#J@(yrYms&g{mRZm zCAiNXu*R1APDDfmNSqsdW|?h9h0eDy{{VTr)S#%7qOn!KuKTt5p?A$qyt|2EkvK0zeQ65bZ!@Nf=56~i1;Bn6E zD+gK0EZMiY5EHX1eA#5QC`bsfkoS2AMv*-xkBEMVKgeR$D4URn(JtoKIL^WW;{33z ztn3k=i6|lG8$7J#e@JwWA5P!5L&LDrlAMH9!ne8~e`_JJh?f6zVlfvYYLcFps2NIM z2wvO?ukOy6ZTzaCFh<>;2T0o396SBJf3Hd*VNv;Bnp%}Jf5G6Z$^0}MSEn<2scu)D z2p2V$-23oVb2FMP-h+$-+U$jhD=waiJpGCf>pMUTFfh&^1yN z;`frPB7cR-P7*AXE_Y)VEn4KvkMdJ|*4@Y``jtpfP|$>qXa6gHzHi+XF)^j3r4vd= z(~aB^-8v@*U>jrAz2c-owgNhHmT>%hUmWHMFYS{h`B^w+ zh9KTh*duVIP+WZMVLZJS5@Q>D=Z+9tx^|MkRE%3+BUfP_SddLp^$E2?M}Z+#0h79gr8TpvpAYk4D-{COi`*cKHA4&xo}w6I{!K7 zORn!EUEB==003BNzncEzGby8QK^R6g-3JfQY&?t=`Jiwf>b%x;sH|5#O}GS`e=yRT{U zFzraU*CZt-!X=_}dOxrdK#+Zo=ecqUIxd9S24AOy{8Z0Ev*UiZ&lr%Cmmhq64Uf=g z0Yyc{6}RPrjggKR%CHevEMIP|5r~!8)mmc-FOwu&`q7kNDZ3J&+TuY#f6L$-NA(3P zAxZQqR#aBj${zv05L1(c4eczCkmGV!0_zj9UBA#d;n#UPbt>7&^jDhT_ z4d`ZPjKF%KqX!Nie90<4|J59SQ%G>|7YUh7)RRkToagFnY0-!gcklB0u9;+Na#~K~ zB=q!)D@d%bY9afCQ>nuq@1t)J(=)3sx%w}$NhEL!>5emXk6W-8 zcKt+)BEnPDw{E`8n?t1;xa3}&&JO$ZVN}C0Q62cif5aGpN!B|46GleP8!oCrJC_;}d%AETp>-Nq87Vr+G?%#nL>sM(40r$0A%anX|0 zij@q(Ihrk>YJ7jn7OFw6^DDgxa=wU6rJxus)KgNrSiy7M>9~hSaklhf)byMW2VgEd z(pZJP_&75K)-CM;Hup1$Aq|j+wuiG_=yc?Ri)X7Y(b8xUTr-9qtdY@CNtybpDnU89 zw(m)J{2Dt2z#4V=T7yG#b8|>xF1h9ea&;n0MbV-O!=Pv&Mjg6)@tj6MxUi@G6@K}l zzI$rJY}2kN=k2O)DZ8?|yR)+sT84$33KB>d>X5W}_D#JR#b^$#;(T+6Z@*t)Q|Of| zOJ#<9T>Y8KDIY|%D=5GdGCj0l1lrYTN`&5m$k$$UE<=m@j{_Tdg@&Zh$xm$tb>zJ< zF)0ZlvkhBr1Qpo^hZCx*s##_g-3{o!<7LxBNXWLVMOE+jb{;KC7cbIb2jY9Bn&D%+Rz!eW>IkOMx4gkxN5+d;8IBD7&bN8c0vf zzj?1^lRFz;o$@e!$=+P{8^9`qu&wpt!g z3Ef1!FTAqrot?iiEqY?;{YHn+aN>nERFqaPLvi18Oz{eVpj~HqJSXpIl~$Y^UAA(a zqwI+tX9tcVC?dOV3Ae=#Fuv}IQ3#{G@cZTIr2;jo&d-3!W(<(AVA||kS9KIk!lL3R zKf1=Z2d#cNCOUQ08qGY;QyAwd_hAXzyKWo3Aw*cW;Z>LzbI;gk08Xge^G=|eqzCEg z=`}44y6j;2+zlgtPea-TbqgtK2$gy~QH`Te4R7*{=l+9E5O;@;;e-+^yNCR6`9G~} zImNkJQ_0e$x1M86L)E}NbGLw%+ihX}V2Yg3^G*`_m1hFFfx-Q>M2NJK(n+*thHR8F zuUHxGGv z`Pr*%#~o#CwA7No|u#`}SRVrXvqzL(i$tIisnmiDc`Xg$VuFu{e^MMKuyZ z!g}xK<(LccvHC>z4{c&XSRcqE$9;=1_{Z?`>z26q(@S;u4u3z7@gK z+0|v&az8%u3JhztrW-%GcCEHBv{~g3rh(kNrRxgN)yC7;B04w7*sCqX(CyS?Oa2D* zp2~KTNZng`r3Tw_A5MMAKv{1&h3Q&%>TpM4Qha=Opnq+qE2AGTwaY$Ih{~rpE{NLp z;*xHmh27~k6LlvglK8pN=Q?xi{QgP`guI7m$^edR?Q>meW_I6N*Eel5e*7KV>-!IE zv=E#Ip^az+&@v=CU{teUz6Jr>>am1}l{L{W3j4J7GOx7Pm#14uq(T(ksjd|2a*NVqs5*3;!UX#cjLfH;L>{PGSWGyJ9`+h8#h1>bwr*RPLGHLnkW8~n>_BJPfiIQp*6nN&Z{xsJcdOl0~5j(#aj z6o!Ugpc4GNOr23o#FU{Z)Dl{=a%J1&vk&L&4uOCOO(XDmmLniQXnZ{+($C$2gP)!h zW&hZwomjmLigW6^WTV3D2FEHeW}#ODYs0pO6Ietr3jJ>7MkPpb{!b>NWE^UL#_Tv9 zyd?G>J^sU``AVi!*Zv-#_kK_HzN0B#$PV0O>YOh9MrqqXsUrdqki>Lo`P9?wv;y zz=jV4m@XL{D|6t`p*s2t%49*{i7tBt8UWL3?jPRdxx>FAgjz#uqUEeS=8FnHXioz!4|>b~6at7sq0R{Xm@>eR}&=MnU1z zrMIGqTyCW(t175x!}? z`pnf|R?BQp4!a;mMDQkz};0fvlK;TdWO6U6kTBCI#EJn~Q9bM0QTdWT$Toka=lJdSl^?j8|BX zJ)X){i@plrrF5W&&g2_>TG{3YeJk)n633RvPPk+{!e#8n64Rr529pSl7&#L_|F#P8 zb&)R14vM(+LliXEl_#2bk%n`}eRB5Jiy-n4!!Pif08wq6&Y{p!Q&W4L(r!vtd6(}H z>tf6u(pll_dvCLPAhi4&EG8Q{wV99or~1W@cuW3^B?aA>qVdO2i*0B}w9o6cLj_G` zdF=Z;z~L;e{jRpSk=jO%iyZ@rbWP`j;=K+#kw0b?(FH+$AaqSe4R>LOZydm}}G##!j83&%u>1Ykz& z>F;A>+m9w60pWshEC#)Ii1{K!lGRRgt$BWZjRR&& zv48HYxEUUe*oT>9H@F~BIG}m}-;usS8o3ouF!Z4?hn*qL9Tk;Hn!!$|udKp)cgKvU zh<+)=P@38HV-L1SL%&=*6RIJ+gDu>YN`5qGy@Ip5$-}D(v#{dhoM0~+8 zSx6F`jW>KwI4BGwcs{au4@OR~^jjkx=60`Lav#ft(+_)CwCrYdu1VaA^qL!s#pgWs znlGWX8L>B!E=-@n?yGoNkE|sdO0YInb?2IEwl?}>RY3J?J~Hv6Xy){@?L)8MD!wMJ zLe;zg^xM66fCU5WC0k#LtV`U(3W6GUcWPkP6}3`dNz=IpKoe&mQP|TErK@J&sfaEo zc?s_t{+JtmKKCgj9na9%iaAu-D9z$%pvexMKz=Rfx5W9JU|bIs@WYvPS5TZeX|hmU z31vGb&Z|7@4@O4Tsy)&7IyVp3M=jkXBxH@*?VaW8bR{Hee5O&Ah^c$gCVF8R9bF^I z@;i)PIr%KQ-i7Ns&iVm0HDcJUpJKrroDPN`9*`+psX6Exr?FlDZ!xM>CWpR*eSL%& z(P0y*=vCt4#A*Y(;wJn03LWT8We}r$*KIS)bk=l+o|d>eTv*>soSFl%(t^gt$8iN( z6!vhxZ8E=qOyi0Tzz$h!*dA6+aeIk^oDl6$c?j+(+JN z9S;^mS}yhSf*|Luc$Cn5`QFT-*F8vP-Tdk4A1X*IaDxGu=H;z|=?;%-GBq~~J79~a z_G@7>*~m}M{d&l$h?x&QxdKlxFJdAw1jBV#6b7Ro{9Ue_mX;Q>KtmL432!^D#igkJ z{1{aCzK~)CqpB@q1j_s;xKN-aTiNWf_?&U^XxKC|tHAu5nQ!1B5TK4(QVEpze#1<* zLYKyVrx)Fr@-sk`pKDgB^g5vb zld}UnbI;-yYC+xPeO`!-@U!UkktI`(+#{^2Y8q?Wl#K$=M?E|=ls3;;K67;a0zam- zoFWhr(wgo~z10_C&j9pfH>RD9-Bb5%g3GrrV$S%3%6kuc!otWnBXG@Cu>HQun%T*K zg8t)qwvlcu-Zak7*eD<%UT2WS#T`LRkJ=d+7_@;faM(iStB1VWX6NI%eD{Yd7}@=* z_ku~Jq(ymF%Y4dp2ya4$X~sn@$GnHo6bMF7dqDBbm%zMkz<>|;_O9SND5@zyCVw#F zXC=L;K_HDWs{drtVo>aw<@TO9cU4szKZtvGk;>5ScmJysas%UAf#vhWJqVnfwqv|E z3p-(i5!Xut&}p`ELKuD<@C{*q5C(~LdtX1IUx3CJChZsh4H^eNLofQnS?43$d3m3P z?r`Fzjv);Ybop{Go>%bz8!eujC8f$Kc@FLNGw1j!Y$Vv@plZC`zveq3#6cliZc+!P z0iV&_D`ssl)7u7h1Bn>r`Ktq0Q6?kPh>xCcZf=JE()4Y{sXN8kT#)>x!wfxyegY`~ z)ktUiA~Qvv%R-F@_K>~x0o*8+Y(}tNoF|l%QfusUq_B;OJF(5JKIQ3UnK4Eieg6Ep zcLT8R3CHc{swm9N%wOeKdtbin^6gVvS}ndtdHFtHjl2SBYL|bArmR!v6=s91{hUXd zTX&N|h7uPa%*@WNo=eOkx!R0vRsg>9rlhd1$G2x~&R9Z=2A!*uqE#`e&v*QC zco4viOWaiHzyTju-{G#VuD-r-i0mi~;9j0WQHNSECtux$#uHkPfe{JnYm(C7s@P5tR$zCo9#y+Zt>b%m#_Afs`FTd^cDlx?9YiI zBJ|saCt|^xft;kg@pDlwIV&9T|R(Ccai$p=8(MvTcp18DR#m%zLtv-f_ zoG6;8|6p-opOTM+*-KbIdmpD5t*qjBdC9(2%DW)bl2E@mc?~>uU~_E&KnoW!6lnB? zKm_W*H=J*OBD)Y;W|@q?qYpIIAa?-N`h<>+gP?DbyW3P4`es{mi|{I=^4I@tI31Gr zeo=;`Y%yJ^TcCj0W)5akn6;;E)ikw~!^tXh{{p!2!7 zx)P>NjODLh&x#_Wn*Hy*T(zdub7ZIR`BJ<~OOGXRU|PS`{({MhDG>Z8CnuqTe0G>v zwR&~#!UNQ>LY+!>+iWl=qcT4H5ktR*v|Dx4jU>3+n88asrK6je?a{+Cvo7gkP}+wLO*edfQPV^&nPp{X zPrgNu&CGZ0QH8C)O&4icmLqmHtU)4p$K(+&ziS4bRQ=Q!+NiBKmxUlC?GoR$Y{iNwwz+02hq}WxsulvE z*U|M$=eR<58Kr>Hl6}^dG4QHdjNBV?F!q(h$jRqcUb8oYJXkq*x%f2_W26J0(;^xE zVsPM9X<(qvhE)Ug8O}(|pv<{`{rZoAH-kL$lPX}%!c<;p#K>8YsUe}EuQN~V_0?bm z+X$1!@$+Eh32c{cMDn$O&(#e?dxCmXB?YWO*sgQ-Q_nh36M^k@!ekdP^2Fqnn`~nC zH2Mip5t>$F$Wy?AnUg}Gy4P>mAY=_`-wr!k_%_pF<|^f+ITO#Riq4Zwh{S|x31*dE(c1QhDKuA8U7ulW7j;cjUDxc{`>}^JOF3!7wk&1wV!tp--QY7LQ z?ko7!ykwm-DPP_p2i?@P%j!m!Iy<0Ct(^l)O~~iVm|#_x?GuB~|4xPXU{@EP01vdM zvC{6^u!o2akjG+ytYmZ0?rWWO0^cuT?k;Wh2Y4zUNgL)}p^U14G; zaFL*T5BrJT+q?_Jf>8~aZF}?W$i3%(4BKV3!$Qs1yZ5D3V)F~Li^`z~)zx(xcd?H47Lg}>B{(i8!V5svEvqJv3i_4 z$5f@uY2lPw!SpuIgmXhDiRkd8`{86KetppN8mL@bu)G^bV>*yt8SD|>_l@b`t=|oy~plYIM_VutIZ>(b}68`_c%&d$yP%`kCr zq=e4ErwwX3^@3bJAeU+l1jGHarLarYT?H95zO@D+CHmXeo`a_;5qoxwL*`(;rCE!C zEKRT$0r1tXVzO&)t%Ed~8=wcp8ffuktpoFM3`K!&!#VLe=Y#VzlUcdbxxV(`7L+(! zLqpdkbfap_U2}59vSl}dI)d-t-Le>Oz-;(4A~wFg?oT0*p@NN!Lwr@Jo^mJ7!%}h1 zZVHZ2+y!cAD7$IX3CNGw>RPo(Fh+fW+cYeqy2PPmBHOhP1#(#@FX8v|8V`h@`iAa< zuNGLAHf2YwL|U`BlUmqr8|zP~tHY*oDa!edrQ zLd}Iyf=Fwvy+DhGv&QEBBC`@^!5A0TI|>&bPwbc{!k8KI{Al4_^ThIa!TDn(jxbmM z?6o%{ys*iI+VY@aqn$f;*E#i1Em^MqQR@4hLXm7zLmghl-}qwZ=FBp0qNdOpA0o!; zY#Qk5Keuj&RU_wE>rwD6NI5oMO%wQ@tZb&QNM1es>GkDKOi~vmV|`rUc2>2zB^~41 zh|jMG)UNdgi!v)3dSpENTgYS)~j zoWBLxhXs5Q&(DfKU)!|X%5J!K4V;bsdvk=;7VZc7k4^>UrllW$@*wpMGx8T@Qa~-@ZUg^m zig`AOw5@C2unFrO>C0UO5#zxJ?x=@DdVD|Juvz_VJ4;;swQ|^_r< zJz;NB=4Qq;V#-v>Ab=!$3(uqNX4+}ZuQ$elx-)Ja#*XP(TR*5MJAL{zs&Q@b^Pk1)xf9i|CG2Spnqdq!X(uxz4J|_SH9yA8xH2VMUk+U%KPbA8Y z@EvETYT*2*US%S$Z+h9(7*QW3qtUAR^XH5JE)_VUwFx}C^efF;Gfr#{|68}-+>VyA z@;6FVy0lnM@t%XihgyT>7&PXeTh>@kskdQ1&8Bc9J|O}3#vT3i04JwqC0cKEMLD3? zjK$~cA3YkHE$kz!ow_~`uAmjhUTom}-q~-7aUP^XJb4Np)8e6;-beLY9vDao@hX zH>7Bsl(|7*r`%Q{ylPZK3l?U&4?M;dLd-Nx3+G^6d zt^gNe}<*K7WW@>%u7 zc^ktYe`LxUBDd?Ll!ASgw(0`+A~E%wLMgai9mc`Z@LrNvLAK-jc-@8M|tbKMzm0d|!w zxRoQ`_Uy!0eP8}C-hk3y8Im)KwfxI0^jzWF#du3buObT%agZAoExN^>%7kJ(2zR(4 zZYtAjRkeCT-`7Q2u9A(n^p@&Z%L$w|jGuR@GZ1DmqGAiRONp#vlz8i_8G2|(b+9M$ zUDt))c!XmNxVC>Fz{f|NyGe8L15j+9Xg~;hLiU`|i_AJJ8O6cT9QLkA0ZriqzhbI=W@8yA60&k14?^`IrI^zB4dmXvz#UWNU5QgUuB6lh& zD4WJhRWVHP+LS%fBLorS-Vy>Zba4FYOt{$h+l1}>y1paGRejH%%cIFjK|SnhkbPHI zQsmAJrwW^x6<_V|@2{20Cw7+JD?VB@DgurVI&K@rX4 zIaV5l^$^@{y%yeIv9AWXI5q5yBsByp1X!`*^ecEkwmL*?*A?X}f*0iG&6|?%Vv%ve z^Jg*T^(67eZ29UqII-bd2hf_9Q+Nz9E?dCAqPv=?oYrp_V8e`%JG87b@H&G6b{oT* z>j_U`#$HW@WfOI1WGdmxT%vl+KuYT*sI_B^4R`}~_F>V{WSUq1Qi*84KJd|%9X3v~ z)UMHxESOxKvrrjq^q@D}vbHHphT4o!?_D&B0Wm$RP0si8M_GJ5+_V#F9^^O>5n73Z z*oYkMzAimnHZLm}eESL}wvwAu%XJ}6&8FLdx^jir~ zudOy^eEW=HFL_046Pm9b#sZ1^|IVlaG{5kO^6IW;dQ7+j=+u6nr{Cfpah?LePM|O> zodHoDb;vUUoL?=z5{78juE|4)c%8l<(dEeL_zE))o~D#z4{y`#5TEVGwhaabt}Uzw zxn-$4_DE&l&yHdE7(ZyE6dshpN0pZ zTf;O6tk7^T^wIFG)}~J6gE|JSVgof9 z53oJlt))v~n{qz2dGltYqE&)jBshv89~!w!D!7g)>F@rERh85$b*b3eNt)IUG(Pki zYniFUtYD@QIeVYw3{GHLBVg2Qm_@H&v(`GNxMV$H2 z_tEz6F*pRGBQPCpqffyCY{)S2v+LO*xKQYci+24_I8*%btsNX>=IH~t!dQ8Vwj!t9 zYyM+#Vex2;mi#HKGn~naL0jetZF;C#$aLfZx$X9 z)yB=gMUM~N5M?I7!_uylo5(KxEB2Nz+q`vab!1o?*d|zZ?J6mdarnZSv;&_QiwMZ^ z)jhY+BUfEjdA38W2Ubi`flpkm@0DT3cuUU>$>-Rd4g7#l#xZUiPJv&BwByv3e}*ZM zG`*~{-k7tfOVpOBhHeG+JCyXRn)w}^{_GI>9h`t^d%{aipDiwu)*W5N8f#4@GI=5z zj3%TdMMN5AcDV~cBYweJH|g4lhdu3K2=f@|RQ0Jh=U0^EVuV_ zoqQcyYB9?Nh1GYJ$hH8ANuhH^BsE-{ju2W_h^@wbGn&hll$4(08b&C6J;BpZJ%|R5 zNqS~>W~;*d zQ^dVrCM91T*+|&mYqnJzZES)F8gTQb9HbSHB`7_OaD$WGJbOUjFPDnTgJO>^v1j-q zkceuT=_jaX;$%X(ePC}NKH&E5Cv5bMoIYT~>YUiu9Nz#sDkas@qOk!r5DKb;_zoIs zYG(>OtvX3&tUF8iu6L2r7nPn4d}yldWShKb((yQ$qkit1ew{8Z#eub&i~OlklJm{0>{dRSaJZ^M)vj=tU6d6=S!KH;(b*VI0$ns;_lxMtE$KaIrIrc z1fUc8=Hy@Ww?eG4o%S`4~}kIA$I~)&oqrwBB0QtmT2(# z$Kn$H^17mA1hC#|l$oKW`}gl}bHCdZtEjPnb?%CdEhRQ~1o%>yKw|XiclzvG5I^gu zeVr*>JUohn^M%&itcf+QR{xh2dJT<7WTYIo<%GK8@I3S8>pdeQgQh#6 zMDD|5&%oIU!vXrShMp$Z?V&}7hK80FK0}_)z4fbs44hI!q_!IUPajfk4x11DL3zKi zpHm{47+>i%=ixoLg2d9qlvGFy??9C85hUhm_ec`M){Iw}A>Ec!{HiDn>bnJ`y*JgS z+P{qxx9s#8tRlKhc7$xtHOd3wqISn`!D#jY`>#8O82d2z=VlydaP2 zg|uD)VRx%8A-fimHwAYvH9M=y1fE zy@59W%=r=uZ@Cv`VAyKHUOzZ?C}{*G#rIeS&DUv$=O7SIuda@!MWeLyfCm?-ZO5D$ z!GbK+$UZ_Q{J>l+jlf}8BfYG+!i%u9g(T^L^z4@3udM7gvw?|)MGBRCm$wRM6(#`q zct12r0|@u6yRIK{u#0V> zvWY6HeoW)McOYo9b0kp_9`5^yVe(tP5x1o@zSens<}~FPDF3<{-9RiDrt=a|>C&Z3 zZBhv^{&P6E0<#_%aS-_<12V zqABgC`g^PilVFKaA_~FuAy&I|3J%VMPMja>!lDF9S%jg&+5Kzd&}n@BJABTIlAA8P0ydLjZ4 za?hv+P3_TMZwRCTyWgmIjpF>2Mqj|?B9=v+OEo){apoUQcogZ}_V2@YR}m#z=dopo zzPRH1pbBa}KPac+&^ekceV$g{DhJ>!aV;!o5($B|uJ4hnI$l?gWaoJ+-5)pU1+gq} zy<%C^!T(6k^N^g&KdFBKS?WpdBU(g{`(4?}6@rtB>(;{R5a{0a$n!M{yRkv%$j`rq zK)euXFGdU75c(1^5*FsZl?yfaUtlh;qL{VF&kkoe#e+@e;GMPicn^*i*H|Dyfy=D3 zKwbXvyFvDOavxy6suwX7%Vnq7wrvO1W&TJ%Kfni_#;vRv#8o5DvU7rqJ?EyZdJaZl zGFQu_D0~9cvt1-yLlRU+Xj|AYXq4&U0)^G^`2lQ+-4Eap4D1oziddk1?E@yKoX9d) z_w6Iv&qcMt3Bn_evT1!8fx2@L67sD^n}YHkuN;lf^_mRU*urBV7K=T0ft zEmT5Dnd)H^F(3xsFV80~v$h*;W;7koZC28^;T0EuF*)Ly zsDE=S08!|(BShPd%1@M6J!AHW0j$1uz%ZX)0^V>blwv$LpYBfJ2aSeGv*4hnbuY!c z-a~aY@Lb>0m>tbZG($#9?m{X0$#k}JbKE|*oHOJ;d{|}EGc{C@!NrihGi_)k7_zWc ztlSKg(P+0MY*5+x4=ds~H5r(_ZuKY%yV^;*{Kc*C9UK`$1F?vB0DjFM6gxA87V+@! z7i=toq{fjkkFBV>KVkv=cQ4hK0sj0&uhKo${udBL@CayF%OFc}W4a z;A}lD{kT)`m@vk|88e$}^al4klXq_1?zXb1Wu<1PcuR{&?P-8TGHG4$SQ@=%EbC-K7Y=)B8x;i^;OFq^b z(4vtCYARrtvUqzWnizoL26ZiLF=Bh|)h=jT(#PlFFiGY-^*f$Du^9i)@oa$(V1hMc z(+?%QJU`onD?7i(TI*2X)rSZ+A=@xWYq)jj$U1ghtO&+h-rvG?1p#(Lsuwtt+B|eL zYS;RtYu9orc)T;6#UQ4z;uFX{W5g=bSvYOAvCG5JQm*7tX}Us-wz{Lzi5DINs>YG6 zR{H}&I-)e|p~S>Q%wllsyt}x$?qmtVJ~+IOOLv2$FV6-P5y39^nw_l+8fT=PP=yPnvnJ-S1Ysl zIZ%MzxC!z1CSBV~h(`Z;lY%@3+4X>|Y&zEZ4POJfdgB6k_q=EV$NeadyG=*Y=Z~NG=lrSfU5(Y zz&*P*!d`mjK?64}Iyrf(h4KR=x;D;0G-SO#Zj-G;R*8I5@n#G~!*mpYqi*t%O=IPh zD{#ARy~QM$`bB28784?L;Esl2>jdnJsx?49q>_Ci9fn*yPM1E)QK2SRlw+gGYNs1_}P)KPIb=6jyc7 zQ)nXYRv)3!T8rIsa?yZJ!zbrWuD#r--9T8`vhp`MoLP{G+rA_;y#a4+H3Kb<(T1j1L zUo1m=p^cU+X%i}1RFcMMp|q!z6eU-CuC%ZJ;~jJVX1+7O`}KS9fB2o({a|KpQ`hzR zyx-??oX2sTs|JaX;KOzbhW>&_IloBrIt+){0nPI`b?WYOU!)0Sgp1OLE6>kdJLT}2 z=7tU~zy_4Tbbirl-tw~q+)p8!S`h~(PsGUD?J-ce2ZCTjNfdyw!Owk6RZOsd<4817 z3uNQflDA%@zp3H1A3Yj=Z2bK!$pN!(?W$9AlP2sgpoSUf{Zh?t(q}C~nUb1*qcEXaT7r3Uu)UPd=f*M$Bd<~#2YhPCh zp25%_=)C0+us}*r{04FYI;78?or$4y=(dMH#4Curz$0F&m4^OXw{?UxsiuN52uP>E zHodMx0br)`FwM9yO&ER(6Szhe$&bK|IR4?<+A1hbsx7sjf}HH{|p<9PHlrK#aTLnW|4&r*;jBw7}Q1!&|COjQqfIOUeXky=4IhPqj-N9`ohkf$fCD}D~0>GH!-I&wd7 zr`MwyYG90T&sS5IlK+V?f!4PS*&}Cesv}2&?=j1x7WAV~pOJ2Ng;V4rV6n$W7jA333>$%Su%|s6-2Kdsp zZryqWBEK_gm~W*aq~iVdf)N6=39O`;N0~y0G5AEz6+VwKP#VU{pfMJB&CSA+i|Jf- z38M!7x&K!DjRn38{d*QAb)EuNi>WhzO#)yr|IzkeIU=;n z2#W%jn&?j!XBSz%PczynCWuNa6J6>Vs|Q zXUe>h{p(|N2WIpDfDH?a_!=Qc7EnH&mZ=r~78CB%*rDS^36=F4B#KHGfKL~t_4Y_M z2~c@ZY3b+R50OHjxGrQ4)_Ht$!~@pIZGvJAJrIgT$2$@p2n{bBx^HEK2AFrqRm~pP zsW`qz&D+vUv4b9+l_lxFqG;pk?;fu+A8}PN4YU=u!OdgoBp)~TDe&*kM=~J#@vFYZ zz`X*^0>6KHx#%$Q6b_&Bfa#C46UbVYgA$1t zXA(QMib&KuWMvgnlgntM?Gw@&qy>Q_QG;_8AgS6{fb=`6e?v+_2S05%o?Z5xh z)!-aN^wDm*>-P|b?aO!RE;v;i^rXr_y8etC-k>F70MtR#`m#Xs?TT+#3R*a-m_XPv z{W4^w0~vJPLY+IpR37N&upDl;e7xQr^&)lC2vfl=+)?57bE^6$f_XRi?+7N?aIZmIfH=p0 zNFvJ*`5fzrM$t$n{Xr0`=|+Soww7`PC{!mFTWvF-$?OQk({;r6$W&7({eq~Bd584S z8aGnZE>_ckrr>O4G%LvHU!D-88_8a7^X8Y?YMXhO7a+O{qZ+MPul)j2T$hI+(L1}j znO`M`H@tu>qKAyg;=A1>fKfObm!;|MeI5=Bp29nMP{`6(SN;4e-0_v?yP*B}2EIL% zOuHTYZ*JWK_Ak1Il?@75!Qvk;$X7(saAOxY^~vVM5FO<{IS7{9pP&CIKaq~I(y{+= zi{`D&B@_)@tmeDB7tKHhvUZEe>93l7&oQ(7M`ly!ysY>kzIAH?&}pomw|f+?QCnL}Oqmekvu)c>4uff5T=aQ#Y>Z{D z)PWlWf@e?$)ip0QnBiAIphsy2PV__g!dL`5-$5R@50Au+9|jBOUSvOh_oE&;?YbG< zeaOqdV!RG?G_y;8t+y(BHBmcR)a+qDQ?uwE*FIYx%45voP)p>4onaz12BSiS^*&|& z&5qy+d_6~wqF%L-Ny`h56AnSQ@4(g&G&Z5h3fRB|ctdat4&^5pi|S`vh0z@@H6c`i za?5YEL-P`eX^V8ColwNeeOc>{&y1PI^KXBw0L;jotdHbfyiW|Z9}`o+8#Vrh#`rzgmiv|y zfd>++l0#QD+!tORb@L4joB^8w#eEwHA!1tloP4bhV2EAp%k%X+nyuo}Qrfqij5Dn# z(YJuhRQygedSBARDA^4-W$Uh5;h=s!l8wpF8LW?Q0%H38oo=7X@SGs5qGRW#)eMj2 zMlld|?4QW=CA+`l@~6(e{z6#Dt(5VXo(u*b36`0tjoWti!mF&TEZoglXai-};&XM<&$m6q?*6GVEY&KK~3{kX37!R`{)3 zYZ2tA%mH6Sag+g6SZ^l|gFyeDiRF{EP3~_tLN@x65q>&f7?JjRhn_M)06nmDB4HWB z?-y_UtVDN}eEJ&(%pkFMi8Gcnb#iV*0K1tAmS-O>r>!$^_3O+xUJ2_z{#}8OaHh{r z0LS^ma290nq(zxei2Un?DN0M&R($+=42)Y;Q#WM{aSh5psKWXom+MQ&76yjK-G9DC z)~kO{2X9xheu52fh}-Wthfr!Ywr$>M15*w;`vBYL=%gOLYkxn=c`IupaCUhbbb{H1`Gf5TQ8&R!3A0{%gvB@pW| zgkXK37l7PR0BfGw{n`aoQYy=rZn z2%a!Rt4cTA4JL;e&<%{&`#$6hVif@d(TKyHxW2@fAu(YiAKu!4#YTvD6yARh*INX59#wJn`q3SH< zia-lkv}h4WZUYfl04_Rj1w|bBJ%o??Uk`;PTr?dOrTRNNN7B7!$~CQ78D7_dOLk7H zjqvFvtaWs1F-bDt{tdS2wMKGf2rCC7M9%3+(}=_ajK!%)knzX?1atqtAASFTfWDe| zZ_JA;PHAE2hxtbN_<~c10jB_x(2yW@R$+yn@SaXmhIU3?Nr^g8W(hMRVm=Sa*!z`V zr)LJepg51p{{R-*|HCi6s)khJhVO{V090LpV~=jf#u}pwMy7cWhG64KH{mT?{z!ee zk>NEb+4pNOCn_grFk6QbYgs~x;W>s8q6QhJpNwG)(fjTpIgfU% z&tQU5lC>2#Q{fou%Zj-9U3v_ke$G!BWd1$~`iuAPe<}ylMgKD<&UdoJ^9!%S>^87AIa``O0a#v^Hf(41ob$(g*3;vhDU%Ta~knt}0%fGGJbF0mz#bbI@ zZ0zko-`l>kXPcf=y*>(>msLi8zZ3K3`4~3?)`&5LEpQwWe8$Re%rL*C0dBB=`J`(Y zu-ofW8AJZ$`j0C^MH!C##~JoN`E3~kc8cvXyJR%ucYaA33$|U6?>o=1{ zRP35qIKTGnhh6o5o;}$PfY12;U3%BqyY36_iqHQPLhVAJng9D;4A^pp&)C@W z-1R7=1{KkAnUnejX!aJg^5p|cm>@zyY8bR zvpcy9QlC$@`D3?vf!Z)LNYPLTm955p7wrktY;;Bus~R`iK7YB3vR3WYzK9j8RslrK zkyQi20k8ktU6*0D^erUli*4hIA2IpvzlCJyVCSICN&z%j^nB=i;^*u8YoCY{Qal_$ zO1)9JikN(3g3H!wrmC7k{EnI2vt;L`e>kYkY^Fa z=ClYk9vVeyxY%J5)7*L!ZW2H857+3wWie5vDzPGOI(v7bYa%#MOD;r^;baHV&`o0^ z9q>|Z8J%5upMu)ltg|gJ^QYS5BVF^nA;2JA@B1Oe7=QNy9}!vGy8bPfoZLb+@kwu&Bhw{WLkwP**Uy5V;7Thsgd%0px0?TacO~K%1tP^J z<@$Ia!(e&PS}1mo)8?f0Ry*Lid|zjy7P$1tFvY}I2#$HU?wYEPiH^;Na0u}X#f3I zWI&$AGCN(cLLubye0~-h__t>|;EuyzraOU|m~-AAI2d}aDT9zzR9L9J)1yZNc(5qk z0YBrD_*&Wu^xy1P!yfp&10zT!b|Rb`Z3UF~bTsn$(8f1|#XqJt4p*~bAsDzifBJoa zU7#*ahXY%+7XA22m}r?fBs~XyBl}?vjvp>xR9nDcN|=5PWVq7e&o5zDG%Vx{6a!NP zUr)iw2hT!rk_iRGP;`3Kjm3G}tND^dzvbUbl9tWX5Ki~CRj}VMPT)c7VBTH$9P8z0 zN1F-)Xg=B;?CgE|Pr>~RIOJcNu8rjbdp*}zF-7>MueS+ijSQm6egxY)yYZxM)h?#> z>%(AwU+z7Loe`l+Consc<~+?zkH%ZBd0TVRa2;BwQHwwr4YEQ2q_;@8!rwW_J_{r4 z<1y7tb2occ*y!v(D#BsGcDaAlPm)Yk>Z_G}X~fiOvU_L+i%2L2p%Yv@JUoEzy7Xc{ zmOE>KFA-fXay-k2$%;Nc@@u=C=WE=7*^(*epjTK;(y75Bk3nlNkIfc<^wDy4dHZo{ zxl)5Q6!@+dQKkdO=`yOy)`ss=c#X}_cG%*t)a6szVM?h!vyozv3KzO z2zs11uTjOz?Tg+;OnQT>)wWULkGuhY4u;q}uvy|b+4Up$2Qc0Fw_5#gFI934o>OZn z1>QuPIZ6L z9Ubv;Cak`^>8B1bn==m2h+G^5Atp?qx2Hb9k;4>0?ngoUuQ7E#0)u zScwK5vct44y1}7XC|v-W%+NBUf@p>Xu2NgK7UdDjF)Otp*fBBAL({NM~UrkC1bjY-e)pJZ$FhX9zRYJioGbVQSM}|i=G*+sC=O)e zVExdFtb*J$c|mY~_*kb+#uot5Ht-+f#?%lH3p~3l8q+>vaDsV&iN(2DOwM6D1`AtE zB99@?!%0L5Eh=D5#NuIzDg~a=QEXcRJhgY}lj}ecVUv?f%)btXuUx&l3NvTGx>V0e zOS{Q%QDc$Om(|O0`N1tcCVw5ks?%&Q7#4|M{nc?-Ww_=rQA1`T=+r~(@`D)$1PEqt zZmA5nBKm<`I)M|U>^AynHv3Kxjo*B+D^~hFS4f7e59swdtTcTeTU}j^2=8Y21(a^k z*$)#RHkhO;dwJTefr$ZT#eAw+{B{sMXlQDV6OUJ?c1T&}T z@Ws9`enY_JYxk1Ow3t z2)wZM8B~zhJ0nl!f%N0|yi+h@;|de~4SCXs&a2uqPVXpINyUS?cgd$!W30OxYt zv7*EM0EaP3&2kdQQ-FVZ(yUBx=s;`1PFz34C2QL~eOF~ICXJhi#i>N$(%BAV3#f>W zG%>-=cnJ!_`GJl(Y?;6jCyMCV@q6WpQ+=6a?@D>t{`5~)Qy7pMrhNhEd`y~>Bb?&K z&q7v?e2*pT%fJ{v7}cQv4m6;IZ&wZimz{>C1_D)Izh!H%ad)RqyluIgsht9!wDeOz zEJ&0M6b+b$egic(MZH*yCWm@t>6P3Z_=1HrYZNg6&fG$ z>^EB5Xkl05l=l^Puj|o~=9)0VJnnk;a(9{Eava|$eb#ZvMgaJ5tMexPl8)ivg4uHC z!vWWo6fb{TLfy@JxoY6!sWzhk^GheR5e0DL^dJ-~5y9rj4vgx5d>Y&xK|S8G(2=$e zBgUddhmSs8SK2b6jTj3*u7DDeYu?_$@V%YPI9K7p{3BZ^`28D1lJpSJsRCGW9fBv~ z;^II8(~?+Ur4A!FR0WDJz9rSe{OZI>iSI`hum+~p1;T_8uU0Vj@v$~F7s13VL1uL} zLVy`YGp?SBtx?XEn58D>NG^r3bT*L_W!c_iW>fqFcecTkV#;E=8@00R(Hq|^W}vG zXU1UUv_RB9N^Hq{*)<1E-1@0V2UNB6MEh5jcwm|P$H0x%0`DZv42u*1iVGilFU4z#a z#AV&VfAV!Ycb+194Y2_Osg^9P20^;kEDDskls4hv@6K9qNiiJI0EHJH>myOLfLxe* z{GHMA4urnZTwnb7Ji}v;bRQW!GPY5%IcQU3CSi9ryKzucLCrPfP_#h=GcSBSce{>^ zfd(xvBL<<9Q=zGd{0S5(#Z06UDi4mu1xG8nz45Du0u5oeOxo3xOQ{0a6ER^x>yA8? znTrdq0|pVo$kbRI@(RZnpM^sfdcqa8wl@mmcSbt90)kL>`AhIunh@{K zcNd#h$-01Ewl}W>M z!WuQZi6!IcYNbX?z3I^q${8FF8)rLJZuZ(i!5mzivN;`K0w5P_-VyIatG&)x4TAa%qUv+p_k+VZn5=1K^bxp@s zQIvgY31c@UIp$g9_7470i|HOG4G&-@F*Dhl?r*&h<E^S-4h*jMe}M6w8MRV>bv^wtO0zDL`#!Ve<#r^q_S>_2qi<`Z~3v&Ac%Z zOMR>Ed(txTy3lZkAK%8u=Y>*^OxoDsXRc$F)F)BP%JflZBE6?=D+?ZGs^LDi-2?mS z(dcU$5m6_Mm#^dJmY)OdfmjHSOt4`jI++D82~P~1bU1%*W!5P?#)hqdGY!`ABa(xq zwhToSG_;99szl^v_MjcSBpl8|g_c6{0VUfKoP+c;Lv`W570zFb>vbH*1hGQsujg(h zo8;>|xvZdxL#vfAdhZVnY?8+m`1&70=#PAQLnqfNpp8EvviB>3L6=t=3RMEmWL#cf$zT5ZS{c&=ZxLe%SZ`K(mKWl<`qWf9k<+ zsC6f_we17?iBMzWtt`4c!jx=c2=8Jf zVnjA@*LZ}LFbI#hv&tLb6REdqw*X4Bm+J`Fi(r{B+FpO(_`Bl)Sb`M}+(!QiH>tc6 zMYyy0g02tB?9P%Jr)K14feWa?yMA%3tKJi(sSkW@h2#zzH7m`HQ zHT#3ejgI_8Fnl9nG-$XSA1`(Dc?;M(O7?n{M%v*v_3KF-r!;Cn0P_@ z!Pm+D>eZ{_ar?&BDWcKyq>EqY&{N#HM5Fm$tM$U(QhriQ?+@IZjBWeU+u%D$aJv-$F-Bukaus;v9> z@1vZt>I+`CgbBMn(`xWrD=~V7XgK}F2__b%6EAYm*vhURg0uGnx(isjpuaA#^a5%1 zRFenunvL`GrW-Z?>`{ODcCk9mIcWIA0Kp)$Sm65S4-bWPRRXZT9~McY-Yo^6BGJ-= zh|vZ#9LDy}Xr00)n$lD-1vHKmm(l??d?vJQDtV_6 zj-}WReiy*18%wn%>N=F0xeV0(XtHE4?NsWJSWUzxZ*P(4ue5W4iyX%O( zQ(&TV5GC>|I)@T2$146AI{OM$Z?JhQ-|(=j2C)kWkDwIIwYHI`hD1lZV`Ug%#1z`q z3%8*`F)Ds(s8AXQFHp{hU0}IlWoe?}18`yo*xaz@fQY+82D=Q%VjP{gx@Ef(dvkR6 z(%I=oha+dzhei737H|~FhAsC|wP?I@`n;P$X(W%O2(@G7P9oKA-pP~3QLAStrXbaf zC*-qsYl+WzL#h^^rACa&%Q43~I=iB7vYh)=e@?9qZpTRfmEEz-(b8(;*-FkvqgpfgdM&eu%1)?Et-klJT zJfu&3IDqMPNRluP{MGXCTJ0;?%-KOO+&s6k#;c2;aY zcs;tb_zepHy-1^F`Y%c}B+FME8{b?B!dXtUdn4)=k@)?8z#6$2LS}3**SVv z9qr<0T3ZNWBcMgzm>^)Bx2LSO`!Jnr+>xV5E}6!65$bms!*n;@!MhfWAmSQTlZy&! z>jZ<}389s0erE$!tfPnR*fuIIB4P&{FuF!x@dmld+DEr-(?SuEZ+Hb zP2DVcBX5&GRl{yFh8_(|_ygZXyV_m>uI!c?1TQqtrdxS_<=FleX#I}PIVR?<6*sSR zhiGNwh3{ONjWk%AGg0rM?8x#4-iJl?h6?6rg31yBg=}@ykDmeOEpdiuwM$d?D|-~e zO;K@58ac~uIdDq*##)Q{Rjy^Di1Y7Uk<@oLUGUT8jeB)>DZ{lQDq62ytzK=VfhZZ4FN%tgY9zn&UCe~ z3MhRC>n7hiC5Yyn4x(E7!z+QsFC7$;@%7}LzJk?qNQAhyISCiQSOWf)Wm40qB|K*p zn;aC>*x4z`dm&JX49&bC7v?MVs%4ZUKyiZG>d&Ei8SZ`u-3${TVXjS9!Y0tsEdbFDJ>`Pl(pJs8#<>z5NySWVfjm_gY;wgF zCD>aV;R>{1rN^$3HOIVE%UzF`pu6i}o-5QCjEY0V+L$DT&Zm;VN<4TTS~s_-3>&k6 zS$tYg0rs6r*mfk6V`gBtgQKcOSvxj#{eed3pn9U4_=1yPQA~jm-u9Rv!dj|!gia&Q z@HyZrM@EWUhg<{u8%fc2Ii%*=JU(34OhGEvK@I^{ZyqmDtjNc&_kB})!~jEtAK2^N zkN?1dxi6((eOg38lI1P%dFW*@vgs=BGP(%er!MWjd@BpSZ!0P(Ij{+&lzY#}qfJQg zjr(;mJ0Qq3!>h8II#*!~Wz=*RvU^n-F=#kD*?^ZZ0>cN4q1Z&wwxGIFc2> zS|vu-7s4PDH5-5yxAAXrhD)!J zvZ5R(o##%}x>sB;VtQcN3;Ai@5%^6TR=!O541c(zX`5wb^^adQd)3l9U%gfD(#3zQ z_O>XoG~8JW*OnNy4~~tJUq%)|7xL9khy97K@5=wjU~w0N@zQg?w0gLdPtaikX=!2C zz1RjVG;)=|hb%bRcWEUhB$$?ks^{H8xJ!weP*>pFym`0l2MV&PstjR&Zf^v;dr``6 zV3}czC*e^L9|d|@j6EX&7?8($&Tc~q`&y9Y4OKBe{|!qhw5XyS{n!j+Avy%lrWE5p z)^YTqzoNhC+QC+3iHTxfti0YlYrJ6!>7d z=}d3Op`nVK`N?|Be#42z{Q){LJP4Bf4+wKd{r~MiV--f~^f)+g1#!ig0DcvtLPgGH z7KLWZP)2LHFZSskFW8LU5TJy@m|``vB(!f_3nCM;rWMr{ffk~5(;EqqMdATE$aCZFNj#qu&kx=xSc@b z?Zo1Y)8}Dsc!kH(;%-WZ9A_biK;afwW>30ASLy#E6J(9InhuZ+L(H7 zAt8;XQwUJ4u^tg2l^X+TfV_*n`6gjE5Ip)7(OZyXc{Y?;BmE!7Kq{q7wC5~|r?U&3 zT*t+gKK}%P`QQnn81LK?$R;)Pf&tUO<}`x%3y6rEM2PG89 z{`2s^JwewOPh`i&VMQy^^^suXWN zd9T@N02Iw9h}SUIMkG?Mp^Imiv;P7hX_%c0VHITs{|C1g3ZDRM`BJsA!M@PVJAzU| zWB4+0nR#A)-b|Z^$|L_C-8rTzWFNqp$6dj+Tz9b#B_AaYXyeG#d_E;l?HG9H6fwPn zYGP)$gQcoQmcocNqu|%LyZf9Z*U*qkHuNb61)qc1A16i3 zH%;c9ZD?HX@JQ-mM6@&qZMIAWP>Si9I_ze6Co2UdZp7>evh=p6I*{UZ9w3_v&YSR4 zLvGx-q3Gt=MzvO?Xh7RVI4UR@8w;CJNyQ=rxMiz$$RXQbkY2TXd9BwLAPur#UBlNZ z8s;5=id%E|GNuUpdOEOg=qx<8x7{p+LV$XNjhCN#@O|4F?ITk+;O z;ze;mYIXDBATB$bf-A&+KcJ-gyVZTUImhlf<-KG4w`T3>7;;U$2_i5$2J6thd77G< z?%%)v$dMyJ6%wUuZ55R0MaceQ-K^FB6>ZzR8ca z-(X1`?oHLE8myGIWc)D)6QAs!#qf&^UN`RGmY0_YG2r8}wQ9e2e)zNs=k#Jv{o$vD z7kk4i3#3@8?#N$!U@st6tudU1Wmi^(q2~>>%WspVxPV+ZicYi8+Ts3*-KG9rZxJ|p z%+S6i^bV+7H_Wx<KZ`CN1o7 zIrPesa7IcnW)>O_qg;vDmZA9bvXf(L{URqB!i7H1c`|MS{&t>4``TIL~ z*8q;HCy(ZzD^915&%2*7fPT-=3OufD(ngT;bsi|neQI_)SyzEiL_~{3Uy`sFpZt+$ zustn*9z$TjjO7QTSe%N^Oz!$g(A2O%riG~? zk&%;cC8~1WO~6dU!-k-NP>8D!dSA4_m7~aD!SF+KCOfV#ok8mbboQZGt%x+Uu(QkQ z6q~mzF^eLk_86^1q$%j!61o|P4$+|wYkv==#i3c$xe6pnU=!fXmac7NomnRv=;o)q4F1O+{wpKn(X>KdfCxLT@!IG3!Ku?xztVqy-P5!fGUo&?ZVTu4hd#`&Ov6yn}45Q zRDUe(G=%U37Qlr;4P2I(VfyCGu)Ijjx{;&rGfoePQF&4m04%yb<=nLCA?;uiaPsP1 zE1o+Cky;d|Gx+uY9K}uIdiE~91|hD!Mr{=)Y4;SG(FKS)y4{Mc@4B(KSXd@mxSrj z7*Yu+dlGvdz|)i_7`~`Gokl+l247Bw=KlS!(T}X`|AGO~&h2CYrkTVXcxKopm3IU; zk@4IZa&c&@ETPpUN~5s4m9gWDz+Rae6c*Lr7tI27>G6PZ!i#PmQOz=LA>$W@R+;E> zWY%GiV5-*)G_FU|p5rl#M6m|a_KO;U;-fdmDg#2Bt|kxJ2e7x)1^Yu%!SmJigq+&Qb%2S1Xns`WGT7*RF=hFTp^S2C^t`mW&bdnj%9N=fB8 zstuT`R5sNBfxRErn;gr_EiW9@mw2mKLc#pzuFV=Ywg=`SJ1j@}Pvgcqa=e+3XP?~M zFc7rGSP%yRco#oXPqr7#@_x7o4qNi?=4av`Vfc&*40UW;?nrV$=Eaaho60EN_vv ztr~S4Rd;|DXYH|**Wj86R+`*@`~+TCg7ldH{!PS|e{QnarCuUFq{cZ&^22M)c`gnF zcf)0_DPs*Mrzh(9nbJ7SsW^?BvL7Xz{q9^ze2xrVFfADtor2ih5}TgE7jYXyxxa0M zQL#`e2bLO^DUh$emy)j)cT_0NXkT-R9*xl|`VSzJlk@!t`(}{$^6qw(Ov-FmwZ;HS z(eqW-SyB*zEtJ2(rKx0tOs!6v=TxeZnz*n@DMe_!6}`|>=Y8{a^flm= zV?*LpvIH1*Av?oUVPID}TBcS37)-2GnddK;1XIGp0~#3ZW`96{_DJR4K>N*#oBE&d z^&XZS#3w;vA+*HRnW4i$cSZ%hh-x>(0NcwDjpHd}^`a(&*ne$y5EsMeL?!HaIoBPJXnx-V)5pWeWD zMzD79#zQ`%TdL;?2b`JtVw&;fonbH&^K8yaQhBtVyk|6@0$$eD!UgzUV9i#a;3P^D zpyK`ISYYQZJ({%=wZb-0L6}p|nxRh+j#2vYZgA*?vTK+#$VSh^L=?t09bK?%Nq zi#sw9!2dX!n+tu@pQ+^i`YMA$&w}CEsM3fWQeejIS&UIK6vW$2R_sBDNX)u+5DRDY zfHx-Soz~UWHIfde`o92Y_arAPzRhV&d1^C=Q%PwevO2Pguy8J_?TEl)uZZJF4Pyv$ zoH_TXS$q0Y^Gg7Zhz!~oqYsPWYH(2C(;VrT!;G0tj4>Hl3=M~3c?AMfc?tFG`RS90XvuS(1H~qk zS}AJ$Y~Cz#e7C^`*hxSjr#|ScH#xZ{^i3-oPY#IX6zE5IiQSXfzIu&y9@1(*)U{JQ*}*mRJ0 zc$m)vTXVb5pIZgIF=PXi@m!zcEmk>9GR7Q1qH*^6H;Tw4Q`M7O;Hu#6%E$U&zjiHe z%3!On{jFh$$nBRe-_d2 zN&+yUmB0D{wk6ug1A*AwdI1CJK#nP}+@J^DESwD=9H;(7+C8A1qeHJTJITYS036(e zVpu7ms^075Z-`@oUS!g^5v0hYh0O0^rxDnq3)3JJ-H9qio_+-vb9|`Il?ZAhvD;$m zGi_1o?`q-}6FcmBLP|*JZHIpsdY}-`bS2VuIIPxDpurmKTkZ!P<;IC6*)k9*d=4Oa zVdnNE7XOhEXgHgqYZd^ymhrG~ahW^Ut~-F?xR$Tq9a`p(#j9gFUD31O+xM`15MX5Y zc>xM~21o6cRw)J`7-AY~m_}B8!9PuB3LsMRPI+)|_U&CQ| zjmc2GCr16FoNrcgRz3OTmX9d#ywG;x!j5?+zxeV0Ncv`cX!>uWoVBogGkS$%Z-XXH z2Pal>jugYjS=Qh#<$jU2aY#)0dYE9pTL$@~(#lx5Y#`Hq(3sC;t zKcTvBpAb+yIk~1LAuOgsS%q3t=U`39N3&j`O&qK+dDpc2MswBzt=A}MnN!h@8j14j zqs3iJAa@J~>TGQg>wX7c)1qtIR_=hXv3%ivekshl`Z>OG{#=IHy3}rSi0Z@f^JH|G zqAS5E>av0T|Ls(ODua_+#`nAY+#x~Y1UAu2nPe&A@%!1yLbR~-Kb`CG-%D_r;Q*QM zp8Sk>1v#bzkAS-xn$_f?*vZj;L5>1+SDGd;wu**#^@Y4Jcs9Z^Prs#15gWg8Tq$xB zd$1QA`ksU*vtYCw@W)8O3yndn$7mDGlu#C#58nnCh^dIre!>O)wQ!I*uvXwN{O5D) zMXlt|US#4c?%0M!Q=jaaF*rbxvbJ{+As)y_z4EUE3ck&~2!a#Pz9e46#<(|p9y*Gd z*JYz>e_g(MwUg6tn~?8eAW?HsMTY<6b7wsGcNJO5HvEvTXV`(n*6Q35sI5HXfDW0I zX5}(F2HOf_0k_e%ph>L$t`3@bOM#o7;zJ+G(+FUmqj-ck0JWuulQw73o@68?-`P~S z0k@H_fM2jPFFGXzEheGIxPJpf)<^sUy4#w>#GH)81z`3GZ6Mlpcxfd#-NtAb2M1tu zp2DGadtDeB)Yzf{N@3SM0DelAhXb~d+?5?ior&ODI~&1&c#pfxnx&^&b5u`Q{N z$iPb7H)7p~e4jO{pCjntEUSAA#wQ-A$pSepjD;tSMm;QxeMBg(RP7!!1-|}5e7!53x_fO^^66cQmIKZ0`JpANw9^^4x+kQU-f#7q$>-I0Qp>fGwmSM^C5mQ9E)& zA6A8`o%uxC=-CTf3A1m^5q#+J>3?BKT{XfAmmP>`#LEUf=jqd@4<9}ZHu7LoCbI2L zkK8cS7#>h+p=)zTZHJ}sYu2v41xhd`Xj#^c=P}9vq9B$fjTZq$%5+yeS$1&*ARQ?F z^xYsEe1PU3t~abR+*aUTun3Y(#riI|2$`mD*0Qo1ppLb>vM(6iu~Pv3R%@4m<3}VE zbb&u!y#nv~OU512RwPGfc^x|fiq6w6VMsSzwyK6~lmdik1Ku<7(#b1Fm4V6}aD@`O zS2VtA?L1rg_@Eklfzxo$L;+ysy>08T4<3*zcr#xy3uqA@L2>R09yVCJ*sj79dpuga z#uiQm<5))lp645r@%}#DrV}QZ?&Kcx8rml|l?qzpSUF@hp!!QL^n>E0t`+m5->pU_r zw+&Lt^P2Oldl(oPxW4g)d925=BM)%fki(4F73p5Yat_Nl%npin5ED6PFvue%ErGUA zKsV-BvswOeuGKJBq#jukWjWIc z?gV}4K?Gop22rcXjWuUtRTeNA?uQ)SVXfW0QYruZ1Sq&l04oF)Kr4GCmsUIH0+Ql5{KeRs z%W%pWJE=tENNA7FRX(5G{2c))F{Xa6!yoV8>-TfK zhYSKMI>9l3Uv!*%e}kA#mJ1Bg;8uYnXWGS$lklo*@6>QFvMXUpVzn(!!}oArCYlS~v9vlF z&w6_!+!Pg`Ea^86^bZPZpguj>;WC2oB~1^10K)=anm8kK1>RlkSa?zGjp!5m;8^Mdk6M*Q=Dac-RTJRyW|v># z@#KFHK5EP3KaiZs{cz685e9!|i^P7P`684sGqyMZUa8LRJZS)raPrn1{iAEL_~Jfx zdCO|`qHAaIyerEz6qf$9rP%NdgwzXQ1Ka5h44mEF@*sd+uGWQbOGUIeX3{Yy4B+nt zo511ca(OT{p4sVzq6^gu(3w&&FjtT*Jl>jHae!ff{&e{2uks%Y+?>!qmX%%~ILWy| zCX=8NmG7)dDPR?Ir_Mps{q3}E=*bQ1i|BSPT#B%$!wgd~cp2RDGAwGg#3;LvHGfX4 z$0S<~k1B0taT?&vX%B(&5^P^&?`}G`&*=kncc;;h&Vdcja zm&kqhvObS*irRQoc8Q92W7n64)f6<*S##T%fq{2EIpvpLhpE`R5XGFn{c8cN4ffA& zsG?8&8!OYFSfjhnhO@A-_1u2&9_W^s7#(Wa(eYDiWC%4dwBl(gg#=^N*S*tbnGlbH zRNdRs0wQI7+}d*hEEKT&ikjIcF0cbE+!1*XkWAqUW&hQI**p#)L8VmvLp^PpPD3fB z$d{7n&9N9#RQxr$uCk4h%g5{Xgj_M-=?bg=Fbd`*WOnnq`;x_(swOaQB%4MGoqShKgJP0*A}SDpL3YI|RGt+#dAw)$BGvMCck-V@1^R7# zYWlVy!{s+lYt>nGEnR4we0GA6OxSIk$ODab+BM8U^Xf66}IXY8Qs=T&SfUaD^luV zZU^Crzjyht^8+AT$k`fjWbIDD7HIn_n|=HC0U<%VI0bz+SzXmp)#&U1<5PrqY%Wef zkZND{o42=rs?RxJ;nA)d6V%~wby{Bkgq@3vtaU`qo{FMVTWP`+Awng1Eyu2w!Uz-& zAUAWlf4fYjmE3y&{yqK(#taoTDoCQ>kz3ZNNO>MkKnmE#**aN`dJNTtfMW)j4`-Hv zk?V7FJ>0>dRMWb><7LZhl*JR@$2DzH$dRUAHUT8<4YBf2ZeiBHCPuTq6^@%@a6}M^ z!1^Z(TnY~FgZ0ccWS@hrMcJ=~%>{TaT9J||Xb11@_*xcrFaZ0hhg8(m{H+7KYC%8q z+^VX!Bx1M7UVLtHfK?(OK*;ZEy~pCqNtT=js)RmavNL#d`y&sqt(D)w=aB(A8nDx@ zrD)?ip2O?~Xd^HlpF#z-^3rLMG&3Yl&yGNuqLG8{o0;Nw)oaktCBWbgJtlnW-k|v6 z^5wbn(g1V-N7e}RsgdUu@${+)XJ7MU?)psK*YNl$E^253Y7cdH(^U3JiSKL6LV_L0GNT&;54jXzdq3` z<=eM!nD6Gc@ItW_W4AdG6;p4tJ~7e;LM8TW7IIa*?Zmpf-__qNzwKO!>Rv(X!Io=V zu=_N*Kp8!m#JW3n8|Ca1kmXUC4cL|OMOqlWL!bNePpm!@c!P zRd({!VR&3kgP;2!>}ZV$k}H33E0|3cVha#Y)M$5?EnjXw*0P%6#_nh(kK+>_7^Qo# zGcz!-|9HB8dLpqt5Pv@tn?fm&9lvICct~A7XYUN9(B= z1{d*~fH?t)uh^%-vM0(j^p>&s$Y&e*-vN zT2it-3dLrc{*yfuQ**Tk%bV%!Bjq``46JzHgwh-i6AlpDE=?YtK*@JNn1EMNs4IjEpaOI`t?U2>5Fv^vts@71P**?Kg`vMMQcR*3H+U*$zH@VN4fq= z()TM5W1aDNRKz`cLGkhNKpO_n&*vFOEdYOtf>Js*A<GT|;ZahPFz-6tkp%uTj!nz;V8T^5p$FQ}?$o$RGa1K-;tIDnjo#U;rQQ(WI zyz&^u--sIe$L!o5bzNiM6{Ld+W!mfwyaSd3xhD$8xy!!X#V?CNA)G4o{LFKL2_U+& z75|^)zDO0j@xITMcQOk{phVlQ>966mkbQ%QVHZ0auJo5n47;v}MMgfDK5@H}t7Q=b z_tj57csxJt(r(0Uzlw?iQCGc?M#us4dZv%46Cws5S%H6CXs`ljd#7FdZzU(8r&bmh z*ExQmqxfR?j49VwQT9+eyXBdMfvblQVF{2nb7mCe4x^_;P5y3k5^|^&qJT*$;jIc2 zQ(OS*3A->RiJr?xSZaUVE61ku$qYpVCrdY$vD)|Wj+m~I7x>PBY@gs;pb369#W9S` z?MzaT?H-uB$zdZ&c}YX^=IFC;Z+|{t0ky1-+Hv-x1zz7iT`o^Vr}i#nZ(j5S=x_T) z-&t#v*mYHJ9&zAfvmJze&3&~oVz}M8FtjK+M9YuoW@y^gN{2Vg;Ur7ZS5Z-kQE<^Q znbFbGwZNvX@^BonW7@QO*s2E#k?lqAib0GcM$R#B%IAH9~lej5PX&yD8R+W7hommbRgFu7(BC% z4GopV%9{U7$=@zZklQ75t~r77l5A^W0o8E%$6qgCes_6jf_Q7b6&UDe zoDc7qEM&OE{5Q)y?!8BsH#j$2)$t+*i7c{# ztZN@QIhDgp)x}BxBwq_rnBd%}GQKp@#xh!}q=^spi@)rlvTiN*bQxs54`wC;0-uHf zmFGtls8ql%X=0N>>vYn6$^2?eAr(c|Vr$x~Ii0E|)5vu)7rI*VGD{a1g0f-MW(#aDNUQ0w4t73@_+))C3i3^$p_=Gi===9xf% zL$1G$lHEZOpNopp35G<4M!S+Iq?r})sDv=guTdf z0|IQ0osJfw_6WJ%EWZ-qZ*uM3(A&4W-1}G|(1lHJ(60=pl7wDD*)!VJ)s>L275|3e zzz?gipH`KZRvu-w@tudzT_uj0x9&SswX6fD@#(n}W|_$L-9N-gi^u21NR}Q7$p~bV zEXa{*r53^O&CM3sSwTt#)eG!okBFSatT>{gUi~u8NUyu-$2Wgb7yWxes;0eegB5*I!VR+rec@kD&qMv$vemTcy7La${g zlem$M5PA5CX&pLrWtut*=r|0&gjAazNhr#!q4gKezcl%D5At(0D@+K4HVP7@ z>Qu-PMn**ajrfIO?e|x6_l079QI8XgvB?Fa>Q*0!M@Rr(vN;s7dk{5784~ zRHa^tn9c(wH&|G8d217H;K1r!6!biVv-qvyBhD&@@0qchOEdcNc~tyvfqoL8ssG0G zW;^E1p}@I z$;0p-A5@Z9z%u_DDh^15i>|EZ-f?aP@eX$WqcZ(1VTzm_bsirq&+0;nJv6DP@1Q*G zB|_UixF%@@5098t8$|9M=9n|F9(c&RkZs*FLuFj^fin)M5g~0bb4O{mPCx6UG9Fmq zMGSbn-#t_iCBg%E6xV@v3lW0u+XkdnwR7IXHDq`kwOjYA^*Xgr;2<|1%m+m=LyM+6-HFpO($i$$KDVbsa8IZG8-AHr50|Hj2cr14@8R3X zx8yS63SCCMuDCmvg=PMI9QFzw0A)(pul;31hTRO&ApP zb2C8yew3hrdH8ouC)w z3U`9P4+{M0kMpnch>8;P7lfnPD&ukE=}OCH`KZ%%JL@t}TCWwgZnSn-7Cb3K#K9TX?mZxbX~&&^mtQNsybB zwnSW%bDho@jGDyvk)OGW*F|@A=!lLw-1W>h2{ryJYvGLxU<-BFvhW-(boX(LW$Ht{ zvFk*SY7T!}A!>cyNd!%SLZt+rrRLHz;)Hr*iHkvfxG>2y_xmX&tzkt@F+RA0}P;MOyOX_d$!hhK*7aj@B9;av$t8m1d z*YwO8wpUNV@W&G+FvF5cysn7)yI?2)`h9fLYfuD8&d9ErqA>%v=dLX#b&r9D%|1Rn zZwpgmDPlNy^eu+KBR>s^)4FmP6hEGeo~vZH2h_kmb2F`3>cJB$0AUqs(*lkod;$lO zXIyJH+*rjrzddvR%T;rhXzbZ@+xGesodxq33<+_aciTOGwYUw3sL+A^`;Y9|+qgyJ zBrL2C621or)zL| zq-83s-lqRWc@c4+@*>)ipVj)~>FsAB+P86s3g|)G4a|fUFn&jgVJ&b9=k6El!i#;p zvM$Tqu+afYf}>cI1U!%j$W!t`^%MB>XQ6B4#z<|ROyM%}kEco}2RKOTA3GtGm3AK3uXVg8kU@S0Vnqu=11E4dZK2zG`dPTl zZQ~3hcsElNO>^^~&itna_;l*Mn5iQMD~uhxcczSJVRg1@c>4YPa?I=~yc#h_?1ZWM zgFmvr8^vp9jmG2iSLd+8i#%I{j6 zbXRXiMBUxsW_k3KGH>n!-Ygc=!_7aWrFJU&$G}&aZN?cVjSt~aOtuPmcC9~dj(g}G zW}%-ffaBCrqaA2<>&MX~IGt4XWKu9IKeppT2c|b_L#YT~8j8ogZsT|rgpQFyeI$4GlH0-!Rc?jg$^!l0zKdx|JCshrpHv~eCgZ%}-dvJ?qb2VJe=^XFHtMu2!lwvaV7eMOd|#e%eay7F{Sw|sb8djUbF zBT2r40yTQ}gZqFKj1P`sB$X3$72Bz9dD9zU?x*s@V4eCqD3hhf3fSzOW|(!d+|y@^aJD_=@5_Ub4Uj82s0}e*+^dcxJ`F<*IGoCc zCD?B=(p4oqlZ?qM|GEuee#!`v+2$(;oIC+$;9=X3XhH{(2`>(Kw#W)jn2Li&i*TYp zd=0@w?=Ve!?nlr&uwqbL!0Got_=-qCO3nC)#zZcef_IIr2e5Zz3Ka!DY4Es%JZ&JK z=aO9dEwnpZ8X5tW#JV2mAC7}Tg;&@rlj|AxaU`6p8Qnnp3|daw6#BGwd{s~{JBjpT zCj;e45=|N*8c)2Kh(Y;ObxGB0o98{@nIY6L{U#V!wmsboPvML6q$>ApKZ{Rb;|>EY zB!OA%It>$=ainpB*k=<0f(xZBefcL!8D>!}GSEn0bz~%6_^MW)dt(V_sFAX{xjC*q zv|WaTq=oc5UY^DN2aPb!ciI`pO?c}senh`qcnvzcllBof*HzYBnTlLbxH~CLV@MmS zKbgHq0K0H4VEI~eK#Kh1TQWh}{1WY-c#+r^D`it%rrr(3gB^WuW);d4L@;-40y%hd z{8^wbv#%UOqy5$dgZHz^4esH7boj%h2UC2`I)dApP^(g6^6=rqo^B#-o*t446feyAXa(LJ z3WmSDcG;%Cy!OJQf!UZcwx)eX#e?6=XiOlO1pOs1ODSbI%vxlA{P;0@3=_@?HR@6r zNQac-8E{MwJf7h;AxTRU%8dRduyBr5V_hg)ZwHAdYWUY@Ev4hbzknE2n`5^VI&y1x zhdJTZ_lvs|4cXgU6eSwL|8egm3{JDTKF!}xDyUNJgo8kM>|P!!r|QGnQ^bsZIg8}n z*11;!F|+fb4-1NFmUr1HNWQZ`IpEv{v`JwOJavR~y3-28-A8Vcg(R;k&N%7ZJ<>bk z&4rZC1p(fZ794^!?egg9LvY48c<}YG->81zH3mov0hciRHq*4)M-}1w`%)45$5O#- z1^@F#e{DPFrL5u;xtv2Yn5CwvWL8b zvR5%UfDoU{aGV(G6p`TJ;c;Y^@kN&6AqS?IZWIyWX~;!a65yhK=uqG6^+km=Sr-S$ z$?Yu>e!F#sTVQY^^L~l;pZ~lmWZOJhn)KPi z6=`~C`$N~=CUWaCD41FWER>g*C(eW#w4O@4cGWbF0}6QYY9F|Gak$k_;oczrToX$b zA?B4Pcf@Y3g*9XUF|%KX;7$l&wssywS1Lh-r*#q|w%VJCvq9PV1Yk>GVC!lXTifkV zgLEci;Gjf{7Fcz7!q7_vO5?IQZygnpbAT%tM~3Y+8sjZ1SsE)QDXr*S?cGAc?e za|hJL%?(dh@3egI8V`4;bd)i;x(8Mmnr zZvXZ7g)(xY-_^c4jC^z0NKADCc-K0y(u z@POd#(_*Rhb|ivOetk_feSoYKL9OEpP2k`8%7EGBAA{fg@@SNAAEgXV5UuBK9f7wT zhy`-BDKIip9;=(ciP759^4&5vmu&3RcVNUWpp>GCMFm}Z-~#a8%@W`A zFARm#vv9Ai94bQ`Bmt}enXBAIn&iPIxsrMinB}YP;sDVzsfrrN({|L5j z2AM1riOd!Sgd&%VD{5;pDqY8JL}1mQ10nA0a51VEp8gn?Hb zn2&CFy!SbQ@yd81Ct|*1w<1U(@B-o!5BIr_br>=s)S_LEFU#rJSsyG$T~AAdYQby< zCLEi54IN`ji;C7tn%*e=v2H#tdN|-HNGQl-i2_9kIAD9b1Lr(OGqGPj06c9I*_j(0 zX&Dak>+t&kLGU~WaB=Xm0)RIJ-z{JY%8ID6&WFH|qr(DGB{e0A^>G%nd}4nA9~nhh zO&M<>kqo%Qd_f^Ihl^HMF zW#t3a2Y~-MV|WzV3i3$79Ud-z{2w3s1SlL&){I6ycDaBU zn3X+^u6H<0;p-^N@>p2@PoF_hz-#E{%qqAFnz84wg(IfE*$O>|GcvBn&(`}6vjOS| zG(aV2jR_}G{^<1op&ld(161{frJtK^K1JYP1J5Mv4xr_qi~^1=nhA!><}eNT@ncl% zCX<4%5D3BAaA##N+T3^$4~E+vigQXZ<*U$}!5lDhYXyc17`36ZuiLk)VPtkWm%Kk)R6G1e#XKT^6X*!0y1ChL z&S!jgE=+}Oq(}M=lsh5b1-ZGj6%(?}>1o3H28%u=ZHe|tKm}XA37~Nd zWuC0Roov^w4=}D0S(e+;2oQfh82$6222TC}^AG1+iv5Mc?d|RHnZu7J1w;-%c#`FX zImHEm*Rn)PJ;=^6({#e}A#5iJmU`mDek&POSOB=7!=?no6s`i4jmxn@J3>&vHYvPB znJ8m&YCTz9rxCSCMHQPPM<8~Dehp!vkZad^QWf8H$X)Zq-W3ec6K)UR;jX6I}JB5~dNY+qCuZ_&A6O4_oFsQogL^_fuzsi8A*4;gL2<^epj&0wI3Sz2oJCg>xEzqAQ2AFtp>%5Y8e&ag z@ODSQ7D_6@Zn2D5mQ*2Ha|)K_*r|gK4&!&%D_g#=Q&C?vi3{aqy;<`yBva!i=;Kec zyok-CvN8%5VPn4o^jK0k zMhIPJW6YJj?MRE;Lwm_Oy<%1aCah!=>*?de3kD>(N?Y%EKm z6oSs7dKBE?jSzPg0g@|rRl<$~9yh3T5RFxs!;SA3fGDD_l3J+@E4!Q7x<#pCqOY+z z9pk0maA}XB!i@+Ik1Yjy*Y#x$>>WifD5S+3PazB9AFghJ7PzLDfGCgKdEVOqG z@jWZUJa<>JmwVomhe1+#MK0GCdLhhcI$!%EEH*caK#OGA704Tl;fguKdpGU5+xm~7 z`v_25IHuTj;54Z1|H@mAhw;Y8Sn@ssQu!8=X-@rzB-6Mj3$vk#B%~WCxcJH(P*KCx z@!h?y`$F5cS%D#iqvn%d+INF1woy@025Z*;yFhD|ZdzGjTY8ueBW;#t~ z6fm6fQe>2rAX($-NhZ7v;Y`B8$7ck7A{Z2&VfJsdx}ZnPt@njrHIVr5_*@mB7ANs) zP`lzWSM?7}#Gxn(MUZn_5Cl3WorKX%hDX5pt_oKxTDH%Is%d%PotOYH?2E?Z5PSa~ z)CJ5sJhdQKa&Yt}7F)FkYG3;co)5Vw+Q$ir3=Vsf%uM~s#wU% z{R!@D0Bcna9Pnz^AN++1Q3}@ru)^ohpN~_yP@-`<&FhCd2h>}dC>Rf1M;ylM2h%WW z+n`oUG$twx*0Q6f3ki_q#dT9Ul&N>QKbUfRM;~ErWLKK}1)PyOS$lZ!17^3M@ZoUf zA#hWF(64P{pHY%!l?`4#aLu>*H%d7+ngZ!VSG{Tx1-D_I=jy}@I8=d@q~f}5X*sLu zH|H-sm&adAuOn+G4bfh<)-UT3xjob@zx^Eg_iwvdmn@OTJS2MoP+&ani$>puGFGps z%~*pN-M^r-r&j!B*Rlj=KL(v$R!XX(`f8KwqLG@zp3GErESK22QK$pg8mif*UM0An z7|lDZ+^&;=N}T1xkRS_72`(JskBi%1mhClq<9VSijDZonpYCLZ31DV;zGmXgHI#->RA$y&FVd#fI*T9)7N{vSt} zQubdk&^>0q+^}^Fd#;0PUtg$0Fk=X?un{GMdwK*OOgY3buN37$0r2R?Np5X5^W~^k z`Sojk_i;M1{H0m`#ranl{MbT?laQU%&VvpMM{A`?3w9ISIPL{ss7<}{$$k~45^7pn z@c@M)MR2YWipS4#yLP36>=={}5$Ill`SWTG{*&jw;QaaXI0f$QB7j6Rl}VM;m~i4G zylVM@(@L0FmAw>o8rU|#>gE+$E-*tfF~A- zrKq`LCC*o8xhjP%l>3h!mj*6*@7uSf_)AdZQDJAAkdV-Sa0WOX5y)-uSr&?{Bh!z9 zf=)n*ukMZ!r&rJ1!vR}SPq6&C;lK8{@h=$M6Ddj+90sZZB!hM?&-&B0yLt#Og8CBZ z9c7=LogIGhHlylS2C}X3=!}vlqk&PNwzuUv6^;qT-*)cg{J-y95fmcmN-M#MgANDR zAc;iEz$pUA3NPDXxXMB|_T&`|!${xZlHuFEm3YhCTdYx6xK4J?5L%GQD71Yirzh~@ z+BSa>B{0I)U&x%}$VB$*Ulzc;zbybP5FkQ-9{jkm{OtqcNBr%q*n917X9e*${<0^p z34Zw7o}B$Fe}2w;R{Z@r&;FG^pB;jW|9*DN{*^x?rYq+EwMPCURrk+l@8Y?C-@>y$ z<`U-UZQNQqE>*^%YjT>*i2fna)G1vrX?YL#F4FLiH<>BSx;({{~2ZuI#2n_0= z6?ekO2y?Y@kjL7^+Wx!q+IsNu%p`O7(WPiH09`{qtONiDDlgU(!0;-tw-ZF| ztIo-g#{#S)!G<6f*E-tusN2q(y zcLlI{Nvot~gH76Q-$&^5LK^>M-O^_JR3cR93e9xh6M8L6eg5&cCO4 zbFF_5<^PqP{(GuWi+CVaj+HAbz-a;eQXV-Cj=VwYbXz;Y#)s7kx!?;}*lz6_3Ukni z?)En%NaSlW&Uo z(ZFntLDeD7uYuxl0uRnTLV8MxhA>HvP99(M0sGc25&WI-88uvR00nCmxP>NRS__W= zssed;XN(O-gEO^<5KP((FrIzMk}HulD2;cIw?#Czu{Fuh9=Lm+`28M7{pIkv!2@_s zm^Aq-CwuUl-yTdLw;fh;aDcoB6>Gb|CRE{SuGOnBYQsViY}BP^qMAfQ3cF~*ti zMzaF$vHXtyy>h|TP~>L-*ty?b%S7fue1GpAYzYn&(+H>(<1%*p`=DzSmXvgXTb?6( z8!LJHjvZf4OE>T*_U!v_pWSNhL$Q_wi$#1DCd$xs~_WEufDngoz~3t z87zFPw6PA(F#{44_6GrCUon6is__m#T}Rk$jQ@~~yu*Zt#fulkuxNT}iTqDRjO{>h zOr{Q^2X?@wY5Fn(F&GYw@HcX$=>QGEH_Kb10v3@Ke@2rML8|$bev`6%wt8IA_M0dL z3IlPJ{!w6_W`M8U65k&gY;2S2DML%74wwtqpk125x`MQ*dl5h7!%z1M;BXk%x2t?s zr+O-3X}we~k?d=02M7fuU3xfrf3sRz02REf^#83>cx&fPs}o@NELU|@XgbIe{-BK z(SuGni!ggRJ!z(cK_yz|&$~aQffjHrPLpuwg2?A;0XXit$HM(6%YA;OP|7eUc^`zX zG$&;WoXCj5%fQf*PG85;8E@eQYo*qr{>KWk6|`Bg@9sRqJQaMD8x?Cuyg=fpDv3l z~oP%+lgAlLF#ubonpm!up4%>-rvd@`m&u1si;Nqod zHV`HXCeA|fd1R8U@2XFPyZn{2X&X-nm#s#Aav&M%)7%f;y#M{tqlk#Z#TXN1B)kL= z{_~!7$wpdp1D~nyQ9s(xmKM_BCuOx1*I(I-o~P7kgBIWcP2GkL0ch z4R~TNo&q53Yz+zmLATw%|3<}EG@?&DG;+x|z_ZZjYR74V&)Ouvdj|lC$bq~f_x4YN z1d%QlgorEGYvgmuvpvqWJp#YSq(|1yKHopuAV()2MjD(cVO^E+5)js+ritC2% z<~sjZH(ysDrWX_l9Riba9Bq@ZJtGcqwr2%7)L}3Q^dOsNOc}u}9BSU)Ii+k;@eiW|xyIu|7KP@EEg!ou`J7|rs8|M9oC77Tt1 zT^nF=AjJ`)JQR-Df~em5W@cA?(effXT|dRz2uchfQHDTJu8>Z|8IHj_fltY065p|V z8-X4Xl^oDpOsKZ%JoDsUjqMn3kkW&)^fl!Bp`pAUH9Jx1z&|KpIry(WoqTc54j2z3nNz}^pEL|8`AAH5iW`Q7%4B?=W46|jF)@5_YbDR`TKjF8Yi zUgvPl-Pm&|R9CtN@}#e1q`_4D`zKHV2=^TvCE9Cqk8IVAGsKiGmwOa|VyW|rH~!zI zIC7eS?AY{8*u1s#YPQ9h3KWgYJq_e9Nl8hNR*1-C=Z!xffqqGxqeI#)!>9L<3WRp; zGO!$Jn3=YRYD~ao^t7blf%7X>_hL43;QGoy)nj$|vHDKM)>Vd>-C&D>vbSyA#PR^E zG_oAFaUnfOUg7X4j5ToDeI|@7c3$ul--dAK&1yj5R*LX6Wt5LP)N(V@kVjPU)iiW8 zhxhdOd_o^&YJR>tDcUSPcWo+nj_Gy;R}W*Zv9R)t0*ws5I|wA5ZcT$ZTH=S@Agy@% zu+HJgR^yD(G$t(uHcN6!qOcS<6Fd%6K&%cJ=#HW!V~ZnrT52g0`bmY`qz__qf)NAW6P{+AmT7Oq`gr}8|Av)V78d*_3;zX6 zaHu9|fgo}ryxgS|O z07Aw^F8fO)=0gjb_BT7!`BsU9>5}!Csa{ZAdn)4}SBQ*L=Qr3zq(u@wbTfo&ujbVR zZX0@YAK~*-k+0jw`#GWypCD*v97Y)@XLf+*);h2SGd)bMH^z74&NgN1Z%)=H?s@*a zVQlmyEPx(|Px?V&VNKlGbrpzviMjRLt=rFX}< ziHaX?;mkl6H=eV^SW~kMXA(w4uS_}}hP$X^{bt2NxNP|O7S6T;ETMn!f&OyQwQ0lN z4c(+qP81E%#X;gmk1lSZ`O|3Ah_!`r=~~yuG-!RkMb)l$SiR|1-c=)S)I{|Gm zj%NK6S3&5-5DFzDcsNd63XdxrBchO8M1KTocXQ0rO$r)Rvunz@!W3O5Q~NZCdJ^^b zLS17NrYI3x9^Gb^0NjRySiVvr-tHtO6YC~dH9hecXjok%l@MTXp%O(ta!|}B$*r)S zgVCtw)U^ypk2!t3Rj5I>s%1NCZo$MkXHk219{K%vhggW;BS-2;D3n9ZX%RVjlrr?R zTi=?U4TgNBWBbIz&Tk-}=scaV-o+^85YHzfxT+C;=AKSi=Y^!rL$_BCm4dwQ`r8@z zB-|*z_W~Vl%`qc>DHkpCz8{|-Mi&~cVQSOw;QX`K$xY<>`eu*Q(3s~A2H?pzm!JNP z@qVcKXFIu<#NLBDCVM6YBZS`G59l1i96HXUZ!60ugU}`5*!5;26f}z%R7D2q`{1*T z#z$1bZ}GBa1fcyka+qVp$A>k9ky7KBTc`D|yjG_vCqF-*?rQYjDXq^14iH%xmRQN`CL}mXvBCt6&-9roXm4bQg07f=k7-Xk$8ZvPTfD_nB`KBDG0{q z2~{=W1|aYZVesy)`2t`-sSK|4T?mgk8Dl5H+If2!5TJh0aPyttz)PaD`F?_Hk&)kkb>GIS|?q&$e+z08%2l#@{D&*cA?HP7nxhJtzMri2-3x%!VC9Cp& z8M4u0hT2NrL#*j2#udz}=rFiq=YCe1kOdpIZY7K*hU3@*^MMX7rgLUsWIVP?f0JfM zZgkkmEV)un5y$FtQF5p5!0Pm}Tfyv{=l^jAz}JindH6;LVMM?*LQ80qI5;?norrJr zt@=5n_Mib(tKRQtGL&pu3Qb(6j-MyIxd8kfn~?4YS3!peZ8jO3fh=2GmLEKU)kR<% zB&WB;QOe-L`>L+SzMfiOex{YfzIDCv8ydlVuttYl2In1L0i~Xlx^wRa0{wV7IPTrQ zZ~E*pMrlt;QubiWSxJM&d)VG{CF7pJ4A1YNUO2?hdeept zm^iD}-*`dfR1t*XGOlNw3~l~fDTG#HRIsY5%BC24S1p{l6G>Jm^FCKp45q8Q+^#v; zl{Zu!_~5}wIr79{(N#IVFC1kNT2gI!-}#c2-<@s-+02c;td5jY1GBp)X+bIS z(NqUtL_oe#F;w2S4<=W``01kE2aa=?+>jZK-d1OtWN=X%I8{qNuGJv1z*4e^9`K{E zJC|27%JrgDE$p;62BhdLg`#?uh=`iHWEjTHvFHHQnt{z$84f_89T3#CUv^aU*A3Kd z7iHJezhGv%KkSVRXGjgq(ZHg(s`<;d2?cg`^XwS6vFkT)8-#b_4?!HbCP-W4%*{A- z@ZjBD>v!xJ1aBy@aug*Z;Tlt_dLRFspggbBeR-@MY99s1WY?K#Xa{**9ZpcCei7)^ zXtt3b6P8Gb&k#ooqNjzrq%_Knywfk$z?I9$b^@rnuY}hKK=e8EieRB znC_kjh{&@ctx!2_i+k81g?9i9TzLK1DB1{HO4m-OM7VlUdFz`k%MH4<3cT<2=A6D8 zv!oiXWOWBC7dXtC57X6k_OmI$Ib>SLv~A1SkubFvx#SkvTb@FR@ZAa1t)ZMA32i+_ zDp;mUL>&*+K1U7+kOGCrx|-yZhac}YipuQl3q>3L-AA;FPKruwZ1l3)N?X>Mo5=*;3fbl8B-BLiw!p4W@% z!05=hg{@17oZ+NEBDzn(-#68=d?5+Untv~ZQ>OpGB=)RexFf=xSMKr$%H|J3$mRsO zRWpwqaAc3AWfvg{yW9ZiJR;*;6k5{Z@J02oBrr4M!*N+>-*CMC*{K#Udz?I--luG7 zbwdNzJKh8gF`Kr%v+M4-%t`2m`9IVrUkok{s;2mgP!wpNK@yl+PKeCyqQRnSn|b|q zc(1CX9tl_27LRMe5jWS+bGqTVqQZY*O~K8C0bE!zkB=zgf%h9Gor|6_a@f|L9)}CL zdbq)9;7-mf&r2Byk)SINRac(ZBl6(NtUu;eCnE#FEE;E_oRhQrlRI+lqz4`Cr%#<0 zrzN69Hkwfvar&`U0+EdICh?XZjmN$URN$2)RqA*#3oott#D0rnfy$CZYYvOZK@PPL z!XSR=-VF#imU{U#m7topCBftq&1p53T<(k4^7^@NECHsslo9e zB5^e{ALS=%gpCq$k8iyusChcA%lS!d`CFddVWzgB%94?7LD+7Th22KpN|lP@%+w{1kQIw=eqY<%$J-m)cx6^ZuwC*W=pz)d zdvVC;zHQ+r$iYxu=0qlG*|q1AlW?8rCFC~toPS&}fdawmt*tsK3KrBGuVRj}&+FYe zG;w9dqZ=#BgnLws1)6b>n2MTB!gTSw)3XyuHMXSQ0~@WH=LL9|UcSC|qi2R>9-HI0 zWadc30DtKh3O{2dBi-K?J-RfYQVW@K)y07W*tBXQ2q-TZydsJE!YUCwXzugR0|NsI z4z)XOa@GJ@p&^38xo8ZXInc^hGnfon#=lwVE4{Lh{Kjsmtz6l7f7b;NaPDopz3-*% zlg%GxVq&;%UB8JPrwSfi#Em)4O--!gsQ_K6*_Z{&9sk7d(DoF7>1Ym)b)*t3Q&o)v z$}4HYQ}q}oH*|H-5D!JYS;<(!&Mwh@5EH!p(qV^CJWm3;7&?CC@nihSJ59SjW*7+O2k{IzSuX;SW-_Y zz;<*(>5#2KWC)ysbn;&{>S3RMOnw$OQ9mJn4!>NKv1~2X!~16cmmp126#j!vA{bXZ z4LJalS_35sJFC(lz^kco;LV5YUX^+2oSTWtROQ{rOX=mgI=EWtM|+0rf0%Am_}2Io zdW}##bGa^Zxd#Z7{W=(-NcWp54u3zRLTlO4-`0*MH9*l7UOO3v@&_8WNRT-k&7AsR zP*dbh03YY?;$6YOHBAH6$$ln};S9KvtocN?cVx!GkI`)&{W)D|p#iVZN`ufsCCd=3 zr1wk6$(>l~bjSEjhaa4BiXd`t63Vg499+3iwnEM)7M~#$Z^A0$zKjNGi}5dLu-;Zy zn3F7kxw#}bmPv6l`pU}hb?zgn1}t;mHGnwcqGiBD_7bn{iqW}qFEV8_C{c3gGu}Mh zl5oh&V`e04Cb_aA51qtli+#Qqr_29+PsW^yrg=++FVe6mHWcA5O|~M^Q{vO`iTB#M ziGbvE{7?Z8W2*^}TDY+u{Eyxjq45;Rf{zG>M+<|EGAM8EQ8ZOlZuQ4N9@KO7f(ZKM zh^}X)=Aam_mAiaOU;ojx3i<>dul-|W*+&K)0Gfih+7Y#@*-W4mVb2F#u45O&P_yP> z3Jz2w;|uUoPzlm8e$cA+JL8X*1-hGY9PDYn?U>Hwjy^+|_K&j=z*%9rffg~JKdk#u zw_nL)xho&di*bn?)Es-I|8;az$=5g(+Qr^5(FxsUUpA^jwy`1lz4*NW%X``9#bh9h z;K0z^{mh}u;}bt|qm72F%Xt0g*HAF3J{}Nz7vme?xuZSly-qt-Pd@Nv21uCH7simc z;M)_4-RQ=0|6x^zvn=i!^eDvB=gd5?U&h2hdN)A@}WYj(35fQC2 zP1mlzmBncB$1jmV#Q11r-6%Hf9%m}eh)R5g2Zn`GB}_3}c4D4WIFa*V%cXqsVFM7G z+bLyP(?b$7Rf-mCeVCLVZLd*YO7e4!<7q>A>E+|IO8QY|=uKH~cl=cVzCOJrP%m^O zQ+y|Xa+G^CmC#f2_VX}ALG=|AxrzmpoVH#T??E~dh>-E2v9(UueU^yY5l0AsP#dq})>Ol7@u~tzNcZp**XGp3?TqsUZch_$X!MS*xrnWk) zy-N+FWa0Q0KQdlk&#zZO2F!sLQ4kf3NsIoN7 zqeWtHP`j2022D#_@WPtKT4EGLX_owTrv2z6V=E%*#fyrrFMmRA7cWsP@fVNOuH zlT~l09wv;Iyp3=c>QcirGp!{AHGc(=hYAZ1HcOZ!9@Ns(UAYJKQ>Esw-&DT`JpUD> z@dQd!cG9nFlYLpJlB3r2etPPjLn*_TZj}L&klI6PU0aPEe^It2h*E~Zx?&v@OB(-8l!**1S2xQmQznj=O0S6>ALJAVm{1SJs~U{Q>dWs>kbi7~1!bf?SH{4bxev z1b(~!uj9xU_V zV7NSKoU3?R>GqtjAPQNY2dAU^D0C6w%cSwi&|kfG*5OV{G(I_oL%gp_I~P5X>V z%|xvgVzjE+7Mvq7QLe})1Sr`@v_}RA1o(P-9z1lYT!sfWwY9ORI>%!+(jpj{eW8fE zO4Gx5K0{9il-Hi77^|ejJ)j`9WI;@Hbh;CVw1#CkVMf)*+qW%silz5!OmI#(^UKrI zadxnhEK={TN-P+PR-=kuL)EjokJmdiG?ZXJc}u*9PTvMrXFy|K4*F=ImIt21R7z)V zMQtU)%eMyQ>B5G)WoE3HL0rR2tLVaeqv;7`Od{6%udQ{Jx2IXH&YqOLFpYb8`BKX&Fs6o2(sT4>u$ut z=>)M!L73H$YSK+h^ntSn+4kfEZ)V%s!%36UwC3a{1ZJo9lq0b1tlP)1Z#H5P`OUx`W!n?$4e8A&&04`b>T9rfHn+9YE_ye=JeCUH{zYE0}x1T`o;~^V)G6@+|zZ)uN9FaEeIP%l7VPnD@_s0VaxOBwg<|e}T zk4{J4mXPe_V!s%%EOy8oC1#SrR*B0}j6zd@^$7o^v?&(MULx(ZP|;hApFYN!Tx`_4 zhzhM=OP~{+{*3t-o}eNd9JE2Nif2kKFygC6G6nk25?E_OvV48r_Z`(j1BWnMdY}pFe&fk=9^%guk_rG#Zga?*r$obXeD!>IACN2-Gceqn;;CG zOum3OE!ukE(5F;WY!Nzbv&E>f93?Pu9r+SNTk~Zy0z+8;m~!U-d&>DC70q{%`xp=; z={NT=N-$UhL3a9N18N*x5f{cj<1lNt^3$;{-*q)aq4)00*BqwOsnr1iAs?zkVs_v2 z_a|)MprXn`_kx)-N-t1dKJA|Tg-V#dYX(ny;AVBiWJt8mv)uhU5m6irT4+ZOoE@h%} zu)TJv=B-BkCpcfBiomR(B@)$NbJuQf$u&p`m;REuoi(=K9Agb1NV_U&H^3S(LtvaK z8NM&?F{96sYJz1h(@v|%aPiD|WtLA@1)3@Nrx0gM3jp!T+seNH$4F12p&NVqX@M#B zg>t_%{O>9ZF5D=Po_=lQobH=aC{Il zdfo+HFREXRi<0fj2}_#DjeWe$5K}ce+$BU51E=^w8iI_je<`v>etcL@x*Qccqv zLAyq6i(%~n#nAo?(R;VDiK0qOoIVd-?Iv2|@5_<*DUNqF52Rs#8d?swnxizM%H40W zfYZ0akc4ODO?Bf#7;m*?y}j_2UNs^sDkUu)*mVsosg7&4PcZ!ESNM=8Se_NuHe4OK z-#>tP=}s!dx>6rN_vvgseJT*=I(jp#u_H3&7-$fdafaWPVT9j9Lj{c!4b~$Wm;e0q z@!v$^e}MY(??c@>7eIY7z*20)RbnR&J08%3XD9BXZXJ4!#SI9DQq5cms$Q%fl|N_M zqFx+{tywbXD{^x;1tjk8&JvvXy1lS@mMC})^cwgZfif0%MUqCwA_fhtbMn?92L}hN zysd0BhYnGNT1aicsVVr3C~%@SY85$zjvstjxaZh_R(pznu#H41WEtZa!N*#whft}4 zJif&GMTrljEqsr91B`ITtpG2kfEvP9OWDNiUq_lZa}x8f{sc&~aHIt(i83;rK~YjU zage0?xTaYiC}3K}55l%RGY3V=%UC<)BC3S|m@Z_4{u%1v(7Gd-3qb66dd@E;J^zp)$17mnyz&&rK)$J*iQB-}FZwYsRX^pGID-gy9e3X?(2LIw> zudF}biyLJ*G<0dOo7v7j?!+G;FxS50&ce91y7ZLEKE#Ecb*c&oSk+p!1r zwI$BCClx$Sm#jqVb|l@-s+(s%;q9?*-GwPX+&17x?nscq*s7MFk!Ig(4CFgdHP;o+ zBV4-(pGo=%{H+`!HEe7WbZ!!vI$utJDU|!^e&uWE>eH^@U%4`8&(@|do061v$K#rN z@1$ofXjfEav7e1zRD?b<$@&E-t*Fs)pxh4z$Pk@4L4unSl?}*P-}Ls^bQF-SJE=g2 z%II{d)GpBJNPLXGwjQ{*o3z*9_+G0wfWkg-fz|Xt5XL6nBpybe{*}e=DVl`R(~H6U zS7oUo=P>v}>a_>!TGPZ{OR8+Q1*m3@R&z2WegE_#2|S>` zmI7qZXJ$jf@FDq-zEIFP3#|}AMKnHNG*QZ=1fKb5pl$O^q#iO;1IDY1{*51zljD4?y)Mu@Vs>)1{FH% z5U5!at+83D-ddU_yqO~kuRO?abGS+hAW-j2z#vdbFw=14#6oUhB{6W z;LD;0UM{ju3&0KuW`#gRGWx+tT4!>Cn&J(I9sasZ)KXiVYe0SzzQ`|a?vv1nDWcO= z+m@d{{&f$*7%JA=JEG)|7x|zH=fU_(!lp7`w;q;D)Q0MmOP3M}zD8{BS>PTJG+uh= z0iQAq&dfG^XuIjI8@k}(blB36$oijIf{xwu{bsk>pGw^Pv{8wtdp{s(SWK+gK}1PG zp}IaR>trml#ol@88dEp++V+BN*V^+64I%R_w783#e<(4TlN4CD>U66sR^hbZ(2XO8{Qk--Jsov1)5-v;1`Joiy== zfN3;zfhNL-5Ws63O>p$M4S7IirFk+vck7xii<_+3udj~*`&ZWYNl;>Q*d!vb!u^t7 z!jby&qtG!#?uC;VMwzWLpj$A?%4AZfag1ItxsK9u*1!r-qg7}}l59Oi**F8`;IKaZ z5)b-o$!RG;pryD7K=1E&8&0?`1A@izcEym94~mK?DNd21K&9!Y=fS0`s>ol&5k6Yw zb>JqS7(a11chja?040*qV;yaUr8Srktnz^Aj;Hv2E`r`neYy<&07ea0vNy|~!@#dy z>*av4X6r_?pfkg$Lw`D?V%kTq&OL25kVId$DE_|oZC_v5uu7#)6Mc0%zvGNsS28DQ zRx!x3=?{C%V+*-eNd>kf3-qwdXQeUmljXa-d_6ddhTOM-Vm80*Vf#_eC@5?7P(=ZDQ#&iCDdFPZ8 z@^JBJ*bZTi81L*lh#T&FVjp(fvmLP0NzG;}%E~sA1cTE`x^=BfvxgO4BThwN+(~aB6m|t7fr1oIF^Bk#pUPo#E z`qVP8AU^twN?`I{d%$I8qCq!XUQ|ktrYsb|d}g2-$h^_;W$Apq!Z|Ou(#b5%dz<&N z7%qyjq?Q5R^>-d{LI;bwSR;ePB;r*udP;4Dh9bC7;*Aq}n|oBTLCoCVyB{|*miTZ>3dw8>1*HN;JZIb- zlW?simMMtYZPYcodOS2zNa-&Be1_{(rE6gND;yfhC`@(wPjsk)ltn#z%lnC$Q13Cg zMI@&OJpa-kx(B7IkVFRnxpc4jlb|AhTn2-C< zOC;~H2l@7g+PY?d*0jAM7W=nPm;WI{XvH(PE*jtQG?IvP(fWd7GR?6k?U{omY#fSQ z3c+YRkWi}x05&f z3RyR3QZ+BM%7gxQxkw1Fj3VR>`;>Pr_3F4nIA`9_GoF(~DmOC@r5K7csNfgzJ)wt_ z7H#L>!9V}Q)z|bOCV*t=e%e_uwVEc&hS9VwT+Vy4V;9pbkeTZnKM$)RO!ks-DEnKm z_aHjjALM}aFZu<6Gzo_?d1gR*@7Qsge`4K(ex1`MaHGT7Z6~Tc>FiUO-5)y2r?b6P zey`1U;Ie1f8}O{;k>4~rAb{MkOl&F;lW!Z5zS~aDeOzc1wg>;SNo&n72zS7QufG== zm-h2fLN&|>dZutbF0&d=# zfK_%mxg5NTM?JNxc?r`8mEA2^E=|X{;D9!DTP)6VYXusAyfQOMRu^AU#u#dL&i3nt zI--ZoIaIO3h4MCGmgWM$r%9q842mHA9H>?bmCGCVZ#wH0wm05JK!ACPs;M8}mHpJ~ zSWhKDhT1m(A1=B@a2jtJU1;2sVFW6ixuq1VW@L!{>)H=R+^O0_Az%Cq|@8wpiy98}s*#X(ICqS?}&MTq6BUVvo7t6*^gGGouR9 zJ(O+gYpAf};;j9Kn?XHD0ar!LzL71AqMi6s8dS~IPyez!=EFRo7SFA4LW#@p^4_y< z?YQresSpWn^^YopkKVb|Qb&9$^qt<$IUS%w8@@iZ_gNoiv*f(hy8G*8#avD&$8wpv z`eMa=n6rpjZ_~a^BdVsg-5I+2f&DIe@AiZ4bYEZV_o3k?%2-kY(UVNe?QXDB5PL-h z_ZY&w;Wc|-NG$Bus+N7`H^*7tXf&@1_C0i)TawnD9G?9d*m%&xej))Kx~YR^ba=7V(C zJk4F*NwD@5v#AD2eGESl`NhYdU3vY=#=^WE!c#XTLleEUi|Di8Tz>;zI|;+yy^{rY zr!2$OTYBu@0a~*z*i|W+;FGiQyWT?K@IigA9Zk4$aLP&6A*aKSa$GtgpFEFA?7QQ+Zrk_W zm5Mg0l$EkqQ6Vy-tU`#4sI1KFRVXTH8X;K?BQhd0AzDUgSc!~CW=8h<9Tz?K)BQZY z-|y@7yZ?AzPs!(Vy|4Fmo#Qx;^E@ZbeWA>kExpYeLErJm`Lg`y`J!sFPgaksfGzTC z&MYd352*4QS8`QOJ`x#dU231Oyj}GCz`X$$ieztMKKp^TgiL(ZfOgAk>qp={%g~Pp zVyylsc#-GwKW{LK?Wth@UK!PdCOv9dX|SEE7)1Ip1X2Qfjj)M1^7DOJbhJSc2EFOD zC)#>#BfW!WVP&W;SE6W@b9kBSV6r%eaPH3b7*>zb+@_#s28Ejgx*q7kASTFA$72;> z{odq{)AJN_#9ThzW?Bu{xLS74=8mKfATk)88*JgAHwzlcBQ968q77GXuXz6}t6(~b zjBw!3$D0QRBz(*hwGXVWf=^IAT!48cRKXqSfs&fI1|^7Rgz=)ZZ9) zogq3z4xfh;H`#l}b4$D#pMT3>I5l-eZZZ36v7RS&Cd(LXUcdr+=dGrhTmEO`5JtP= zt^Nw{y|#TpjZA-mLtld{GOGE0d?#IKxk5UOn!iz&z{?rs`z{9Ib&*zidGd2(x=nr9 zY+rHVL!+PXi*q!VY^^P|=f~vYI=34@{v@xY=k`70nYlC!vSCFOS7B#hv8j3_t!ac= zUK;7s7{mTZ3Uc?GD9S}^;C-9kQa0`Pc36Mz<#nqKo_$HhnWfw@YgXj%SXS$A7uI-g zs9bcAQeYGPYvpWp-XCAk!U@E6HbRipVL=@170DZ^B>wFI18)4`0jb+XTNG?CF*0&2 z6NB18gnkP!MxHj~g|uEY=b)?dGp@>bgg_K;yx(|JWUtnQfqvmW+88p{oeO0kXzC#- zrnk(zwMfokpJ4}9`V#VuZI`kxfw9O#W%L-20VV>5@4k)XOd`0W?SydbbB$NPR1&mO zm?rns!+ZyHMg2Zki7+CbhmQb5zeQcM9CmxFGQCnFh(cz(2`15-4T1GTtO-v%m$(30b~_Kh6t5skb; z;*vtDoR5r4!S6bS2i+cybsYXD&AQ)^kLgjpMaf&wA_d;w4U!Je6EO#nj-aAUw3| zxxLfZQwa&IvRYGW<-XAdYJFp`GgTPBUJ)8eFdK3WbhHU>WVtdk*rT~+ngR+Al=9NGz!nkld zMK}f>G*E|QMH~lZ%1Ln3-`?Ibc5lKeaCuS*f1(|6bV$Ou&A z+H{EGlSmY=2^@Y~+3GEOct2N^TpA3I-W%OtYQG3?_gQb|7aa89BHb&}YnC)_aZpNo zdm7YST!fR3+84-ywbX>~fdRY$x5}P92=OWJu_r9YxZ?-Iqh^O`yRm0i-~C!=faJ2t zwIkw{b2RGu*vgiMiW}xwKx3eAbYsn9tus?&139!yQSRmz{!Q%>?gI{8U*;EUs^qPw zG37#Kg8-n{HLwz-KK^>T7pMZ2CKBxmAD*Gj+ zg28aR+VC2lNhN{2+b*kDM!CIiQVZCPcy#e<5ta<4L;b@dg?Cd{dKN;ctKGK#nn8`m zpoD@Na;^T~xmNV=TubIV8Dqu~;h+}&!4~%jqWQ*FB5l9o`6ZJV9h3%;zLqh1gH>x8 zFc`bFK3ZeShx*$X2DNUZUB;1b*(Ul8UP@cDtk^p;z_STv86)!;J5v$uaVejPx^0>H2w*>H| z16pFJF%KwdQ0J-Ch6s&vqn>KtmcwYbQ3-I*o5lBO!YhMJUw3DoK^ariaNC17!)1e9 zD0!9qc9u?u*4&dW5%A`!{OkI%8_WK(Vbq9JM?>@ja*U1|;bHP5%`1@~LBFq+v%~OQ zN;W(&8hxnqW);w+o8`ClLe<1NkB;Z(%k1qEw(z8Byh1#-UVG2EFjnat)yO~%pY=wr z%BhIOzuJ}Yp^2XI)Us*;_#uJ(u?MVj=3sttZ~2{d3qORSI5SN$a%iYPKk51RyB3=< z7i-qz?E6ae*yGpV4x?}ie$SWh*tKfd6S2PRZObR4M_9#2wQbJRgoeeY*^!#56!^7b zs-s;`FC*(9U4LtfPQa%am)ohmXcv7g+ME7KdBtqq@p#H@g&`Mhtd6hMiK*e%w8qhs z)KA4VSf&+dCnGG+=^~l4)P#IVQ8Vl1lULk`h-`)B;wlXcusKt>TAn zk93|N!&wRtwP>*^#q=)`O)}_5nU2v?dG6$k^dC2}2pQGEctsTVUPFiLCEmZH)ZL~% zkc5Wl*(pqIdA$w<1_l{e#9KUuO#<}5IV|ByS!Q44Y1m;=ZK?i4wP)bKUX z*~qcZekioYJ8qZS6Zt6kbquUTY}55*^W1 zWdj}EpT~=K1VzHfZC}&jsMcGgSX@G_enBEx^u15JVl^1`&gZL;T2|*R!a|HB-{;L*cAoQ2EhEB=U zPoXdS+fDo*t=5(q+$RFcEHf>{a1DmGRQMdqjsq|uw{TU-?avruwS;qqM}mj7hjOS9 z=cjor_)z6^@wQu2s9p`q*hXSh)J&0#kO1UR%OM&?G>OF49mYEaX)`2lDFdG4al9yr zT-CHsY0p`$20%mluF$s{zFD$$T{sJhuU01S#&fSq7Q-PRrI|c2Yd)=Cik6-t$O^sR z^C{;@Z)di7@mHZHnzE?%cMPJ&kV#Kld2eSl%W`kkm5b7lKKn4(6(dB)e2Q^=XaAO%YGp5Td-R$k)^-LcsX5SFxyM55%`oTca%|L z%U5Yu0|Y`CMd>3kHbZaOe$RXL-l2Cw1V;e5q}iHJxc|Zp-nU(tKpeyC2h{|0CA zRaleN%E`@QHc#ZF_0ZC2kAd=65Fi!M0)WK$2{Y?=!P}jxBBGg_Ib5u80%>UQ}E*VzNhz z3bwE05^1?eTeQv*i9{!@d>{t%ZI{@xo`Uk{JE2P69R8J?cgg!ugrY|Si;!Q^T$m>n zRJ!u>bWkx+^pJ_OlR$O$qC5qex$UcUau?$ix%f>c8VH;)WbGia2^-yrZ^Y#!*}BsQ+L%c(Th|P*~1+Ng@Y0;7guyyenF5 z?xc26(+EdROF2@CPDIBmqu}=5GCJ!a;s8dW+oe~6?V(OdvMaNvk-Uw}2S^5++L+CU z0~=9zJ%hWyLb(!Jon=u%PTgP@D%?EerCWkXSeJAx^+951u`@74yi`h4S$qcw>2>24 zh#kEyhP${{KyRwpSc>CI`khAsWGL+Dfi;Z3fSGK8YTn{Jb36ahmU8769Lk+<8PqkM zr~~x6cWM&V1{Qc}Dbxp>o>XX;DNci-zip_Xa+nI=DH{gm^OBmUM~jiE?0#(8+z z@eM{)rE!tjAWlGVxCJgA9MnUJzaFLGF|`nP26PwMSMG~bIsXogRQ%|RHRp2mdy2-F zVPrx52o9RNH;3C-XalXu{)_|z5&UP|mfYyWZ3mjhQkvf8KS#{kTgIQu>r^ud30Rxg z3Vz)4gVEEd``&CuW|4agNfP5`^9^$8pJ9GPMdX!tJ7_SxIC`mc6q*!p{79#wWhd21 z{@Ql}xhv@<&bf?Dk5^3?p$4XfkF;y@i$R}`RQ|#Kc*<8euu$``>WgUO#5vSHs#zBMZN4o&Vua8^_NP~7PjcIFBATQc$r_UckDp5{VYehs!Zx4gn__*% zr#$2jUzodD(;c2Orac!3Sk)@7=K^?{mE%B8yC{-AbG*uEUjCWNYn0c1byf4E&2%T{ z@)s%@H3KBR<;kMPgQ?C6xBGaSF%0aK=@P^*$HJdgk#2Cvqrh-9a4iDNebrzYnWEZv z0S1pPyBX2#T74U&8AZOgk*z&#t2kLw8SrC+{9_>K*=^Uw&;!MBxm3_@%<2l@DWX*o zzb|wCI!c26agQv6I%L_U6xs%waK};d5MI?iie^(MvW-zUo{#=Oc9LJPD%MX<4n1|Y z;J;(`@E0Qh#d+=~Nv2&^0Bi=Nc_=~41M$;&{bk51$ft06NR{ps-hm-yt@V+6rL5LkNxPG77W?HeJL-8TS zzsP&pUiyM=$DS{!EV2Y@JWyMJ@@@I!L4yo0T;4~p652>-jcnfwJ%x%8ssvDI$eIVe%F(it7(;@c{dfwdDIxf7(JsDWD&nmfrKwvh9cn}G#J?p z!fPo$z9Q5gi|*g?2kz}tNKJ~vs(laj9^naaxE7GC`FM$$3pDcLO-?AF;aWq1mkFgy~6prH~;hA?$u2b?w> zwLn8GGIC_g$6FieLS0R>8l6C>$mN6-rw4av^c^Y2fg9Yph>#hrVInMS_o35ZNy(fI13FAD6}Rk>+2j+BIcotgd) zp#j{e1W&+h!k_Vl))Gdpii6^4DQWv6$nhPyy5n-cli=P37k|Dwhbn5szw?6^1FLk| zxd7k|%(05iXM`Q>gA>zmu-hbiW?_jrhPOhczjoX&W)Baj$yX~_X#^@U!$&q7S|T3o zsQe~z*zM1Iz0t*wsq-0ff}^byJo+SzI|K-PKwA_;Oxo>uRtWkrb3Xve!aYFXqo+6+ zEqkP;*sG}!aC;yx^WU3D7{rVq#0>{C1u=cpPQc%cuQI+NkaMxt3v}M|*^+*yNy48i zS#hdmji>70K?FH#%3*u>ob*2+S7>tjVueJh2jj&}DkB-wb6`$U%%_1o%Au+6UNa*? zG;Fa2B6S#}Q-&#!4l3N}zHoJUa#q2(jgxT`tSb{`)PAk z=3vub=6d2bvFG;y7@~D$A|tJf%eqP=P7;EM_2~nW)FX5%y{p8k+1f6O0$5Hn(jEoj zH*8_b^Fc+Ihe(#KGAr&ui!K-IhS4aWp@!nIn5ILt+eOad5Qau@Wc%7!DQO2$(ig9x zJ3hcLPjy-ap06&8fT|} zdPY^Xg6MV5NogXmSj!$vJvlrggGOLvhJ8!Wb1hWdY9Z zz%ygGNZ;p+qv#1-+9T5ohH~lKCOTvFEYC-BGc zNlFQ{0L=BU?PKj^>?oD+Y4l~J&SMPqj7xlMV}c_&J!i}ULKeYn9&)1fAN88MmJ>@T zY74o4Tv*0*Wy(qV98S%Or3t>3f-8+zy;MTOud~3KCzc8Jz@?tXaIyh2mwL0f&b8;c zPmxV5AprPD5>qkte_DP;S7ofGD0dbt3t#+ebkG^IGboTmHvG~Gu(@&-$8RAW4ZuNzBENczYxc1Tx|4&pUeJaz97Oe)3PKje z7AZ0bU`}`9e3in6(SeaS6Z-uVb&fUKrj7vP;fGRyC;%tk3v2_`7@m_^o`~E&lBR

2`KbW1=MQC`~95q0)WS^x2CJ@}E;EpHXimP0Ic@ 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

=|0UyFKHHqL%d^A=kOYG_%CWwI}* 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

Hd2K9v1;6Y;>Fc1ICgTf8it;5*u4E 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