Compilers Widget Guidebook


Sphere Engine Compilers is a service that allows for remote execution of computer programs. It allows you to send a computer program written in one of many supported programming languages and execute it with the input data that you defined.

Sphere Engine Compilers Widget is a method of integration of the Sphere Engine Compilers service via a component ready to be embedded on the website (widget). It is a quick and easy method of integration, recommended for the extension of existing projects. The alternative is the integration through API.

Embedding an interactive programming problem on the website has many applications. For example:

  • development of recruitment systems for automatic source code evaluation,
  • supplementing e-learning programming courses of executable examples,
  • adding a possibility of executing code snippets on the sharing source code platform

Note: The above applications suggest the use of a widget in the extension of existing systems. Nothing stands in the way of using this integration method in building systems from scratch.

Check out how it works

The widget below presents crucial features of the Sphere Engine Compilers Widget.

Widget integration

Before the widget is integrated, it is necessary to create and configure it. In the widgets management panel the Create widget button is available which, when pressed, initiates the creation process.

Configuration data required for integration is available in the Menu > Compilers > Widgets > WIDGET NAME > Widget integration.

JavaScript

It is required to attach two HTML snippets to your system. The first one is responsible for the initialization, the second is responsible for displaying the widget.

Initialization

The code responsible for initialization should be embedded only once (regardless of the number of widgets displayed on the site), right after the <body> element.

<script>SEC_HTTPS = false;
SEC_BASE = "[customized_link]"; 
(function(d, s, id){ SEC = window.SEC || (window.SEC = []);
  var js, fjs = d.getElementsByTagName(s)[0];
  if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; 
  js.src = (SEC_HTTPS ? "https" : "http") + "://" + SEC_BASE + "/static/sdk/sdk.js";
  fjs.parentNode.insertBefore(js, fjs);   
}(document, "script", "sphere-engine-compilers-jssdk"));
</script>
Display

The code responsible for displaying the widget has the following form:

<div class="sec-widget" data-widget="[hash]"></div>

The [hash] parameter appearing as the value of the "data-widget" attribute is the unique identifier of the widget in the Sphere Engine system. You can find it in the Unique hash field in the Widget options.

Displaying many widgets

The Sphere Engine Compilers Widget allows you to embed multiple widgets on a single page. In the basic variant no additional action is required.

If you need to manage your widgets by JavaScript (see JavaScript SDK) it is necessary to identify them, which is done by means of a special identifier (marked as [id]) determined by the attribute data-id of the div element used to embed the widget. For example:

<div id="idInHTML" data-id="[id]" class="sec-widget" data-widget="[hash]"></div>

Note: The widget is initialized automatically - immediately after loading the HTML document. Thanks to this it is possible to perform operations on the widget (e.g. configuration) before displaying it.

Note: The widget starts to load only when it becomes visible on the screen (i.e. when the user scrolls the page to the widget position). This allows you to speed up the page loading process which is especially useful when embedding multiple widgets.

Standalone page

An alternative to embed the widget directly on your site is to use the possibility of presenting it in the "standalone" mode, i.e. on a separate website.

The link to the page is generated when creating the widget. In the Menu > Compilers > Widgets > WIDGET NAME > Widget integration there is a ready-to-use link with the following structure:

https://compilers.widgets.sphere-engine.com/lp?hash=[hash]

Note: The widget page in the "standalone" mode only displays the widget by default. In the Widget options it is possible to define (in the form of HTML code) the header of the page with the widget. In the header, you can embed, for example, your own logo.

User session

The Sphere Engine Compilers Widget provides user identification capabilities:

  • based on the se_user_id parameter (managed in the client's system),
  • based on cookies (within a internet browser).

User identification based on se_user_id has priority, i.e. if this parameter is specified, it will be used for identification.

Important: Use of the se_user_id parameter allows you to resume user session between different browsers, operating systems and even computers.

Security

The Sphere Engine Compilers Widget provides the possibility of secure integration.

Signature

In the production environment, we recommend using security with a signature (based on the dedicated easy to implement algorithm), which protects the widget from unauthorized use. The signature verification requirement must be enabled in advance by selecting the Secure the widget option in Menu > Compilers > Widgets > WIDGET NAME > Widget integration.

The use of a signature for communication between the client system and the Sphere Engine increases the security level, i.e.:

  • eliminates the possibility of unauthorized access to the widget (preventing even complex and difficult access attempts performed by preparing false messages),
  • guarantees the reliability of transmitted data.
Signature parameters

Signature generation is based on selected widget parameters and additional signature parameters:

  • hash - the unique identifier of the widget in the Sphere Engine system. You can find it in the Unique hash field in Menu > Compilers > Widgets > WIDGET NAME > Widget integration.
  • se_secret - secret key being the basis for the security of the signature. You can find it in the Shared secret field in Menu > Compilers > Widgets > WIDGET NAME > Widget integration.
  • (optional) se_nonce - unique (for a given widget) session ID.

Important: The se_secret parameter it is the basis for the security of the signature and should remain secret. Do not send it in messages between systems.

Important: The signature generation procedure in the client system should not be performed on the front-end side of the application (e.g. by a function in JavaScript) due to the disclosure of the se_secret parameter.

Note: The use of the optional se_nonce parameter ensures that each end user session (i.e., every situation in which the widget has been initialized) is a one-off. It is not possible to re-initialize the widget using the se_nonce parameter with the same value. The use of this parameter provides full protection and security against unauthorized use of the widget.

Signature generation algorithm

The procedure for generating a signature can be divided into the following steps:

  • Normalization by sorting parameter names in alphabetical order. For example:
    [hash, se_secret, se_nonce] ➡ [hash, se_nonce, se_secret].
  • Conjunction by saving parameters and their values in the form of an inscription with the following format:
    param1=value1&param2=value2&...&paramN=valueN
    for example:
    hash=XYZ&se_nonce=12345&se_secret=CIPHER
  • (optional - only if there are non-valid characters in the ASCII parameter values) Encoding for the application/x-www-form-urlencoded format using the UTF-8 encoding.
  • Hashing using the SHA-256 algorithm which is available for most popular programming languages (in the standard library or as part of an additional library).

Pseudocode of the algorithm that generates the signature:

generate_signature(params) {
    // Normalization
    params = sort(params);

    // Conjunction with Encoding
    seq = [];
    foreach p in params {
        seq += key(p) + "=" + urlencode(value(p), "utf-8"); 
    }

    // Hashing
    return sha256(join("&", seq));
}

Implementation of algorithm that generates a signature in PHP:

function generate_signature($params) {
    ksort($params);

    $seq = [];
    foreach ($params as $k => $v) {
        $seq[] = $k . "=" . urlencode($v);
    }

    return hash("sha256", implode("&", $seq));
}

Implementation of algorithm that generates a signature in Python:

def generate_signature(params):
    keys = sorted(params.keys())

    seq = []
    for k in keys :
        seq.append(k + "=" + urllib.parse.quote_plus(params[k]))

    return hashlib.sha256("&".join(seq).encode('utf-8')).hexdigest()
Attaching a signature

Signature and values of all parameters used to generate it (except the se_secret parameter, which should remain secret) should be attached to the embedded widget in the form of the appropriate HTML attributes with the data- prefix:

  • data-widget for the hash parameter,
  • data-nonce for the se_nonce parameter,
  • data-signature for the generated signature.

An example of HTML code used to embed a widget prepared to support the signature:

<div class="sec-widget" 
    data-id="widget-secured-by-signature" 
    data-widget="XYZ"  
    data-signature="e85b12137f76526ca6804625fc4e2093a0750fc6c6c158fc2be210ac5" 
    data-nonce="12345">
</div>

For those interested: The general scheme for securing communication by signature assumes that both communicating pages share a secret key (so-called shared secret).
The sender uses the selected message parameters (or even the entire message) along with the key to create the so-called signature. The generated signature is attached to the message and sent to the recipient.
The recipient, who also has access to the key, repeats the procedure for creating the signature. The sent message is authorized, if the signature sent by the sender is identical to the signature generated by the recipient.

JavaScript SDK

The Sphere Engine JavaScript SDK library is loaded asynchronously. For this reason, before using the functions provided by the library, you must be sure that it has been initialized.

First of all, it is required to place the following code snipped right after the initialization of the Sphere Engine Compilers Widget:

<script>SEC.ready=function(e){"loading"!=document.readyState&&"interactive"!=document.readyState?e():window.addEventListener("load",e)};</script>

The SEC.ready() function has been designed in a way that ensures that functionalities using the JavaScript SDK will be used only when they are ready:

SEC.ready(function() {
    // SDK functions usage
});

Use of the SEC.ready() function makes sure that the library has been loaded, the SEC object has been initialized and no errors will occur while parsing the scripts.

Access to the widget

Access to the widget is obtained by using the SEC.widget() function, which based on the given widget ID (previously identified by the attribute data-id) returns the class object SECWidget used to manage the widget:

SEC.ready(function() {
    var widget = SEC.widget("widget");
    // variable "widget" is ready to use
});

The SECWidget class provides the following widget management capabilities:

  • access widgets,
  • dedicated events and event management.

Methods

The global SEC object provides public methods for managing widgets.

SEC.widget()
A SECWidget object is created that represents the widget.

Parameters

Name Type Description
id string ID of the widget placed in the HTML document

Returned value

The class SECWidget object is returned.

Example

Press the button to load widget
Load widget
<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
    SEC.ready(function() {
        $('#btn-load-widget').on('click', function(e) {
            e.preventDefault();
            var SECWidget = SEC.widget("example-widget");
        });
    });
</script><a href="#" id="btn-load-widget" class="btn btn-default">Load widget</a>

SECWidget.config()
Set widget configuration.

Parameters

Name Type Description
configOptions object configuration options

Example

<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
    SEC.ready(function() {
        var SECWidget = SEC.widget("example-widget");
        SECWidget.config({
            code: {
                compiler: 28,
                source: "#!/bin/bash\n\necho test",
                input: "",
            },
            toolbar: {
                groups: [
                    {
                        id: "left",
                        buttons: [
                            {
                                id: "button",
                                icon: "code",
                                type: "primary",
                                tooltip: "My tooltip",
                                callback: function(codeEditor) {
                                    alert('You clicked the button');
                                }
                            }
                        ]    
                    }
                ]
            },
            compilers: {
                list: [11, 21, 28, 33]
            }
        });
    });
</script>

SECWidget.loadCode()
Load the data (source code, programming language and input data) to the editor.

Parameters

Name Type Description
compiler integer identifier of programming language (see. list of languages)
sourceCode string source code of the program
input string input data

Example

<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
    SEC.ready(function() {
        var sourceCode = "<?php \n\n$hi = fopen('php://stdin', \"r\");\nfscanf($hi, \"%d\", $n);\n\
fclose($hi);\n\necho 'number: ' . $n;";
        var SECWidget = SEC.widget("example-widget");
        SECWidget.loadCode(29, sourceCode, "5");
    });
</script>

SECWidget.chooseCompilers()
Set the list of available programming languages.

Parameters

Name Type Description
compilersList array[int] list of languages identifiers (see. list of languages)

Example

<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
    SEC.ready(function() {
        var SECWidget = SEC.widget("example-widget");
        SECWidget.chooseCompilers([1,2]);
    });
</script>

SECWidget.events.subscribe()
Assigns a function to the event (see events list).

Parameters

Name Type Description
event string the name of the event to assign a function
callback function function to be called on an event

Example

<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
SEC.ready(function() {

    var callback = function(data) {
        // Your code goes here
    };

    var SECWidget = SEC.widget("example-widget");
    SECWidget.events.subscribe('{name_of_the_event}', callback);

});
</script>

SECWidget.events.unsubscribe()
Removes a function from the list of functions assigned to the event (see events list).

Parameters

Name Type Description
event string the name of the event to delete the function
callback function function to be removed from the list of functions at the event

Example

<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
SEC.ready(function() {

    var callback = function(data) {
        // Your code goes here
    };

    var SECWidget = SEC.widget("example-widget");
    SECWidget.events.unsubscribe('{name_of_the_event}', callback);

});
</script>

SECWidget.toolbar.createButton()
Create a button on the toolbar of the widget.

Parameters

Name Type Description
groupId string identifier of the group of buttons
buttonId string identifier of the button
buttonIcon string icon name (from Font Awesome)
buttonType string the type of the button (default, primary, warning, success, danger)
buttonTooltip string the content of the message displayed after hovering over the button
buttonCallback function the callback function triggered after pressing the button

Example

<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
    SEC.ready(function() {
        var SECWidget = SEC.widget("example-widget");
        SECWidget.toolbar.createButton(
            "middle",
            "alert-button",
            "refresh",
            "primary",
            "tooltip :)",
            function(){
                alert('button clicked');
            }
        );
    });
</script>

SECWidget.toolbar.deleteButton()
Delete a button from the toolbar of the widget.

Parameters

Name Type Description
buttonId string identifier of the button

Example

Remove toolbar button
<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
    SEC.ready(function() {
        var SECWidget = SEC.widget("example-widget");
        SECWidget.toolbar.createButton(
            "middle",
            "alert-button",
            "refresh",
            "primary",
            "tooltip :)",
            function(){
                alert('button clicked');
            }
        );
        $('#btn-delete-toolbar-button').on('click', function(e) {
            e.preventDefault();
            SECWidget.toolbar.deleteButton('alert-button');
        });
    });
</script><a href="#" id="btn-delete-toolbar-button" class="pt-2">Remove toolbar button</a>

SECWidget.toolbar.createButtonGroup()
Create a group of button on the toolbar of the widget.

Parameters

Name Type Description
groupId string identifier of the group
groupButtons array array of buttons identifiers

Example

<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
    SEC.ready(function() {
        var buttons = [
            {
                "id": "button_id_1",
                "icon": "refresh",
                "type": "default",
                "tooltip": "tooltip :)",
                "callback": function(){
                    alert('button1 clicked');
                }
            },
            {
                "id": "button_id_2",
                "icon": "refresh",
                "type": "primary",
                "tooltip": "tooltip :)",
                "callback": function(){
                    alert('button2 clicked');
                }
            }
        ];
        var SECWidget = SEC.widget("example-widget");
        SECWidget.toolbar.createButtonGroup(
            "middle",
            buttons
        );
    });
</script>

SECWidget.toolbar.deleteButtonGroup()
Delete a group of buttons from the toolbar of the widget.

Parameters

Name Type Description
groupId string identifier of the group

Example

remove toolbar button group
<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
    SEC.ready(function() {
        var buttons = [
            {
                "id": "button_id_1",
                "icon": "refresh",
                "type": "default",
                "tooltip": "tooltip :)",
                "callback": function(){
                    alert('button1 clicked');
                }
            },
            {
                "id": "button_id_2",
                "icon": "refresh",
                "type": "primary",
                "tooltip": "tooltip :)",
                "callback": function(){
                    alert('button2 clicked');
                }
            }
        ];
        var SECWidget = SEC.widget("example-widget");
        SECWidget.toolbar.createButtonGroup(
            "middle",
            buttons
        );
        $('#btn-remove-toolbar-button-group').on('click', function(e) {
            e.preventDefault();
            SECWidget.toolbar.deleteButtonGroup("middle");
            $(this).addClass('disabled');
        });
    });
</script><a href="#" id="btn-remove-toolbar-button-group">remove toolbar button group</a>

Events

The SECWidget class object has a collection of dedicated events called at specified times of the widget operation.

beforeSendSubmission
The event is invoked before sending the request to the Sphere Engine system (i.e. after pressing the "Submit" button but before the actual sending of the submission).

Data sent to the callback function

An object with the following fields is sent to the callback function:

Name Type Description
submissionSource string the source code of the program sent in the submission
submissionLanguage string the programming language in which the program was implemented
submissionInput string the input data

The value returned by the callback function

The value returned by the callback function determines whether the submission will be sent to the Sphere Engine system:

  • true - the submission is to be sent
  • false - sending the submission will be stopped

Example

Data:
Submit submission to see the result
<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
    SEC.ready(function() {
        var beforeSendSubmission = function(data) {
            $('#result').html('')
                .append("> submissionLanguage: " + data.submissionLanguage + "<br><br>")
                .append("> submissionInput: <br>" + data.submissionInput + "<br><br>")
                .append("> submissionSource: <br>" + data.submissionSource + "<br><br>");
            return true;
        };
        var SECWidget = SEC.widget("example-widget");
        SECWidget.events.subscribe('beforeSendSubmission', beforeSendSubmission);
    });
</script>
Data:
<pre id="result" style="height: 300px;">Submit submission to see the result</pre>

afterSendSubmission
The event is dispatched immediately after sending the submission to the Sphere Engine system.

Data sent to the callback function

An object with the following fields is sent to the callback function:

Name Type Description
submissionId integer report identifier in the Sphere Engine system

Example

Data:
Submit submission to see the result
<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
    SEC.ready(function() {
        var afterSendSubmission = function(data) {
            $('#result').html('')
                .append("> submissionId: " + data.submissionId + "<br><br>");
        };
        var SECWidget = SEC.widget("example-widget");
        SECWidget.events.subscribe('afterSendSubmission', afterSendSubmission);
    });
</script>
Data:
<pre id="result" style="height: 100px;">Submit submission to see the result</pre>

checkStatus
The event is invoked regularly while checking the status of the submission during execution.

Data sent to the callback function

An object with the following fields is sent to the callback function:

Name Type Description
submissionId integer submission identifier in the Sphere Engine system
submissionTime integer start time in seconds (after startup)
submissionMemory integer memory consumption in kilobytes (after startup)
statusNumber integer the numeric value of the submission status (see: status list)
statusDescription string description of the submission status

Example

Data:
Submit submission to see the result
<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
    SEC.ready(function() {
        var checkStatus = function(data) {
            $('#result')
                .append("<br><br>")
                .append("> submissionId: " + data.submissionId + "<br>")
                .append("> statusNumber: " + data.statusNumber + "<br>")
                .append("> statusDescription: " + data.statusDescription + "<br>")
                .append("> submissionMemory: " + data.submissionMemory + "<br>")
                .append("> submissionTime: " + data.submissionTime + "<br>");
            $('#result').scrollTop($('#result').prop("scrollHeight"));
        };
        var SECWidget = SEC.widget("example-widget");
        SECWidget.events.subscribe('checkStatus', checkStatus);
    });
</script>        
Data:
<pre id="result" style="height: 300px;">Submit submission to see the result</pre>

enterFullScreen
The event is triggered after entering the full screen mode of the widget.

Example

<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
    SEC.ready(function() {
        var enterFullScreen = function() {
            alert('enter fullscreen');
        };
        var SECWidget = SEC.widget("example-widget");
        SECWidget.events.subscribe('enterFullScreen', enterFullScreen);
    });
</script>

exitFullScreen
The event is triggered after exiting the full screen mode of the widget.

Example

<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
    SEC.ready(function() {
        var exitFullScreen = function() {
            alert('exit fullscreen');
        };
        var SECWidget = SEC.widget("example-widget");
        SECWidget.events.subscribe('exitFullScreen', exitFullScreen);
    });
</script>