Embed WTP as an iframe#

For educational use we decided to make the possibility to embed WebTigerPython as an iframe.

Embedding the iframe#

The iframe can be embedded in any website with the iframe html tag. A whole working example can be found in the Section Demo.

Embedding WTJ as an iframe is done as follows.

<iframe src="https://test.webtigerpython.ethz.ch" id="iframe" allow="usb;clipboard-write">
</iframe>

Messages can be sent to the iframe with JavaScript functionality. Somehow we have to create a reference to the iframe, in this example we do it by looking for the id which we specified above. To enable all features in WTP we need to allow usb and clipboard-write.

// Create a reference to the iframe
const iframe = document.querySelector("#iframe");

// Create a JavaScript object that can be sent to the iframe. See Messages
defObject =
{     
    type: 'files'
    data: [{
            'name': 'main.py'
            'data': 'print(42)'
            }]
}

//Use the postMessage API to send Messages to the iframe
iframe.contentWindow.postMessage(defObject), "*");

The types of messages that can be sent to the iframe and what they do is specified under Messages/to iframe

Now we can send messages to the iframe, however we also want to recieve messages. For that we need to register a listener. In this basic example, we log the data of the received messages to the console

window.addEventListener('message', function(event) {
    console.log(event.data)
});

The types of messages that are sent by the iframe and can be listened to are specified under Messages/from iframe

Messages#

Every Message is a Javascript Objects. It always contains two attributes.

type: specifiying the content of the message data: the data specified by the type

Example:

settings that can be sent to the iframe

{
    type: 'settings' // type of the message
    data: { // data defined by the type
        code: 'print("test")'
    }
}

To iframe#

Whenever a message is sent to an iframe and the message is parsed and the IDE executes the task. After the task is done, it responds with a message of type “ready”.

run_code#

To trigger the python execution from the parent, you have to send a message of type “run_code” with no data.

{
    type: 'run_code'
}

stop_execution#

To abort the python execution from the parent, you have to send a message of type “stop_execution” with no data.

{
    type: 'stop_execution'
}

settings#

Settings can be sent to the iframe, to adjust the state of WTP. Elements that can be updated in the settings are specified below:

Settings is sent to the iframe and configures the application accordingly. All elements specified in the settings are updated, while elements not specified in the data stay the same.

Fields that can be specified in the configuration:

Field

Type

Description

Example

lang

String

IDE language code

'de'

layout

Array<Array | string | object>

The Editor Layout. Nested Array, can only contain 1 canvas (see also layout)

["Editor", ["Canvas", "Console"]]

tutorial_size

Number

Width of the tutorial size. (see also layout)

0

device

String

Specify device to be programmed

"micro:bit"

error_messages

String[]

Here you can specify the error enhancement methods, multiple elements can be seperated by commas

['TigerJython','FriendlyTraceback']

dark_mode

Boolean

Toggles Dark Mode

true

Example settings:

{
    type: 'settings'
    data: {
        lang: 'en',
        output_size: 30,
        canvas_size: 60,
        tutorial_size: 0,
        device: 'micro:bit',
        err_msg: [],
        dark_mode: true, 
    }
}

get_settings#

with this you can query the settings

Example:

//Query:
{"type":"get_settings"}

//Response
{
    "type":"settings",
    "data":
    {
        "lang":"en",
        "error_messages":["TigerPython"],
        "dark_mode":false,
        "layout":["Editor",["Canvas","Console"]],
        "device":"-"
        ,"full_screen":false,
        "cascade_input":false,
        "tutorial_size":0
    }
}

layout#

The Layout is part of the settings, however due to it’s complexity, it is listed separately

The layout is a nested array. The first level specifies the UI elements on the horizontal axis seperated by vertical separators. The Array can contain Strings describing the components (“Editor”, “Console”, “Canvas”) or another (nested) Array containing further Components / Arrays. The Levels always alternate between horizontal / vertical splitting. Instead of strings describing the Arrays, it is also possible to use Javascript Objects with the Fields type & size to additionally specify size. Look at the examples below for inspiration.

Examples:

[“Editor”,“Console”]

{
    type: 'settings'
    data: {
        layout: ["Editor","Console"],
    }
}

https://test.webtigerpython.ethz.ch/?layout=[“Editor”,“Console”]

[[“Editor”,“Console”],“Canvas”]

{
    type: 'settings'
    data: {
        layout: [["Editor","Console"],"Canvas"],
    }
}

https://test.webtigerpython.ethz.ch/?layout=[[“Editor”,“Console”],“Canvas”]

[{“type”:[“Editor”,“Console”],“size:20”},“Canvas”]

{
    type: 'settings'
    data: {
        layout: [{"type":["Editor","Console"],"size:20"},"Canvas"],
        hide_topbar: true
    }
}

https://test.webtigerpython.ethz.ch/?layout=[{“type”:[“Editor”,“Console”],“size”:20},“Canvas”]&hide_topbar=true

files#

Caution

As of right now, it is not possible to work with multiple files!!! If multiple files are passed, the first one is taken

The Files can be updated with the postmessage API. The Files are sent as an array of objects which should all contain name and data.

Example files:

{
    type: 'files'
    data: [{
            'name': 'main.py'
            'data': 'print(42)'
            },
            {
            'name': 'file2.py'
            'data': 'print(43)'
            }]
}

get_files#

This can be used to query the current files. It will return an array of files containing file names and data:

Example:

//Query:
{"type":"get_files"}

//Response
{
    "type":"files",
    "data":[{
        "name":"main.py",
        "data":"print(123)"
        }]
}

From iframe#

Triggered by Events such as Code execution, the iframe will send messages back to the iframe.

ready#

events: IDE is ready

data: -

Example:

{type:"ready"}

code#

events: Code is executed, code is exported or flashed to microbit.

data: Code in the Editor

Example:

{
    type: "code"
    data: "print('test')"
}

output#

events: Line is printed to text output

data: Text Output of the code

Caution

Not every line is sent to the output window seperately. Sometimes multiple lines are bundled together.

Example:

{
    type: "output"
    data: "test\n"
}

error#

events: Error Message

data: Error

Example:

{
    type: "error"
    data: "test\ntest\n"
}

full_output#

events: After programm execution

data: After the program is finished, the full content of the text output containing outputs / errors

Example:

{
    type: "full_output"
    data: "test\n"
}

Demo#

Specify Config and send it to Editor above

Output From the Editor is logged below

Source Code of the Demo#

<body>
	<iframe src="https://webtigerpython.ethz.ch/" id="iframe" height=500 width="500">
    </iframe>
	<div id="app">
        <p>Specify Config and send it to Editor above</p>
        <textarea id='messageInput'>{'}</textarea>
		<button id="sendConfig">Send Config</button>
        <p>Output From the Editor is logged below</p>
        <div id='outputArea'></div>
	</div>
    <script>
        var button = document.querySelector("#sendConfig");
        const iframe = document.querySelector("#iframe");
        const editor = document.querySelector("#messageInput")
        defObject = { type: "files", data: [{ name: 'main.py', data:'print(42)'}] }
        editor.value = JSON.stringify(defObject)
        function sendMessage() {
            iframe.contentWindow.postMessage( JSON.parse(editor.value), "*");
        }
        button.addEventListener("click", sendMessage);
        const output = document.querySelector("#outputArea")
        output.innerHTML = ''
        window.addEventListener('message', function(event) {
            output.innerHTML = output.innerHTML + '<br>' + JSON.stringify(event.data)
        });
    </script>
</body>