From f50101db8a717ae97e736d8e0a2df99fd861fdd0 Mon Sep 17 00:00:00 2001
From: Alezia Kurdis <60075796+AleziaKurdis@users.noreply.github.com>
Date: Sat, 16 Mar 2024 14:04:24 -0400
Subject: [PATCH 1/2] Add "Import" Tab

Add "Import" Tab
---
 scripts/system/create/qml/EditTabView.qml     | 19 +++++++++++++++++
 .../system/create/qml/EditToolsTabView.qml    | 21 ++++++++++++++++++-
 2 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/scripts/system/create/qml/EditTabView.qml b/scripts/system/create/qml/EditTabView.qml
index 96e66c109e..2db23ec659 100644
--- a/scripts/system/create/qml/EditTabView.qml
+++ b/scripts/system/create/qml/EditTabView.qml
@@ -301,6 +301,22 @@ TabBar {
         }
     }
 
+    EditTabButton {
+        title: "IMPORT"
+        active: true
+        enabled: true
+        property string originalUrl: ""
+
+        property Component visualItem: Component {
+            WebView {
+                id: advancedImportWebView
+                url: Qt.resolvedUrl("../importEntities/html/importEntities.html")
+                enabled: true
+                blurOnCtrlShift: false
+            }
+        }
+    }
+
     function fromScript(message) {
         switch (message.method) {
             case 'selectTab':
@@ -333,6 +349,9 @@ TabBar {
                 case 'grid':
                     editTabView.currentIndex = 3;
                     break;
+                case 'import':
+                    editTabView.currentIndex = 4;
+                    break;
                 default:
                     console.warn('Attempt to switch to invalid tab:', id);
             }
diff --git a/scripts/system/create/qml/EditToolsTabView.qml b/scripts/system/create/qml/EditToolsTabView.qml
index 998c3a3aac..1000724458 100644
--- a/scripts/system/create/qml/EditToolsTabView.qml
+++ b/scripts/system/create/qml/EditToolsTabView.qml
@@ -291,6 +291,22 @@ TabBar {
         }
     }
 
+    EditTabButton {
+        title: "IMPORT"
+        active: true
+        enabled: true
+        property string originalUrl: ""
+
+        property Component visualItem: Component {
+            WebView {
+                id: advancedImportWebView
+                url: Qt.resolvedUrl("../importEntities/html/importEntities.html")
+                enabled: true
+                blurOnCtrlShift: false
+            }
+        }
+    }
+
     function fromScript(message) {
         switch (message.method) {
             case 'selectTab':
@@ -304,7 +320,7 @@ TabBar {
     // Changes the current tab based on tab index or title as input
     function selectTab(id) {
         if (typeof id === 'number') {
-            if (id >= tabIndex.create && id <= tabIndex.grid) {
+            if (id >= tabIndex.create && id <= tabIndex.import) {
                 editTabView.currentIndex = id;
             } else {
                 console.warn('Attempt to switch to invalid tab:', id);
@@ -320,6 +336,9 @@ TabBar {
                 case 'grid':
                     editTabView.currentIndex = tabIndex.grid;
                     break;
+                case 'import':
+                    editTabView.currentIndex = tabIndex.import;
+                    break;
                 default:
                     console.warn('Attempt to switch to invalid tab:', id);
             }

From a509f84a42013e0135db3f14452085fea97e98fc Mon Sep 17 00:00:00 2001
From: Alezia Kurdis <60075796+AleziaKurdis@users.noreply.github.com>
Date: Sat, 16 Mar 2024 14:05:14 -0400
Subject: [PATCH 2/2] Add "Import" Tab

Add "Import" Tab
---
 scripts/system/create/edit.js                 |  81 ++++++-
 .../html/css/importEntities.css               | 160 +++++++++++++
 .../importEntities/html/importEntities.html   |  77 +++++++
 .../html/js/importEntitiesUi.js               | 217 ++++++++++++++++++
 4 files changed, 533 insertions(+), 2 deletions(-)
 create mode 100644 scripts/system/create/importEntities/html/css/importEntities.css
 create mode 100644 scripts/system/create/importEntities/html/importEntities.html
 create mode 100644 scripts/system/create/importEntities/html/js/importEntitiesUi.js

diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js
index 5d3e924ccf..1d7f4fc05e 100644
--- a/scripts/system/create/edit.js
+++ b/scripts/system/create/edit.js
@@ -121,6 +121,16 @@
 
     var copiedPosition;
     var copiedRotation;
+    var importUiPersistedData = {
+        "elJsonUrl": "",
+        "elImportAtAvatar": true,
+        "elImportAtSpecificPosition": false,
+        "elPositionX": 0,
+        "elPositionY": 0,
+        "elPositionZ": 0,
+        "elEntityHostTypeDomain": true,
+        "elEntityHostTypeAvatar": false
+    };
 
     var cameraManager = new CameraManager();
 
@@ -2009,7 +2019,8 @@
         return position;
     }
 
-    function importSVO(importURL) {
+    function importSVO(importURL, importEntityHostType) {
+        importEntityHostType = importEntityHostType || "domain";
         if (!Entities.canRez() && !Entities.canRezTmp()) {
             Window.notifyEditError(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG);
             return;
@@ -2032,7 +2043,7 @@
                 position = createApp.getPositionToCreateEntity(Clipboard.getClipboardContentsLargestDimension() / 2);
             }
             if (position !== null && position !== undefined) {
-                var pastedEntityIDs = Clipboard.pasteEntities(position);
+                var pastedEntityIDs = Clipboard.pasteEntities(position, importEntityHostType);
                 if (!isLargeImport) {
                     // The first entity in Clipboard gets the specified position with the rest being relative to it. Therefore, move
                     // entities after they're imported so that they're all the correct distance in front of and with geometric mean
@@ -2792,6 +2803,72 @@
                     type: 'zoneListRequest',
                     zones: getExistingZoneList()
                 });
+            } else if (data.type === "importUiBrowse") {
+                let fileToImport = Window.browse("Select .json to Import", "", "*.json");
+                if (fileToImport !== null) {
+                     emitScriptEvent({
+                        type: 'importUi_SELECTED_FILE',
+                        file: fileToImport
+                    });
+                } else {
+                    audioFeedback.rejection();
+                }
+            } else if (data.type === "importUiImport") {
+                if ((data.entityHostType === "domain" && Entities.canAdjustLocks() && Entities.canRez()) || 
+                    (data.entityHostType === "avatar" && Entities.canRezAvatarEntities())) {
+                    if (data.positioningMode === "avatar") {
+                        importSVO(data.jsonURL, data.entityHostType);
+                    } else {
+                        if (Clipboard.importEntities(data.jsonURL)) {
+                            let importedPastedEntities = Clipboard.pasteEntities(data.position, data.entityHostType);
+                            if (importedPastedEntities.length === 0) {
+                                emitScriptEvent({
+                                    type: 'importUi_IMPORT_ERROR',
+                                    reason: "No Entity has been imported."
+                                });
+                            } else {
+                                if (isActive) {
+                                    selectionManager.setSelections(importedPastedEntities, this);
+                                }
+                                emitScriptEvent({type: 'importUi_IMPORT_CONFIRMATION'});
+                            }
+                        } else {
+                            emitScriptEvent({
+                                type: 'importUi_IMPORT_ERROR',
+                                reason: "Import Entities has failed."
+                            });
+                        }
+                    }
+                } else {
+                    emitScriptEvent({
+                        type: 'importUi_IMPORT_ERROR',
+                        reason: "You don't have permission to create in this domain."
+                    });
+                }
+            } else if (data.type === "importUiGoBack") {
+                if (location.canGoBack()) {
+                    location.goBack();
+                } else {
+                    audioFeedback.rejection();
+                }
+            } else if (data.type === "importUiGoTutorial") {
+                Window.location = "file:///~/serverless/tutorial.json";
+            } else if (data.type === "importUiGetCopiedPosition") {
+                if (copiedPosition !== undefined) {
+                    emitScriptEvent({
+                        type: 'importUi_POSITION_TO_PASTE',
+                        position: copiedPosition
+                    });
+                } else {
+                    audioFeedback.rejection();
+                }
+            } else if (data.type === "importUiPersistData") {
+                importUiPersistedData = data.importUiPersistedData;
+            } else if (data.type === "importUiGetPersistData") {
+                emitScriptEvent({
+                    type: 'importUi_LOAD_DATA',
+                    importUiPersistedData: importUiPersistedData
+                });
             }
         };
 
diff --git a/scripts/system/create/importEntities/html/css/importEntities.css b/scripts/system/create/importEntities/html/css/importEntities.css
new file mode 100644
index 0000000000..61c75dabb3
--- /dev/null
+++ b/scripts/system/create/importEntities/html/css/importEntities.css
@@ -0,0 +1,160 @@
+/*
+//  importEntities.css
+//
+//  Created by Alezia Kurdis on March 13th, 2024
+//  Copyright 2024 Overte e.V.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+*/
+
+@font-face {
+    font-family: FiraSans-SemiBold;
+    src: url(../../../../../../resources/fonts/FiraSans-SemiBold.ttf), /* Windows production */
+         url(../../../../../../fonts/FiraSans-SemiBold.ttf); /* OSX production */
+}
+
+@font-face {
+    font-family: FiraSans-Regular;
+    src: url(../../../../../../resources/fonts/FiraSans-Regular.ttf), /* Windows production */
+         url(../../../../../../fonts/FiraSans-Regular.ttf); /* OSX production */
+}
+
+@font-face {
+    font-family: Raleway-Bold;
+    src: url(../../../../../../resources/fonts/Raleway-Bold.ttf), /* Windows production */
+         url(../../../../../../fonts/Raleway-Bold.ttf); /* OSX production */
+}
+
+html {
+    width: 100%;
+    height: 100%;
+}
+input[type="text"] {
+    font-family: FiraSans-SemiBold;
+    color: #BBBBBB;
+    background-color: #222222;
+    border: 0;
+    padding: 4px;
+    margin: 1px;
+}
+
+input[type="number"] {
+    font-family: FiraSans-SemiBold;
+    color: #BBBBBB;
+    background-color: #222222;
+    border: 0;
+    padding: 4px;
+    margin: 1px;
+    width: 90px;
+}
+
+h2 {
+    font-size: 18px;
+    color: #FFFFFF;
+}
+body {
+    background: #404040;
+    font-family: FiraSans-Regular;
+    font-size: 14px;
+    color: #BBBBBB;
+    text-decoration: none;
+    font-style: normal;
+    font-variant: normal;
+    text-transform: none;
+}
+
+#importAtSpecificPositionContainer {
+    display: none;
+    width: 100%;
+}
+
+#jsonUrl {
+    width:90%;
+}
+#browseBtn {
+    font-family: FiraSans-SemiBold;
+}
+#browseBtn:hover {
+    
+}
+
+label {
+    font-family: FiraSans-SemiBold;
+    color: #DDDDDD;
+}
+font.red {
+    font-family: FiraSans-SemiBold;
+    color: #e83333;
+}
+font.green {
+    font-family: FiraSans-SemiBold;
+    color: #0db518;
+}
+font.blue {
+    font-family: FiraSans-SemiBold;
+    color: #447ef2;
+}
+#importBtn {
+    color: #ffffff;
+    background-color: #1080b8;
+    background: linear-gradient(#00b4ef 20%, #1080b8 100%);
+    font-family: Raleway-Bold;
+    font-size: 13px;
+    text-transform: uppercase;
+    vertical-align: top;
+    height: 28px;
+    min-width: 70px;
+    padding: 0 18px;
+    margin: 3px 3px 12px 3px;
+    border-radius: 5px;
+    border: 0;
+    cursor: pointer;
+}
+#importBtn:hover {
+    background: linear-gradient(#00b4ef, #00b4ef);
+    border: none;
+}
+input:focus {
+    outline: none;
+    color: #FFFFFF;
+}
+button:focus {
+    outline: none;
+}
+div.explicative {
+    width: 96%;
+    padding: 7px;
+    font-family: FiraSans-SemiBold;
+    font-size: 12px;
+    text-decoration: none;
+    color: #BBBBBB;
+}
+button.black {
+    font-family: Raleway-Bold;
+    font-size: 10px;
+    text-transform: uppercase;
+    vertical-align: top;
+    height: 18px;
+    min-width: 60px;
+    padding: 0 14px;
+    margin: 5px;
+    border-radius: 4px;
+    border: none;
+    color: #fff;
+    background-color: #000;
+    background: linear-gradient(#343434 20%, #000 100%);
+    cursor: pointer;
+}
+button.black:hover {
+    background: linear-gradient(#000, #000);
+    border: none;
+}
+#messageContainer {
+    font-family: FiraSans-SemiBold;
+    width: 100%;
+}
+#testContainer {
+    border: 1px solid #AAAAAA;
+    padding: 0px;
+}
diff --git a/scripts/system/create/importEntities/html/importEntities.html b/scripts/system/create/importEntities/html/importEntities.html
new file mode 100644
index 0000000000..a1550a642e
--- /dev/null
+++ b/scripts/system/create/importEntities/html/importEntities.html
@@ -0,0 +1,77 @@
+<!--
+//  importEntities.html
+//
+//  Created by Alezia Kurdis on March 13th, 2024.
+//  Copyright 2024 Overte e.V.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+-->
+<html>
+    <head>
+        <title>Import Entities</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+        <link rel="stylesheet" type="text/css" href="css/importEntities.css">
+        <script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
+        <script type="text/javascript" src="js/importEntitiesUi.js"></script>
+    </head>
+    <body onload="loaded();" >
+        <h2>Import Entities (.json)</h2>
+        <font class="red">* </font>URL/File (.json):<br>
+        <input type="text" id = "jsonUrl">&nbsp;<button id="browseBtn">...</button><br>
+        <br>
+        <table style="width: 96%;">
+            <tr style="vertical-align: top;">
+                <td style="width: 40%;">
+                    Position:<br>
+                    &nbsp;&nbsp;&nbsp;<input type="radio" name="importAtPosition" id="importAtAvatar" value="avatar" checked><label for="importAtAvatar">&nbsp;In front of your avatar</label><br>
+                    &nbsp;&nbsp;&nbsp;<input type="radio" name="importAtPosition" id="importAtSpecificPosition" value="position"><label for="importAtSpecificPosition">&nbsp;At a specified Position</label><br>
+                </td>
+                <td style="width: 60%;">
+                    <div id="importAtSpecificPositionContainer">
+                        <font class="red">X</font> <input type="number" size="6" id = "positionX" value = "0">&nbsp;&nbsp;&nbsp;
+                        <font class="green">Y</font> <input type="number" size="6" id = "positionY" value = "0">&nbsp;&nbsp;&nbsp;
+                        <font class="blue">Z</font> <input type="number" size="6" id = "positionZ" value = "0"><br>
+                        <button id="pastePositionBtn" class="black">Paste Position</button><br>
+                        <div class="explicative">
+                            Note: If you import a "serverless" json file, such data include positions. 
+                            It this case, the "Position" will act as an offset.
+                        </div>
+                    </div>
+                </td>
+            </tr>
+        </table>
+        <br>
+        <table style="width: 96%;">
+            <tr style="vertical-align: top;">
+                <td style="width: 30%;">
+                    Entity Host Type:<br>
+                    &nbsp;&nbsp;&nbsp;<input type="radio" name="entityHostType" id="entityHostTypeDomain" value="domain" checked><label for="entityHostTypeDomain">&nbsp;Domain Entities</label><br>
+                    &nbsp;&nbsp;&nbsp;<input type="radio" name="entityHostType" id="entityHostTypeAvatar" value="avatar"><label for="entityHostTypeAvatar">&nbsp;Avatar Entities</label><br>
+                </td>
+                <td style="width: 70%;">
+                    <div id="messageContainer"></div>
+                </td>
+            </tr>
+        </table>
+        <div style="text-align: right; width:96%;"><button id="importBtn">IMPORT</button></div>
+        <div id="testContainer">
+            <table style="width: 96%;">
+                <tr style="vertical-align: top;">
+                    <td style="width: 60%;">
+                        <div class="explicative">
+                        For large import, it can be wise to test it in a serverless environment before doing it in your real domain.
+                        </div>
+                    </td>
+                    <td style="width: 40%;">
+                        <div style="text-align: center; width:96%;">
+                        <button id="backBtn" class="black">&#11164; Back</button>
+                        &nbsp;&nbsp;&nbsp;
+                        <button id="tpTutorialBtn" class="black">Go test &#11166;</button>
+                        </div>
+                    </td>
+                </tr>
+            </table>
+        </div>
+    </body>
+</html>
diff --git a/scripts/system/create/importEntities/html/js/importEntitiesUi.js b/scripts/system/create/importEntities/html/js/importEntitiesUi.js
new file mode 100644
index 0000000000..6e80c7f173
--- /dev/null
+++ b/scripts/system/create/importEntities/html/js/importEntitiesUi.js
@@ -0,0 +1,217 @@
+//  importEntitiesUi.js
+//
+//  Created by Alezia Kurdis on March 13th, 2024
+//  Copyright 2024 Overte e.V.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+
+let elJsonUrl;
+let elBrowseBtn;
+let elImportAtAvatar;
+let elImportAtSpecificPosition;
+let elImportAtSpecificPositionContainer;
+let elPositionX;
+let elPositionY;
+let elPositionZ;
+let elEntityHostTypeDomain;
+let elEntityHostTypeAvatar;
+let elMessageContainer;
+let elImportBtn;
+let elBackBtn;
+let elTpTutorialBtn;
+let elPastePositionBtn;
+
+let lockUntil;
+
+const LOCK_BTN_DELAY = 2000; //2 sec
+
+function loaded() {
+    lockUntil = 0;
+    
+    elJsonUrl = document.getElementById("jsonUrl");
+    elBrowseBtn = document.getElementById("browseBtn");
+    elImportAtAvatar = document.getElementById("importAtAvatar");
+    elImportAtSpecificPosition = document.getElementById("importAtSpecificPosition");
+    elImportAtSpecificPositionContainer = document.getElementById("importAtSpecificPositionContainer");
+    elPositionX = document.getElementById("positionX");
+    elPositionY = document.getElementById("positionY");
+    elPositionZ = document.getElementById("positionZ");
+    elEntityHostTypeDomain = document.getElementById("entityHostTypeDomain");
+    elEntityHostTypeAvatar = document.getElementById("entityHostTypeAvatar");
+    elMessageContainer = document.getElementById("messageContainer");
+    elImportBtn = document.getElementById("importBtn");
+    elBackBtn = document.getElementById("backBtn");
+    elTpTutorialBtn = document.getElementById("tpTutorialBtn");
+    elPastePositionBtn = document.getElementById("pastePositionBtn");
+    
+    elJsonUrl.oninput = function() {
+        persistData();
+    }
+    
+    elPositionX.oninput = function() {
+        persistData();
+    }
+    
+    elPositionY.oninput = function() {
+        persistData();
+    }
+    
+    elPositionZ.oninput = function() {
+        persistData();
+    }
+
+    elEntityHostTypeDomain.onclick = function() {
+        persistData();
+    }
+    
+    elEntityHostTypeAvatar.onclick = function() {
+        persistData();
+    }
+    
+    elBrowseBtn.onclick = function() {
+        const d = new Date();
+        let time = d.getTime();
+        if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) {
+            EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiBrowse" }));
+            lockUntil = d.getTime() + LOCK_BTN_DELAY;
+        }
+    };
+    
+    elImportAtAvatar.onclick = function() {
+        elImportAtSpecificPositionContainer.style.display = "None";
+        persistData();
+    };
+
+    elImportAtSpecificPosition.onclick = function() {
+        elImportAtSpecificPositionContainer.style.display = "Block";
+        persistData();
+    };
+    
+    elImportBtn.onclick = function() {
+        const d = new Date();
+        let time = d.getTime();
+        if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) {
+            importJsonToWorld();
+            lockUntil = d.getTime() + LOCK_BTN_DELAY;
+        }
+    };
+    
+    elBackBtn.onclick = function() {
+        const d = new Date();
+        let time = d.getTime();
+        if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) {
+            EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiGoBack" }));
+            lockUntil = d.getTime() + LOCK_BTN_DELAY;
+        }
+    };
+    
+    elTpTutorialBtn.onclick = function() {
+        const d = new Date();
+        let time = d.getTime();
+        if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) {
+            EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiGoTutorial" }));
+            lockUntil = d.getTime() + LOCK_BTN_DELAY;
+        }
+    };
+    
+    elPastePositionBtn.onclick = function() {
+        const d = new Date();
+        let time = d.getTime();
+        if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) {
+            EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiGetCopiedPosition" }));
+            lockUntil = d.getTime() + LOCK_BTN_DELAY;
+        }
+    };
+    
+    EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiGetPersistData" }));
+}
+
+function persistData() {
+    let message = {
+        "type": "importUiPersistData",
+        "importUiPersistedData": {
+            "elJsonUrl": elJsonUrl.value,
+            "elImportAtAvatar": elImportAtAvatar.checked,
+            "elImportAtSpecificPosition": elImportAtSpecificPosition.checked,
+            "elPositionX": elPositionX.value,
+            "elPositionY": elPositionY.value,
+            "elPositionZ": elPositionZ.value,
+            "elEntityHostTypeDomain": elEntityHostTypeDomain.checked,
+            "elEntityHostTypeAvatar": elEntityHostTypeAvatar.checked
+        }
+    };
+    EventBridge.emitWebEvent(JSON.stringify(message));
+}
+
+function loadDataInUi(importUiPersistedData) {
+    elJsonUrl.value = importUiPersistedData.elJsonUrl;
+    elImportAtAvatar.checked = importUiPersistedData.elImportAtAvatar;
+    elImportAtSpecificPosition.checked = importUiPersistedData.elImportAtSpecificPosition;
+    elPositionX.value = importUiPersistedData.elPositionX;
+    elPositionY.value = importUiPersistedData.elPositionY;
+    elPositionZ.value = importUiPersistedData.elPositionZ;
+    elEntityHostTypeDomain.checked = importUiPersistedData.elEntityHostTypeDomain;
+    elEntityHostTypeAvatar.checked = importUiPersistedData.elEntityHostTypeAvatar;
+    if (elImportAtSpecificPosition.checked) {
+        elImportAtSpecificPositionContainer.style.display = "Block";
+    }
+}
+
+function importJsonToWorld() {
+    elMessageContainer.innerHTML = "";
+
+    if (elJsonUrl.value === "") {
+        elMessageContainer.innerHTML = "<div style = 'padding: 10px; color: #000000; background-color: #ff7700;'>ERROR: 'URL/File (.json)' is required.</div>";
+        return;
+    }
+    
+    let positioningMode = getRadioValue("importAtPosition");
+    let entityHostType = getRadioValue("entityHostType");
+
+    if (positioningMode === "position" && (elPositionX.value === "" || elPositionY.value === "" || elPositionZ.value === "")) {
+        elMessageContainer.innerHTML = "<div style = 'padding: 10px; color: #000000; background-color: #ff7700;'>ERROR: 'Position' is required.</div>";
+        return;
+    }
+    let position = {"x": parseFloat(elPositionX.value), "y": parseFloat(elPositionY.value), "z": parseFloat(elPositionZ.value)};
+    let message = {
+        "type": "importUiImport",
+        "jsonURL": elJsonUrl.value,
+        "positioningMode": positioningMode,
+        "position": position,
+        "entityHostType": entityHostType
+    };
+    EventBridge.emitWebEvent(JSON.stringify(message));
+}
+
+function getRadioValue(objectName) {
+    let radios = document.getElementsByName(objectName);
+    let i; 
+    let selectedValue = "";
+    for (i = 0; i < radios.length; i++) {
+        if (radios[i].checked) {
+            selectedValue = radios[i].value;
+            break;
+        }
+    }
+    return selectedValue;
+}
+
+EventBridge.scriptEventReceived.connect(function(message){
+    let messageObj = JSON.parse(message);
+    if (messageObj.type === "importUi_IMPORT_CONFIRMATION") {
+        elMessageContainer.innerHTML = "<div style = 'padding: 10px; color: #000000; background-color: #00ff00;'>IMPORT SUCCESSFUL.</div>";
+    } else if (messageObj.type === "importUi_IMPORT_ERROR") {
+        elMessageContainer.innerHTML = "<div style = 'padding: 10px; color: #FFFFFF; background-color: #ff0000;'>IMPORT ERROR: " + messageObj.reason + "</div>";
+    } else if (messageObj.type === "importUi_SELECTED_FILE") {
+        elJsonUrl.value = messageObj.file;
+        persistData();
+    } else if (messageObj.type === "importUi_POSITION_TO_PASTE") {
+        elPositionX.value = messageObj.position.x;
+        elPositionY.value = messageObj.position.y;
+        elPositionZ.value = messageObj.position.z;
+        persistData();
+    } else if (messageObj.type === "importUi_LOAD_DATA") {
+        loadDataInUi(messageObj.importUiPersistedData);
+    }
+});