Difference between revisions of "Widget:CodeExplorer"

From Coder Merlin
Line 40: Line 40:
     <div class="merlin-code-explorer-banner">
     <div class="merlin-code-explorer-banner">
         <img class="merlin-code-explorer-banner-merlin-icon" src="/wiki/resources/assets/MerlinRoundIcon.png" />
         <img class="merlin-code-explorer-banner-merlin-icon" src="/wiki/resources/assets/MerlinRoundIcon.png" />
         <span class="merlin-code-explorer-banner-text">CoderMerlin™ Code Explorer: <!--{$experienceID}--> (<!--{$exerciseID}-->)</span>      
         <span class="merlin-code-explorer-banner-text">CoderMerlin™ Code Explorer: <!--{$experienceID}--> (<!--{$exerciseID}-->)</span>
        <span class="merlin-code-explorer-banner-text"><!--{$codeExplorerGroupID|strip}--></span>     
        <span id="codeEditorStatusIndicator<!--{$exerciseID|validate:int}-->">🟢</span>     
         <div class="dropdown">
         <div class="dropdown">
             <button class="btn text-light dropdown-toggle py-0" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
             <button class="btn text-light dropdown-toggle py-0" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Line 58: Line 60:
         <textarea id="codeEditorTextArea<!--{$exerciseID|validate:int}-->"><!--{$initialCode}--></textarea>
         <textarea id="codeEditorTextArea<!--{$exerciseID|validate:int}-->"><!--{$initialCode}--></textarea>
         <script>
         <script>
            let codeEditor<!--{$exerciseID|validate:int}-->;
            // Preserved background colors
            let executeButtonBackgroundColor<!--{$exerciseID|validate:int}-->;
            let submitButtonBackgroundColor<!--{$exerciseID|validate:int}-->;
            let syncButtonBackgroundColor<!--{$exerciseID|validate:int}-->;
            let broadcastButtonBackgroundColor<!--{$exerciseID|validate:int}-->;
            // Broadcast
            let wasChangedSinceBroadcast<!--{$exerciseID|validate:int}--> = 1; // We start at one to ensure that even an empty file is broadcast
            function onChange<!--{$exerciseID|validate:int}-->(codeMirrorInstance, changeObject) {
                wasChangedSinceBroadcast<!--{$exerciseID|validate:int}-->++;
            }
             let currentLanguageData<!--{$exerciseID|validate:int}--> = "";
             let currentLanguageData<!--{$exerciseID|validate:int}--> = "";
             function setCurrentLanguage<!--{$exerciseID|validate:int}-->(language) {
             function setCurrentLanguage<!--{$exerciseID|validate:int}-->(language) {
Line 72: Line 88:
                 codeEditor<!--{$exerciseID|validate:int}-->.setOption("mode", mode);
                 codeEditor<!--{$exerciseID|validate:int}-->.setOption("mode", mode);
                 document.getElementById("codeExplorerIcon<!--{$exerciseID|validate:int}-->").src = iconURL;
                 document.getElementById("codeExplorerIcon<!--{$exerciseID|validate:int}-->").src = iconURL;
            }
            // Status indicator
            function setStatusIndicator<!--{$exerciseID|validate:int}-->(status) {
                let indicator = $("#codeEditorStatusIndicator<!--{$exerciseID|validate:int}-->").get(0);
                switch (status) {
                    case "red":
                        indicator.innerText = "🔴"
                        break;
                    case "yellow":
                        indicator.innerText = "🟡"
                        break;
                    case "green":
                        indicator.innerText = "🟢"
                        break;
                }
            }
            function syncToLiveTheater<!--{$exerciseID|validate:int}-->() {
                setStatusIndicator<!--{$exerciseID|validate:int}-->('yellow');
                // Clear output
                $("#codeEditorCompilerOutput<!--{$exerciseID|validate:int}-->").empty();
                $("#codeEditorExecutionOutput<!--{$exerciseID|validate:int}-->").empty();
                // Get results
                let username = "<!--{$userName}-->".toLowerCase();
                let sessionID =  "<!--{$sessionID}-->"
                let url = (subdomain() == "stg") ?
                    "https://language-server-stg.codermerlin.com/" :
                    "https://language-server.codermerlin.com/";
                url += "codeExplorerGroups/" + "<!--{$codeExplorerGroupID|strip}-->/";
                url += "experiences/" + "<!--{$experienceID}-->/" + "exercises/" + "<!--{$exerciseID}-->" + "/broadcast";
                let response = $.ajax({
                  type: "GET",
                  url,
                  headers: {
                      "username": username,
                      "sessionID": sessionID
                  },
                  dataType: "json",
                  error: function(xmlhttprequest, textstatus, message) {
                      setStatusIndicator<!--{$exerciseID|validate:int}-->('red');
                  },
                  timeout: 2500 // Should be less than repeat interval
                });
                     
                response.done(function(responseObject) {
                    let sourceLanguage = Object.keys(responseObject.sourceLanguage)[0];
                    let contents = responseObject.sourceFiles[0].contents;
                    // Set language
                    setCurrentLanguage<!--{$exerciseID|validate:int}-->(sourceLanguage);
                    // Set content (currently only first file)
                    codeEditor<!--{$exerciseID|validate:int}-->.setValue(contents);
                    // Set status
                    setStatusIndicator<!--{$exerciseID|validate:int}-->('green');
                });
            }
            function broadcastToLiveTheater<!--{$exerciseID|validate:int}-->() {
                // Only broadcast if changes have occurred since the previous broadcast
                if (wasChangedSinceBroadcast<!--{$exerciseID|validate:int}--> > 0) {
                    setStatusIndicator<!--{$exerciseID|validate:int}-->('yellow');
                    let username = "<!--{$userName}-->".toLowerCase();
                    let sessionID =  "<!--{$sessionID}-->"
                    let url = (subdomain() == "stg") ?
                        "https://language-server-stg.codermerlin.com/" :
                        "https://language-server.codermerlin.com/";
                    url += "codeExplorerGroups/" + "<!--{$codeExplorerGroupID|strip}-->/";
                    url += "experiences/" + "<!--{$experienceID}-->/" + "exercises/" + "<!--{$exerciseID}-->" + "/broadcast";
                    let sourceLanguage = currentLanguageData<!--{$exerciseID|validate:int}-->[1];
                    let sourceFileSuffix = currentLanguageData<!--{$exerciseID|validate:int}-->[2];
                    let requestObject = {
                        "sourceLanguage": {},
                        "sourceFiles": []
                    };
                    requestObject["sourceLanguage"][sourceLanguage] = {};
                    requestObject["sourceFiles"] = [{"path": "main" + "." + sourceFileSuffix,
                                                    "contents": codeEditor<!--{$exerciseID|validate:int}-->.getValue()}];
                    let requestString = JSON.stringify(requestObject);
                    let response = $.ajax({
                        type: "POST",
                        url,
                        headers: {
                            "username": username,
                            "sessionID": sessionID
                        },
                        data: requestString,
                        dataType: "json",
                        contentType : "application/json",
                        timeout: 2500, // Should be shorter than refresh interval
                        error: function(xmlhttprequest, textstatus, message) {
                            setStatusIndicator<!--{$exerciseID|validate:int}-->('red');
                        },
                        success: function(data) {
                            // Clear changes (only if successful)
                            wasChangedSinceBroadcast<!--{$exerciseID|validate:int}--> = 0;
                            setStatusIndicator<!--{$exerciseID|validate:int}-->('green');
                        }
                    });
                }
             }
             }


Line 104: Line 228:
             }
             }


            let codeEditor<!--{$exerciseID|validate:int}-->;
             window.addEventListener('load', (event) => {
             window.addEventListener('load', (event) => {
                 codeEditor<!--{$exerciseID|validate:int}--> = CodeMirror.fromTextArea(document.getElementById('codeEditorTextArea<!--{$exerciseID|validate:int}-->'),
                 codeEditor<!--{$exerciseID|validate:int}--> = CodeMirror.fromTextArea(document.getElementById('codeEditorTextArea<!--{$exerciseID|validate:int}-->'),
Line 114: Line 237:
                     }
                     }
                 );
                 );
                 // Set size
                 // Set size
                 codeEditor<!--{$exerciseID|validate:int}-->.setSize("<!--{$width}-->", "<!--{$height}-->");
                 codeEditor<!--{$exerciseID|validate:int}-->.setSize("<!--{$width}-->", "<!--{$height}-->");
                 // Set language
                 // Set language
                 setCurrentLanguage<!--{$exerciseID|validate:int}-->("<!--{$language}-->");
                 setCurrentLanguage<!--{$exerciseID|validate:int}-->("<!--{$language}-->");


                 // Determine if submit button is enabled, if not, disable
                 // Determine if CEG-ID is set
                 var isSubmitEnabled = '<!--{$codeExplorerGroupID|strip}-->'.length > 0;
                 const isCEGIDSet = '<!--{$codeExplorerGroupID|strip}-->'.length > 0;
                 if (isSubmitEnabled) {
 
                     var submitButton = $("#codeEditorSubmitButton<!--{$exerciseID|validate:int}-->");
                // Disable/enable buttons as appropriate
                     submitButton.prop("value", "Submit to <!--{$codeExplorerGroupID|strip}-->");              
                 if (isCEGIDSet) {
                     enableSubmitButton<!--{$exerciseID|validate:int}-->();
                    enableSyncButton<!--{$exerciseID|validate:int}-->();
                     enableBroadcastButton<!--{$exerciseID|validate:int}-->();
                 } else {
                 } else {
                     var submitButton = $("#codeEditorSubmitButton<!--{$exerciseID|validate:int}-->");
                     disableSubmitButton<!--{$exerciseID|validate:int}-->();
                     submitButton.attr("disabled", true);
                     disableSyncButton<!--{$exerciseID|validate:int}-->();
                     submitButton.css("background-color", "gray");
                     disableBroadcastButton<!--{$exerciseID|validate:int}-->();
                 }
                 }
                // Monitor events
                codeEditor<!--{$exerciseID|validate:int}-->.on("change", onChange<!--{$exerciseID|validate:int}-->);
   
   
                 // Attach handler to form                                                                                                                                                                                                                                                                   
                 // Attach handler to form                                                                                                                                                                                                                                                                   
Line 137: Line 268:


                     // Disable buttons
                     // Disable buttons
                     let executeButton = $("#codeEditorExecuteButton<!--{$exerciseID|validate:int}-->");
                     disableExecuteButton<!--{$exerciseID|validate:int}-->();
                     let submitButton = $("#codeEditorSubmitButton<!--{$exerciseID|validate:int}-->");
                     disableSubmitButton<!--{$exerciseID|validate:int}-->();
                     let executeButtonBackgroundColor = executeButton.css("background-color");
                     disableSyncButton<!--{$exerciseID|validate:int}-->();
                    let submitButtonBackgroundColor = submitButton.css("background-color");
                     disableBroadcastButton<!--{$exerciseID|validate:int}-->();
                    executeButton.attr("disabled", true);
                     executeButton.css("background-color", "gray");
                    submitButton.attr("disabled", true);
                    submitButton.css("background-color", "gray");


                     // Enable animation
                     // Enable animation
Line 205: Line 332:


                             // Re-enable buttons
                             // Re-enable buttons
                             executeButton.attr("disabled", false);
                             enableExecuteButton<!--{$exerciseID|validate:int}-->();
                             executeButton.css("background-color", executeButtonBackgroundColor);
                             if (isCEGIDSet) {
                            if (isSubmitEnabled) {
                                enableSubmitButton<!--{$exerciseID|validate:int}-->();
                                 submitButton.attr("disabled", false);
                                 enableSyncButton<!--{$exerciseID|validate:int}-->();
                                 submitButton.css("background-color", submitButtonBackgroundColor);
                                 enableBroadcastButton<!--{$exerciseID|validate:int}-->();
                             }     
                             }     


Line 268: Line 395:
                                        
                                        
                         // Re-enable buttons
                         // Re-enable buttons
                         executeButton.attr("disabled", false);
                         enableExecuteButton<!--{$exerciseID|validate:int}-->();
                         executeButton.css("background-color", executeButtonBackgroundColor);
                         if (isCEGIDSet) {
                        if (isSubmitEnabled) {
                            enableSubmitButton<!--{$exerciseID|validate:int}-->();
                             submitButton.attr("disabled", false);
                             enableSyncButton<!--{$exerciseID|validate:int}-->();
                             submitButton.css("background-color", submitButtonBackgroundColor);
                             enableBroadcastButton<!--{$exerciseID|validate:int}-->();
                         }
                         }


Line 279: Line 406:
                       });
                       });
                 });
                 });
                // Show Compilation
                 $("#codeEditorForm<!--{$exerciseID|validate:int}--> .merlin-code-explorer-show-compilation-button").click(function () {
                 $("#codeEditorForm<!--{$exerciseID|validate:int}--> .merlin-code-explorer-show-compilation-button").click(function () {
                     const $output = $(this).parent().siblings(".merlin-code-explorer-combined-output:first");
                     const $output = $(this).parent().siblings(".merlin-code-explorer-combined-output:first");
Line 289: Line 418:
                     }
                     }
                 });
                 });
                // Broadcast button
                function enableBroadcastButton<!--{$exerciseID|validate:int}-->() {
                    let broadcastButton = $("#codeEditorBroadcastButton<!--{$exerciseID|validate:int}-->");
                    broadcastButton.attr("disabled", false);
                    if (broadcastButtonBackgroundColor<!--{$exerciseID|validate:int}--> != undefined) {
                        broadcastButton.css("background-color", broadcastButtonBackgroundColor<!--{$exerciseID|validate:int}-->);
                    }
                }
                function disableBroadcastButton<!--{$exerciseID|validate:int}-->() {
                    let broadcastButton = $("#codeEditorBroadcastButton<!--{$exerciseID|validate:int}-->");
                    // Save original background color
                    if (broadcastButtonBackgroundColor<!--{$exerciseID|validate:int}--> == undefined) {
                        broadcastButtonBackgroundColor<!--{$exerciseID|validate:int}--> = broadcastButton.css("background-color");
                    }
                    broadcastButton.attr("disabled", true);
                    broadcastButton.css("background-color", "gray");
                }
                // Sync button
                function enableSyncButton<!--{$exerciseID|validate:int}-->() {
                    let syncButton = $("#codeEditorSyncButton<!--{$exerciseID|validate:int}-->");
                    syncButton.attr("disabled", false);
                    if (syncButtonBackgroundColor<!--{$exerciseID|validate:int}--> != undefined) {
                        syncButton.css("background-color", syncButtonBackgroundColor<!--{$exerciseID|validate:int}-->);
                    }
                }
                function disableSyncButton<!--{$exerciseID|validate:int}-->() {
                    let syncButton = $("#codeEditorSyncButton<!--{$exerciseID|validate:int}-->");
                    // Save original background color
                    if (syncButtonBackgroundColor<!--{$exerciseID|validate:int}--> == undefined) {
                        syncButtonBackgroundColor<!--{$exerciseID|validate:int}--> = syncButton.css("background-color");
                    }
                    syncButton.attr("disabled", true);
                    syncButton.css("background-color", "gray");
                }
                // Execute button
                function enableExecuteButton<!--{$exerciseID|validate:int}-->() {
                    let executeButton = $("#codeEditorExecuteButton<!--{$exerciseID|validate:int}-->");
                    executeButton.attr("disabled", false);
                    if (executeButtonBackgroundColor<!--{$exerciseID|validate:int}--> != undefined) {
                        executeButton.css("background-color", executeButtonBackgroundColor<!--{$exerciseID|validate:int}-->);
                    }
                }
                function disableExecuteButton<!--{$exerciseID|validate:int}-->() {
                    let executeButton = $("#codeEditorExecuteButton<!--{$exerciseID|validate:int}-->");
                    // Save original background color
                    if (executeButtonBackgroundColor<!--{$exerciseID|validate:int}--> == undefined) {
                        executeButtonBackgroundColor<!--{$exerciseID|validate:int}--> = executeButton.css("background-color");
                    }
                    executeButton.attr("disabled", true);
                    executeButton.css("background-color", "gray");
                }
                // Submit button
                function disableSubmitButton<!--{$exerciseID|validate:int}-->() {
                    let submitButton = $("#codeEditorSubmitButton<!--{$exerciseID|validate:int}-->");
                    // Save original background color
                    if (submitButtonBackgroundColor<!--{$exerciseID|validate:int}--> == undefined) {
                        submitButtonBackgroundColor<!--{$exerciseID|validate:int}--> = submitButton.css("background-color");
                    }
                    submitButton.attr("disabled", true);
                    submitButton.css("background-color", "gray");
                }
                function enableSubmitButton<!--{$exerciseID|validate:int}-->() {
                    let submitButton = $("#codeEditorSubmitButton<!--{$exerciseID|validate:int}-->");
                    submitButton.attr("disabled", false);
                    if (submitButtonBackgroundColor<!--{$exerciseID|validate:int}--> != undefined) {
                        submitButton.css("background-color", submitButtonBackgroundColor<!--{$exerciseID|validate:int}-->);
                    }
                    submitButton.prop("value", "Submit to <!--{$codeExplorerGroupID|strip}-->");               
                }
                // Live Theater button
                let isLiveTheaterSyncActive = false;
                let liveTheaterSyncIntervalId = 0;
                function turnOnLiveTheater<!--{$exerciseID|validate:int}-->() {
                    if (isLiveTheaterSyncActive) {
                        console.error("turnOnLiveTheater() invoked when already on.");
                        return;
                    }
                    let syncIcon = $("#codeEditorSyncIcon<!--{$exerciseID|validate:int}-->").get(0);
                    syncIcon.classList.add("spin");
                    syncToLiveTheater<!--{$exerciseID|validate:int}-->();
                    liveTheaterSyncIntervalId = setInterval(syncToLiveTheater<!--{$exerciseID|validate:int}-->, 3000);
                    isLiveTheaterSyncActive = true;
                    disableExecuteButton<!--{$exerciseID|validate:int}-->();
                    disableSubmitButton<!--{$exerciseID|validate:int}-->();
                    disableBroadcastButton<!--{$exerciseID|validate:int}-->();
                }
                function turnOffLiveTheater<!--{$exerciseID|validate:int}-->() {
                    if (!isLiveTheaterSyncActive) {
                        console.error("turnOffLiveTheater() invoked when already off.");
                        return;
                    }
                    let syncIcon = $("#codeEditorSyncIcon<!--{$exerciseID|validate:int}-->").get(0);
                    syncIcon.classList.remove("spin");
                    clearInterval(liveTheaterSyncIntervalId);
                    isLiveTheaterSyncActive = false;
                    enableExecuteButton<!--{$exerciseID|validate:int}-->();
                    enableSubmitButton<!--{$exerciseID|validate:int}-->();
                    enableBroadcastButton<!--{$exerciseID|validate:int}-->();
                    setStatusIndicator<!--{$exerciseID|validate:int}-->('green');
                }
                $("#codeEditorForm<!--{$exerciseID|validate:int}--> .merlin-code-explorer-live-theater-sync-button").click(function () {
                    if (isLiveTheaterSyncActive) {
                        turnOffLiveTheater<!--{$exerciseID|validate:int}-->();
                    } else {
                        turnOnLiveTheater<!--{$exerciseID|validate:int}-->();
                    }
                });
                // Live Broadcast button
                let isLiveTheaterBroadcastActive = false;
                let liveTheaterBroadcastIntervalId = 0;
                function turnOnLiveBroadcast<!--{$exerciseID|validate:int}-->() {
                    if (isLiveTheaterBroadcastActive) {
                        console.error("turnOnLiveBroadcast() invoked when already on.");
                        return;
                    }
                    let broadcastIcon = $("#codeEditorBroadcastIcon<!--{$exerciseID|validate:int}-->").get(0);
                    broadcastIcon.classList.add("pulse");
                    broadcastToLiveTheater<!--{$exerciseID|validate:int}-->();
                    liveTheaterBroadcastIntervalId = setInterval(broadcastToLiveTheater<!--{$exerciseID|validate:int}-->, 3000);
                    isLiveTheaterBroadcastActive = true;
                    disableExecuteButton<!--{$exerciseID|validate:int}-->();
                    disableSubmitButton<!--{$exerciseID|validate:int}-->();
                    disableSyncButton<!--{$exerciseID|validate:int}-->();
                }
                function turnOffLiveBroadcast<!--{$exerciseID|validate:int}-->() {
                    if (!isLiveTheaterBroadcastActive) {
                        console.error("turnOffLiveBroadcast() invoked when already off.");
                        return;
                    }
                    let broadcastIcon = $("#codeEditorBroadcastIcon<!--{$exerciseID|validate:int}-->").get(0);
                    broadcastIcon.classList.remove("pulse");
                    clearInterval(liveTheaterBroadcastIntervalId);
                    isLiveTheaterBroadcastActive = false;
                    enableExecuteButton<!--{$exerciseID|validate:int}-->();
                    enableSubmitButton<!--{$exerciseID|validate:int}-->();
                    enableSyncButton<!--{$exerciseID|validate:int}-->();
                    setStatusIndicator<!--{$exerciseID|validate:int}-->('green');
                }
                $("#codeEditorForm<!--{$exerciseID|validate:int}--> .merlin-code-explorer-live-theater-broadcast-button").click(function () {
                    if (isLiveTheaterBroadcastActive) {
                        turnOffLiveBroadcast<!--{$exerciseID|validate:int}-->();
                    } else {
                        turnOnLiveBroadcast<!--{$exerciseID|validate:int}-->();
                    }
                });
             });
             });
     </script>
     </script>
     </div>
     </div>
     <div id="codeEditorControlPanel<!--{$exerciseID|validate:int}-->" class="merlin-code-explorer-control-panel">
     <div id="codeEditorControlPanel<!--{$exerciseID|validate:int}-->" class="merlin-code-explorer-control-panel">
         <input id="codeEditorExecuteButton<!--{$exerciseID|validate:int}-->" class="merlin-code-explorer-execute-button" onclick="this.form.submitter = 'execute';" type="submit" value="Run" />
         <input id="codeEditorExecuteButton<!--{$exerciseID|validate:int}-->" class="merlin-code-explorer-button merlin-code-explorer-execute-button" onclick="this.form.submitter = 'execute';" type="submit" value="Run" />
         <input id="codeEditorSubmitButton<!--{$exerciseID|validate:int}-->" class="merlin-code-explorer-submit-button" onclick="this.form.submitter = 'submit';" type="submit" value="Submit"/>
         <input id="codeEditorSubmitButton<!--{$exerciseID|validate:int}-->" class="merlin-code-explorer-button merlin-code-explorer-submit-button" onclick="this.form.submitter = 'submit';" type="submit" value="Submit"/>
         <button class="merlin-code-explorer-show-compilation-button" type="button">Hide Compilation Output</button>
         <button class="merlin-code-explorer-button merlin-code-explorer-show-compilation-button" type="button">Hide Compilation Output</button>
        <button id="codeEditorSyncButton<!--{$exerciseID|validate:int}-->" class="merlin-code-explorer-button merlin-code-explorer-live-theater-sync-button" type="button">Sync&nbsp;&nbsp;<div id="codeEditorSyncIcon<!--{$exerciseID|validate:int}-->" class="merlin-code-explorer-live-theater-sync-button-icon"><i class="fa fa-sync"></i></div></button>
        <button id="codeEditorBroadcastButton<!--{$exerciseID|validate:int}-->" class="merlin-code-explorer-button merlin-code-explorer-live-theater-broadcast-button" type="button">Broadcast&nbsp;&nbsp;<div id="codeEditorBroadcastIcon<!--{$exerciseID|validate:int}-->" class="merlin-code-explorer-live-theater-broadcast-button-icon"><i class="fa fa-wifi"></i></div></button>
     </div>
     </div>
     <div id="codeEditorCompilerOutput<!--{$exerciseID|validate:int}-->" class="merlin-code-explorer-combined-output"></div>
     <div id="codeEditorCompilerOutput<!--{$exerciseID|validate:int}-->" class="merlin-code-explorer-combined-output"></div>

Revision as of 18:11, 2 February 2023

Parameters:

userName
string: The current user's username
sessionID
string: The ID of the current user's session
experienceID
string: The experienceID of the page from which the widget is invoked
codeExplorerGroupID
string: The code explorer group. If empty, the submit button will be disabled.
exerciseID
integer: exercise id for editor, must be unique per page
width
integer|string: percentage (as string, e.g. "100%" or integer size in pixels), null for no change (full width)
height
integer|string: percentage (as string, e.g. "100%" or integer size in pixels), null for no change (~10 lines)
lineNumbers
boolean: true to display line numbers
theme
string: name of theme (which must be loaded via css)
readOnly
boolean: true if editing should be disabled
language
string: language for compiling and highlighting (which must be loaded via js)
initialCode
string: initial code to place in editor

Example:

{{#widget:CodeExplorer
|userName=john-williams
|sessionID=qh0ubrrme911kcg7db0i0ec6lct94h7f
|experienceID=W1020.23
|codeExplorerGroupID=WTRS-8527
|exerciseID=10
|width=null
|height=null
|lineNumbers=true
|theme=vibrant-ink
|readOnly=false
|language=swift
|initialCode=func sayHello() {
    print("Hello, World!")
}
}}