Problems
Widgets - JavaScript SDK

Before the widget can be integrated, it is necessary to create and configure it. Please follow this quickstart tutorial to create and configure your Sphere Engine Problems Widget.

Important: To successfully integrate Sphere Engine Problems Widget you need to have a working Sphere Engine account. Register for a Sphere Engine account by filling in the sign-up form.

Standalone page

To embed the widget on a separate website located in the sphere-engine.com domain use the feature of presenting it as a "standalone page".

A link to a standalone page is generated in the Sphere Engine client panel when creating the widget: Menu > Problems > Widgets > WIDGET NAME > Get the code. It has the following structure:

https://<customer_id>.widgets.sphere-engine.com/lp?hash=<hash>

Note: The Standalone page section of the Menu > Problems > Widgets > WIDGET NAME > Get the code page provides a configuration field that allows you to adjust (in HTML code) the header of the page with the Problems widget (e.g., embedding your own logo in the header).

JavaScript Integration

When using this method, you need to insert two snippets of HTML code responsible for the initialization and display of the widget.

Initializing the widget

The code responsible for widget initialization should be inserted only once (regardless of the number of widgets you want to display on the page), immediately after the <body> element.

A customized and ready-to-use widget initialization code can be found in Sphere Engine client panel: Menu > Problems > Widgets > WIDGET NAME > Get the code:

<script>(function(d, s, id){
  SE_BASE = "<customer_id>.widgets.sphere-engine.com";
  SE_HTTPS = true;
  SE = window.SE || (window.SE = []);
  var js, fjs = d.getElementsByTagName(s)[0];
  if (d.getElementById(id)) return;
  js = d.createElement(s); js.id = id;
  js.src = (SE_HTTPS ? "https" : "http") + "://" + SE_BASE + "/static/sdk/sdk.min.js";
  fjs.parentNode.insertBefore(js, fjs);
}(document, "script", "sphere-engine-jssdk"));

SE.ready = function(f) {
  if (document.readyState != "loading" && document.readyState != "interactive") f();
  else window.addEventListener("load", f);
};
</script>

Displaying the widget

The following code should be inserted on the page in the spot where you want the widget to be displayed:

<div class="se-widget" data-id="example-widget" data-widget="<hash>"></div>

The hash parameter which is the value of the data-widget attribute is the unique ID of the widget in the Sphere Engine system. It can be found under the Unique hash field in Sphere Engine client panel: Menu > Problems > Widgets > WIDGET NAME > Widget settings.

Displaying multiple widgets

Sphere Engine Problems Widget allows you to embed multiple widgets on a single page.

If you need to manipulate widget's behavior using JavaScript (see JavaScript SDK) it is necessary to identify it, which is done by means of a special identifier (marked as <id>) denoted by the data-id attribute of the div element used to embed the widget. For example:

<div id="idInHTML" data-id="<id>" class="se-widget" data-widget="<hash>"></div>

Note: The widget is initialized automatically after the page loads in the browser, even if it is not yet visible on the page. This makes performing operations on the widget possible (e.g., configuration) before displaying it.

Note: The process of loading (and displaying) the widget begins only when the widget becomes visible on the screen (i.e., when the user scrolls the page to the widget's position). Thanks to this, the loading of other elements on the page is not blocked. This allows for the page to load faster, which is especially useful when embedding multiple widgets.

User session

Sphere Engine Problems Widget maintains user session, which enables end-users to continue their work even after leaving and returning to the widget. This means, that when restoring the session, the widget will automatically load the user's submission history and restore the source code draft to the widget's editor.

Sphere Engine Problems Widget provides the following user identification capabilities:

  • using the se_user_id parameter (managed in the client's system),
  • using cookies (in a web browser).

To use the se_user_id parameter, add the data-user-id attribute to the div element used to embed the widget together with the user_id.

<div class="se-widget" data-id="example-widget" data-widget="<hash>" data-user-id="{user_id}"></div>

User identification based on se_user_id has priority over the one based on cookies, i.e., if this parameter is specified, it will be used for identification instead of an existing cookie.

Important: Using the se_user_id parameter allows you to resume user sessions between different web browsers and different devices.

Note: To mitigate the risk of a user session being taken over by an unauthorized person, consider using the widget signature.

Custom data

Custom data enables adding custom data to the Problems Widget, which will be returned by webhooks and JavaScript SDK callbacks. You can use custom data to e.g., identify the context in which a specific widget has been used inside your application.

The format of the data is free but it has to fit the space of 256 bytes. In case of using selected special characters (i.e., " or #) or characters outside of the ASCII standard, the number of bytes increases because of encoding them inside a URL. When validating data, the system limits the final encoded value to the first 256 bytes.

Example of proper formatting

course_id={course_id};another_param={value}

To add custom data, please add the data-custom-data attribute to the div element used to embed the widget together with your custom data.

<div class="se-widget" data-id="example-widget" data-widget="<hash>" data-custom-data="course_id={course_id};another_param={value}"></div>

Themes

Themes allow you to customize the look and feel of a widget. To manage the themes go to the Themes section in the Sphere Engine client panel.

The following table lists built-in themes that can be used with both the Sphere Engine Problems and the Sphere Engine Compilers widgets:

Name Description
Light The default theme
Dark A dark theme

To set a theme for a widget, add the data-theme attribute to the widget's div element:

<div class="se-widget" data-id="example-widget" data-widget="<hash>" data-theme="dark"></div>

JavaScript SDK

The Sphere Engine JavaScript SDK library for Problems widget (or simply JavaScript SDK) is loaded asynchronously. For this reason, before using the functions provided by the library, we must be sure that it has been initialized.

<script>(function(d, s, id){
  SE_BASE = "<customer_id>.widgets.sphere-engine.com";
  SE_HTTPS = true;
  SE = window.SE || (window.SE = []);
  var js, fjs = d.getElementsByTagName(s)[0];
  if (d.getElementById(id)) return;
  js = d.createElement(s); js.id = id;
  js.src = (SE_HTTPS ? "https" : "http") + "://" + SE_BASE + "/static/sdk/sdk.min.js";
  fjs.parentNode.insertBefore(js, fjs);
}(document, "script", "sphere-engine-jssdk"));

SE.ready = function(f) {
  if (document.readyState != "loading" && document.readyState != "interactive") f();
  else window.addEventListener("load", f);
};
</script>

The above code includes the SE.ready() function which ensures that features utilizing JavaScript SDK will be used only after all the required components have been loaded.

All operations that use JavaScript SDK should be performed through the SE.ready() function, i.e., according to the following scheme:

<script>
SE.ready(function() {
    // Sphere Engine JavaScript SDK usage
});
</script>

Using the SE.ready() function ensures that the library has been loaded, the SE object has been initialized and no errors will occur while scripts are being executed.

Access

We gain access to the widget by using the SE.widget() function, which (basing on the given widget ID, previously defined by the data-id attribute) returns the object of SEWidget class used to manage the widget. For example:

<script>
SE.ready(function() {
    var widget = SE.widget("example-widget");
    // variable "widget" is ready to use
});
</script>

The SEWidget class provides the following options for managing the widget:

  • creating a widget,
  • dedicated events for handling actions performed by the widget.

Methods

The global SE object provides public methods for managing widgets.

METHOD SE.widget(id)

An object of class SEWidget, which represent the widget, is created.

Parameters
Name Type Description
id * string

widget's identifier placed in the HTML document (data-id attribute)

Returned value

An object of class SEWidget is returned.

Example
Press the button to load the widget
Load widget
<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
    SE.ready(function() {
        var btnLoadWidget = document.getElementById("btn-load-widget");
        btnLoadWidget.addEventListener("click", function(e) {
            e.preventDefault();
            var SEWidget = SE.widget("example-widget");
        });
    });
</script>
<a href="#" id="btn-load-widget">Load widget</a>

METHOD SEWidget.destroy()

Destroys the widget.

Example
Destroy widget
<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
    SE.ready(function() {
        var btnDestroyWidget = document.getElementById("btn-destroy-widget");
        btnDestroyWidget.addEventListener("click", function(e) {
            e.preventDefault();
            var SEWidget = SE.widget("example-widget");
            SEWidget.destroy();
        });
    });
</script>
<a href="#" id="btn-destroy-widget" class="btn btn-default">Destroy widget</a>

METHOD SEWidget.loadSourceCode(language, sourceCode)

Loading data (source code, programming language) to the running editor.

Parameters
Name Type Description
language integer

programming language identifier (see. list of languages)

sourceCode string

program's source code

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

METHOD SEWidget.setLanguage(language)

Loading programming language to the running editor.

Parameters
Name Type Description
language integer

programming language identifier (see. list of languages)

Example
<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
    SE.ready(function() {
        var SEWidget = SE.widget("example-widget");
        SEWidget.setLanguage(10);
    });
</script>

METHOD SEWidget.setSource(sourceCode)

Loading source to the running editor.

Parameters
Name Type Description
sourceCode string

program's source code

Example
<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
    SE.ready(function() {
        var sourceCode = "print(\"hello world\")";
        var SEWidget = SE.widget("example-widget");
        SEWidget.setSource(sourceCode);
    });
</script>

METHOD SEWidget.events.subscribe(event, callback)

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

Parameters
Name Type Description
event * string

the name of the event to assign a function to

callback * function

function to be called on an event

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

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

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

});
</script>

METHOD SEWidget.events.unsubscribe(event, callback)

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

Parameters
Name Type Description
event * string

the name of the event to remove the function from

callback * function

function to be removed from the list of functions assigned to the event

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

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

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

});
</script>

Events

An object of the SEWidget class has a collection of dedicated events called at specific moments of the widget's operation.

EVENT beforeSendSubmission

The event is invoked before sending a submission to the Sphere Engine system (i.e. after pressing the Submit button but before actually sending the submission).

Data sent to the callback function

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

Name Type Description
submission.source string

the source code of the program sent in the submission

submission.language integer

the identifier of the programming language used to write the program

customData string

your custom data added when initializing the widget, see custom data

The value returned by the callback function

The value returned by the callback function allows modifying the submission data before it's sent to the Sphere Engine servers. It also allows canceling the submission altogether.

To modify the submission data before it's sent to the Sphere Engine servers, return an object with the following fields:

Name Type Description
submission.source string the source code of the program
submission.language integer programming language identifier
customData string custom data

To leave the submission data unaltered, skip chosen fields in the object.

You can also return a boolean value instead of the object to determine whether the submission should be sent or not.

  • true - the submission will continue,
  • false - the submission will be cancelled.

Note: If the callback function returns an object, the submission will always continue.

Example
Data:
Submit submission to see the result
<div data-id="example-widget" class="se-widget" data-widget="{widget_hash}" data-custom-data="course-id=1"></div>
<script>
    SE.ready(function() {

        var beforeSendSubmission = function(data) {
            var resultElement = document.getElementById('result');
            resultElement.innerHTML = ''
            resultElement.innerHTML += '> data.submission.language: ' + data.submission.language + '<br><br>';
            resultElement.innerHTML += '> data.submission.source: <br>' + data.submission.source + '<br><br>';
            resultElement.innerHTML += '> data.customData: <br>' + data.customData + '<br><br>';

            return {
                customData: data.customData + ';timestamp=' + Math.floor(Date.now() / 1000), // is optional
                submission: {
                    // source: 'print(\'1 2 3\')', // is optional
                    // language: 116, // is optional
                }, // is optional
            }

            // or just
            // return true;
        };

        var SEWidget = SE.widget('example-widget');
        SEWidget.events.subscribe('beforeSendSubmission', beforeSendSubmission);
    });
</script>

Data:
<pre id="result" style="height: 300px;">Submit submission to see the result</pre>

EVENT afterSendSubmission

The event is invoked immediately after the submission has been sent 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
submission.id integer

submission identifier in the Sphere Engine system

submission.apiId integer

submission identifier in the Sphere Engine system used to access submission using API; use getSubmission() API method to fetch all submission details

user.uuid string

user identifier generated by the Sphere Engine system

user.id string

user identifier provided during widget initialization (user-id parameter)

user.name string

user name provided on the welcome page

user.email string

user email address provided on the welcome page

customData string

your custom data added when initializing the widget, see custom data

Example
Data:
Submit submission to see the result
<div data-id="example-widget" class="se-widget" data-widget="{widget_hash}"></div>
<script>
    SE.ready(function() {

        var afterSendSubmission = function(data) {
            var resultElement = document.getElementById('result');
            resultElement.innerHTML = '';
            resultElement.innerHTML += "> submission.id: " + data.submission.id + "<br>";
            resultElement.innerHTML += "> submission.apiId: " + data.submission.apiId + "<br><br>";
        };

        var SEWidget = SE.widget("example-widget");
        SEWidget.events.subscribe('afterSendSubmission', afterSendSubmission);
    });
</script>

Data:
<pre id="result" style="height: 100px;">Submit submission to see the result</pre>

EVENT checkStatus

The event is invoked repeatedly every time the JavaScript SDK library checks the status of the submission while it’s being executed.

Data sent to the callback function

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

Name Type Description
submission.id integer

submission identifier in the Sphere Engine system

submission.apiId integer

submission identifier in the Sphere Engine system used to access submission using API; use getSubmission() API method to fetch all submission details

status.number integer

numeric value of the submission's status (see: list of statuses)

status.description string

description of the submission's status

user.uuid string

user identifier generated by the Sphere Engine system

user.id string

user identifier provided during widget initialization (user-id parameter)

user.name string

user name provided on the welcome page

user.email string

user email address provided on the welcome page

customData string

your custom data added when initializing the widget, see custom data

Example
Data:
Submit submission to see the result
<div data-id="example-widget" class="se-widget" data-widget="{widget_hash}"></div>

<script>
    SE.ready(function() {

        var checkStatus = function(data) {
            var resultElement = document.getElementById('result');
            resultElement.innerHTML += "<br><br>";
            resultElement.innerHTML += "> submission.id: " + data.submission.id + "<br>";
            resultElement.innerHTML += "> submission.apiId: " + data.submission.apiId + "<br>";
            resultElement.innerHTML += "> status.number: " + data.status.number + "<br>";
            resultElement.innerHTML += "> status.description: " + data.status.description + "<br>";
            resultElement.innerHTML += "> submission.memory: " + data.submission.memory + "<br>";
            resultElement.innerHTML += "> submission.time: " + data.submission.time + "<br>";

            resultElement.scrollTop = resultElement.scrollHeight;
        };
        var SEWidget = SE.widget("example-widget");
        SEWidget.events.subscribe('checkStatus', checkStatus);
    });
</script>

Data:
<pre id="result" style="height: 300px;">Submit submission to see the result</pre>

EVENT sessionOver

The event is invoked after the time set for solving the task has expired.

Data sent to the callback function

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

Name Type Description
customData string

your custom data added when initializing the widget, see custom data

Example
Data:
Start the session and wait 1 minute
<div data-id="example-widget" class="se-widget" data-widget="{widget_hash}"></div>

<script>
    SE.ready(function() {
        var sessionOver = function() {
            var resultElement = document.getElementById('result');
            resultElement.innerHTML = 'the session is over';
        };

        var SEWidget = SE.widget("example-widget");
        SEWidget.events.subscribe('sessionOver', sessionOver);
    });
</script>

Data:
<pre id="result" style="height: 50px;">Start the session and wait 1 minute</pre>

EVENT welcomeUser

The event is invoked after sending the username and email address entered on the welcome page (available only for the configuration of the widget with the welcome page enabled).

Data sent to the callback function

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

Name Type Description
user.uuid string

user identifier generated by the Sphere Engine system

user.id string

user identifier provided during widget initialization (user-id parameter)

user.name string

user name provided on the welcome page

user.email string

user email address provided on the welcome page

customData string

your custom data added when initializing the widget, see custom data

Example
Data:
Complete the form to see the result
<div data-id="example-widget" class="se-widget" data-widget="{widget_hash}"></div>

<script>
    SE.ready(function() {
        var welcomeUser = function(data) {
            var resultElement = document.getElementById('result');
            resultElement.innerHTML = '';
            resultElement.innerHTML += "> user.name: " + data.user.name + "<br><br>";
            resultElement.innerHTML += "> user.email: " + data.user.email + "<br><br>";
        };

        var SEWidget = SE.widget("example-widget");
        SEWidget.events.subscribe('welcomeUser', welcomeUser);
    });
</script>

Data:
<pre id="result" style="height: 100px;">Complete the form to see the result</pre>

EVENT languageChanged

The event is invoked when the programming language changes.

Data sent to the callback function

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

Name Type Description
language integer

the identifier of the programming language

Example
<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
    SE.ready(function() {
        var languageChanged = function(data) {
            var result = document.getElementById('result');
            result.innerHTML += '> language: ' + data.language + '<br />';
        };
        var SEWidget = SE.widget('example-widget');
        SEWidget.events.subscribe('languageChanged', languageChanged);
    });
</script>
Data:
<pre id="result" style="height: 100px;"></pre>

EVENT widgetLoaded

The event is invoked once the widget is loaded.

Data sent to the callback function

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

Name Type Description
widgetId string

widget's identifier

iframe HTMLElement

iframe element where the widget is displayed

Example
<div data-id="example-widget" data-widget="{widget_hash}"></div>
<script>
    SE.ready(function() {
        var widgetLoaded = function(data) {
            var widgetId = data.widgetId; // example-widget
            var iframe = data.iframe; // <iframe ...></iframe>
        };
        var SEWidget = SE.widget('example-widget');
        SEWidget.events.subscribe('widgetLoaded', widgetLoaded);
    });
</script>