Difference between revisions of "Widget:CodeExplorer"

From Coder Merlin
Line 46: Line 46:
         <textarea id="codeEditorTextArea<!--{$exerciseID|validate:int}-->"><!--{$initialCode}--></textarea>
         <textarea id="codeEditorTextArea<!--{$exerciseID|validate:int}-->"><!--{$initialCode}--></textarea>
         <script>
         <script>
             var codeEditor<!--{$exerciseID|validate:int}-->;
             function markupWarningsAndErrorsHTML(string) {
                let html = "";
                if (typeof string == "string") {
                    let lines = string.split("<br/>");
                    for (const line of lines) {
                        let decoration = "merlin-code-explorer-combined-output-default";
                        if (line.match(/warning:/)) {
                            decoration = "merlin-code-explorer-combined-output-warning";
                        }
                        if (line.match(/error:/)) {
                            decoration = "merlin-code-explorer-combined-output-error";
                        }
                        html += "<span class='" + decoration + "'>" + line + "</span><br/>";
                    }
                }
                return html;
            }
 
            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 78: Line 96:


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


                     // Clear output
                     // Clear output
                     $("#codeEditorCombinedOutput<!--{$exerciseID|validate:int}-->").empty();
                     $("#codeEditorCompilerOutput<!--{$exerciseID|validate:int}-->").empty();
                    $("#codeEditorExecutionOutput<!--{$exerciseID|validate:int}-->").empty();


                     // Submit form via POST   
                     // Submit form via POST   
                     var username = "<!--{$userName}-->".toLowerCase();
                     let username = "<!--{$userName}-->".toLowerCase();
                     var sessionID =  "<!--{$sessionID}-->"
                     let sessionID =  "<!--{$sessionID}-->"
                     var url = (subdomain() == "stg") ?  
                     let url = (subdomain() == "stg") ?  
                         "https://language-server-stg.codermerlin.com/" :
                         "https://language-server-stg.codermerlin.com/" :
                         "https://language-server.codermerlin.com/";  
                         "https://language-server.codermerlin.com/";  
Line 104: Line 123:
                             break;
                             break;
                     }
                     }
                    console.log(url);
   
   
                     var response = $.ajax({
                     let requestObject = {
                        "sourceLanguage": {"swift": {}},
                        "sourceFiles": [{"path": "main.swift", "contents": codeEditor<!--{$exerciseID|validate:int}-->.getValue()}],
                        "executionMode": {"compileAndExecute": {}}
                    };
                    let requestString = JSON.stringify(requestObject);
 
                    let response = $.ajax({
                         type: "POST",
                         type: "POST",
                         url,  
                         url,  
Line 113: Line 138:
                             "sessionID": sessionID
                             "sessionID": sessionID
                         },
                         },
                         data: {
                         data: requestString,
                            "sourceCodeLanguage": "swift",
                        dataType: "json",
                            "sourceCode": codeEditor<!--{$exerciseID|validate:int}-->.getValue()
                        contentType : "application/json",
                        },
                        timeout: 6000,
                        timeout: 6000,
                        error: function(jqXHR, textStatus, errorThrown) {
                        error: function(jqXHR, textStatus, errorThrown) {
                             // Display error
                             // Display error
                             $("#codeEditorCombinedOutput<!--{$exerciseID|validate:int}-->").append("<span class='merlin-code-explorer-combined-output-error'>" +  
                             $("#codeEditorCompilerOutput<!--{$exerciseID|validate:int}-->").append("<span class='merlin-code-explorer-combined-output-error'>" +  
                                 "Failed to complete operation." +  
                                 "Internal error: " + textStatus + "<br/>" + errorThrown + "<br/>" +  
                                 "</span><br/>");
                                 "</span><br/>");
                            console.log(textStatus + " " + errorThrown);
 
                             // Re-enable buttons
                             // Re-enable buttons
                             executeButton.attr("disabled", false);
                             executeButton.attr("disabled", false);
Line 135: Line 159:


                     // Collect response data                                                                                                                                                                                                                                                               
                     // Collect response data                                                                                                                                                                                                                                                               
                     response.done(function(data) {
                     response.done(function(responseObject) {
                         var executionCodeResponse = JSON.parse(data);
                         switch (event.target.submitter) {
                        var standardError = consoleToHTML(executionCodeResponse.standardError);
                            case "execute":
                        var standardOutput = consoleToHTML(executionCodeResponse.standardOutput);
                                let compilationStatus = responseObject.compilationStatus;
                        var timedOut = executionCodeResponse.timedOut;
                                let compilationOutput = compilationStatus.standardOutput;
                                let compilationError = compilationStatus.standardError;
                                if (compilationStatus.timedOut) {
                                    compilationError += "error: timed out\n";
                                }


                        // If timed out, append line to standardError
                                 $("#codeEditorCompilerOutput<!--{$exerciseID|validate:int}-->").
                        if (timedOut) {
                                    append(markupWarningsAndErrorsHTML(consoleToHTML(compilationError)));
                            standardError += "error: timed out<br/>";
                                $("#codeEditorCompilerOutput<!--{$exerciseID|validate:int}-->").
                        };
                                    append(markupWarningsAndErrorsHTML(consoleToHTML(compilationOutput)));
 
                        var standardErrorLines = standardError.split("<br/>");
                        var errorClass = "merlin-code-explorer-combined-output-error";
                        for (const errorLine of standardErrorLines) {
                            if (errorLine.match(/warning:/)) {
                                errorClass = "merlin-code-explorer-combined-output-warning";
                            };
                            if (errorLine.match(/error:/)) {
                                 errorClass = "merlin-code-explorer-combined-output-error";
                            };
                            $("#codeEditorCombinedOutput<!--{$exerciseID|validate:int}-->").append("<span class='" + errorClass + "'>" + errorLine + "</span><br/>");
                        };
                        $("#codeEditorCombinedOutput<!--{$exerciseID|validate:int}-->").append(standardOutput);


                                let executionStatus = responseObject.executionStatus;
                                let executionOutput = (typeof executionStatus == "object") ? executionStatus.standardOutput : "";
                                let executionError = (typeof executionStatus == "object") ? executionStatus.standardError : "";
                                if (typeof executionStatus == "object" && executionStatus.timedOut) {
                                    executionError += "error: timed out\n";
                                }
                                $("#codeEditorExecutionOutput<!--{$exerciseID|validate:int}-->").append(consoleToHTML(executionError));
                                $("#codeEditorExecutionOutput<!--{$exerciseID|validate:int}-->").append(consoleToHTML(executionOutput));
                                break;
                            case "submit":
                                let standardOutput = responseObject.standardOutput;
                                $("#codeEditorExecutionOutput<!--{$exerciseID|validate:int}-->").append(consoleToHTML(standardOutput));
                                break;
                        }
                                     
                         // Re-enable buttons
                         // Re-enable buttons
                         executeButton.attr("disabled", false);
                         executeButton.attr("disabled", false);
Line 175: Line 205:
         <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-submit-button" onclick="this.form.submitter = 'submit';" type="submit" value="Submit"/>
     </div>
     </div>
     <div id="codeEditorCombinedOutput<!--{$exerciseID|validate:int}-->" class="merlin-code-explorer-combined-output"></div>
     <div id="codeEditorCompilerOutput<!--{$exerciseID|validate:int}-->" class="merlin-code-explorer-combined-output"></div>
    <div id="codeEditorExecutionOutput<!--{$exerciseID|validate:int}-->" class="merlin-code-explorer-combined-output"></div>
     </div>
     </div>
</form>
</form>
</includeonly>
</includeonly>

Revision as of 15:48, 30 December 2022

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
mode
string: language for 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
|mode=swift
|initialCode=func sayHello() {
    print("Hello, World!")
}
}}