7.7 Using CBA ItemBuilder Items in Custom Web Applications (Taskplayer API)

This section briefly describes how software developers can use CBA ItemBuilder content in web applications.

Important note: This section describes how CBA ItemBuilder projects can be embedded into new environments by technically experienced programmers, without using the existing deployment software tools described in this chapter.

The CBA ItemBuilder is the tool for creating individual assessment components. These can be items, instructions, units or entire tests. Typically, several CBA ItemBuilder projects must be used for the application. Each CBA ItemBuilder project file provides one or more entry points called Tasks. For a test section you then need a list of ItemBuilder project files and the corresponding task names to administer them, for instance, in a linear sequence.

CBA ItemBuilder Project Files are zip archives that contain the following components (see also section 8.3.3):

  • A: The information required at design time for creating assessment components with the CBA ItemBuilder (i.e., for editing content). The files are only required for opening and modifying the assessment components with the CBA ItemBuilder and the files are not required at runtime (i.e., when using the assessment components to collect data).

  • B: Resource files (i.e., images, videos, and audio files) in web-supported formats that are imported using the CBA ItemBuilder to design pages. The file names of resource files are linked in the CBA ItemBuilder to components (i.e., the resource files are required for item editing and at runtime).

  • C: Embedded external resources ( i.e., HTML, JavaScript, and CSS files also in web-supported formats) integrated into pages with ExternalPageFrames / iframes are stored inside the zip archive. An HTML file is defined for each ExternalPageFrames / iframes as entry, but more files might be necessary.

  • D: A config.json that allows rendering the item content with the CBA ItemBuilder runtime is also stored in the zip archive. Only the config.json file and the two folders with the resources (resources) and the embedded resources (external-resources, that can contain sub-directories) are required for using the assessment components generated with the CBA ItemBuilder.

  • E: A file stimulus.json is also part of the CBA ItemBuilder project files that contains JSON-serialized, meta information about the tasks, such as the runtime version (runtimeCompatibilityVersion), the name (itemName) and the preferred size (itemWidth and itemHeight) as well as a list of all defined Tasks (tasks). This file also contains a list of required resource files (resources and externalResources) that allows pre-caching the item before rending.

Taskplayer API: The required Runtime to embed CBA ItemBuilder items into browser-based assessments is provided as a JavaScript file (main.js) and a CSS file (main.css) for each version of the CBA ItemBuilder. Since version 9.0 the interface of the Taskplayer API provided by the JavaScript runtime remained stable, while the internal implementation is changed and updated when new features are implemented in the CBA ItemBuilder. To render an CBA ItemBuilder project of a particular version using the config.json file together with the two folders (resources and external-resources), the same version of the CBA ItemBuilder runtime (i.e., main.js and main.css) is required.

For individual linear sequences, the runtime provides navigation between the tasks directly. If skip rules or adaptive tests are to be implemented, then several runtimes can be combined for the administration of individual tasks or packages of several tasks. This approach also allows implementing a delivery platform that can handle ItemBuilder tasks of different versions.

For programming a CBA ItemBuilder delivery, the following points must be considered and implemented:

  • Provision of Static Files: To use CBA ItemBuilder items, the resources (directories resources and external-resources) must be made available (e.g. via static hosting). This can be done via arbitrary URLs, which are communicated via the configuration of the runtime.

  • Configuration: Via URL parameters or with a structure cba_runtime_config declared in the global JavaScript scope (i.e. as window.cba_runtime_config) the runtime of the TaskPlayer API can be configured.

  • Caching of Snapshots: Browsers can be closed, and assessments should be able to be continued afterward as unchanged as possible. Tasks can also be exited and revisited as part of between-task navigation. For these requirements, the runtime provides the state of a task as a so-called snapshot, which the delivery software is expected to store and to provide for restoring the state of tasks. Therefore, for implementing a custom delivery, it is required to enable persistence of the snapshot data because these snapshots have to be made available to the TaskPlayer API for resuming and restoring tasks.

  • Storing of Provided Data: For a data collection with CBA ItemBuilder items using the TaskPlayer API, the following two types of data must be stored: At definable intervals, the TaskPlayer API transmits the collected log data (referred to as trace logs). These data have become the focus of scientific interest for the in-depth investigation of computer-based assessments and should always be stored. The direct results in (i.e., the so-called Item Scores) are provided by the TaskPlayer API when the Tasks are switched and must also be stored. Snapshots, trace data, and item scores are each assigned to a person-identifier and a task so that they can be easily post-processed afterward.

A description of an example implementation of an Execution Environment using the TaskPlayer-API is provided (see EE4Basic in section B.5) together with a technical documentation for developers (see Reference in section B.5).

The following subsections describe the Taskplayer API in detail, providing all information required to embed CBA ItemBuilder items into a custom web application and to collect trace, snapshot, and scoring data.

7.7.1 Taskplayer API: Initialization and Configuration

The Taskplayer API uses the HTML5 postMessage mechanism for bidirectional communication. The task player runs in an iframe and the host application (the Execution Environment) communicates with it by sending and receiving JSON messages.

Runtime Files: To render CBA ItemBuilder items, two files are required per CBA ItemBuilder version:

  • main.<version>.js – the JavaScript runtime
  • main.<version>.css – the CSS stylesheet

These files are loaded in an HTML page (the frame content) that serves as the iframe source for the task player.

Startup Configuration: The task player accepts configuration either as URL query parameters or via a global window.cba_runtime_config object (which takes precedence):

window.cba_runtime_config = {
    eventDomainUri: '*',              // Target origin for postMessages
    eventTargetWindow: 'parent'       // Send taskPlayerReady to parent window
};
Parameter Values Description
eventDomainUri URL or * Target origin for outgoing messages
eventTargetWindow self, parent, opener Window that receives the initial taskPlayerReady event

Initialization Sequence: The task player signals readiness by sending { eventType: 'taskPlayerReady' } to the configured target window. Only after receiving this event should the host application send configuration messages.

7.7.2 Taskplayer API: State Machine and Lifecycle

The task player operates as a state machine with four states:

Initial --> NotLoggedIn --> NoTaskRunning --> TaskRunning
                                  ^                |
                                  |  (stopTask)    |
                                  +----------------+
From State API Call To State Description
Initial taskPlayerReady (sent by player) NotLoggedIn Player is ready
NotLoggedIn setUserId NoTaskRunning Set user identifier
NoTaskRunning startTask TaskRunning Begin task execution
TaskRunning stopTask NoTaskRunning End task, triggers scoring

7.7.3 Taskplayer API: Adding Items

Before starting tasks, items must be registered with the task player using the addItem message. The itemConfig is the parsed content of the config.json file from the CBA ItemBuilder project ZIP archive:

// Add an item to the task player
playerFrame.contentWindow.postMessage(JSON.stringify({
    eventType: 'addItem',
    itemConfig: configJson,                 // Parsed config.json
    resourcePath: '/items/myItem/resources/',
    externalResourcePath: '/items/myItem/external-resources/',
    translationPath: '/items/myItem/translations/'
}), '*');
Parameter Description
itemConfig Parsed JSON from the item’s config.json file
resourcePath URL prefix for the resources/ folder (images, audio, video)
externalResourcePath URL prefix for the external-resources/ folder (HTML/JS)
translationPath URL prefix for the translations/ folder
libraryPathsMap Optional: { MathJax: '<url>' } for MathJax CDN or local path

7.7.4 Taskplayer API: Scaling Configuration

The display scaling of items can be configured:

playerFrame.contentWindow.postMessage(JSON.stringify({
    eventType: 'setScalingConfiguration',
    scalingMode: 'scale-up-down',           // Proportional scaling
    alignmentHorizontal: 'center',
    alignmentVertical: 'center'
}), '*');

Scaling modes: no-scaling, scale-up, scale-down, scale-up-down (see section 3.2.2 for details).

7.7.5 Taskplayer API: Starting and Stopping Tasks

Start a task: The scope parameter serves as a session identifier for the task execution. Re-using the same scope for a task will restore its previous state (snapshot).

playerFrame.contentWindow.postMessage(JSON.stringify({
    eventType: 'startTask',
    scope: 'session_001',
    item: 'MyItemName',
    task: 'task0'
}), '*');

Stop a task (triggers scoring computation):

playerFrame.contentWindow.postMessage(JSON.stringify({
    eventType: 'stopTask'
}), '*');

Pause and resume (e.g., for global timeouts):

playerFrame.contentWindow.postMessage(JSON.stringify({
    eventType: 'pauseTask'
}), '*');

playerFrame.contentWindow.postMessage(JSON.stringify({
    eventType: 'resumeTask'
}), '*');

7.7.6 Taskplayer API: Task Sequencing

For multi-task assessments, the task player can delegate navigation decisions to the host application:

// Register the host as task sequencer
playerFrame.contentWindow.postMessage(JSON.stringify({
    eventType: 'setTaskSequencer',
    targetWindowType: 'parent',
    targetOrigin: '*'
}), '*');

When the test-taker triggers a navigation command (e.g., NEXT_TASK), the task player sends a taskSwitchRequest to the host instead of handling it internally:

// Host receives:
{ eventType: 'taskSwitchRequest', request: 'nextTask', scope: '...', item: '...', task: '...' }
// request can be: 'nextTask', 'previousTask', 'cancelTask', 'goToTask'

The host application then decides which task to start next (or whether to end the assessment).

7.7.7 Taskplayer API: Collecting Trace Data

Trace data (log events) are collected automatically during task execution. The host must configure a transmission channel before starting tasks:

Option 1: HTTP POST (recommended for server-based applications):

playerFrame.contentWindow.postMessage(JSON.stringify({
    eventType: 'setTraceLogTransmissionChannel',
    channel: 'http',
    transmitUrl: 'https://myserver.com/api/trace',
    interval: 5000,                         // Send every 5 seconds
    httpTimeout: 10000
}), '*');

Option 2: postMessage (recommended for single-page applications):

playerFrame.contentWindow.postMessage(JSON.stringify({
    eventType: 'setTraceLogTransmissionChannel',
    channel: 'postMessage',
    targetWindowType: 'parent',
    targetOrigin: '*',
    interval: 5000
}), '*');

Option 3: Console (for development/debugging):

playerFrame.contentWindow.postMessage(JSON.stringify({
    eventType: 'setTraceLogTransmissionChannel',
    channel: 'console',
    interval: 0
}), '*');

Session identifier: Set a context ID that is included in all trace transmissions:

playerFrame.contentWindow.postMessage(JSON.stringify({
    eventType: 'setTraceContextId',
    contextId: 'person_42_session_1'
}), '*');

Trace data format: The transmitted JSON has this structure:

{
  "metaData": {
    "sessionId": "person_42_session_1",
    "userId": "user123",
    "timestamp": "2026-03-21T10:30:00.000Z",
    "version": "10.4"
  },
  "logEntriesList": [
    {
      "entryId": "unique_id",
      "timestamp": "2026-03-21T10:30:01.234Z",
      "type": "Button",
      "details": {
        "indexPath": "0/0/1",
        "userDefIdPath": "page1/panel1/button1",
        "userDefId": "button1",
        "clientX": 150, "clientY": 200
      }
    }
  ]
}

Common trace entry types include: UserLogin, ItemSwitch, TaskSwitch, Button, Checkbox, RadioButton, Link, SimpleTextField, Snapshot, JavaScriptInjected, and many more (one for each component type).

7.7.8 Taskplayer API: Collecting Scoring Results

Scoring results can be requested after stopping a task:

// Request scoring result
playerFrame.contentWindow.postMessage(JSON.stringify({
    eventType: 'getScoringResult',
    requestId: 'score_req_1'
}), '*');

// Host receives:
{
    eventType: 'getScoringResultReturn',
    requestId: 'score_req_1',
    result: { /* scoring data */ }
}

Scoring result structure: The result object contains name-value pairs:

{
  "totalResult": 1,
  "hitsCount": 2,
  "missesCount": 0,
  "hit.Q1_Correct": true,
  "hitWeighted.Q1_Correct": 1,
  "hitClass.Q1_Correct": "Var1",
  "hitText.Q1_Correct": "",
  "classFirstActiveHit.Var1": "Q1_Correct",
  "classResult.Var1": true,
  "nbUserInteractions": 5,
  "taskExecutionTime": 12345,
  "firstReactionTime": 1500,
  "resultVariables": [
    { "name": "Var1", "value": "Q1_Correct", "valueHit": "Q1_Correct", "valueLabel": "" }
  ]
}

Key fields in the scoring result:

Field Pattern Description
hit.<hitName> Boolean: is this hit active?
hitWeighted.<hitName> Weight of this hit (if active)
hitClass.<hitName> Class this hit belongs to
classFirstActiveHit.<className> Name of first active hit in class (sequential mode)
classResult.<className> Boolean: class scored as correct?
totalResult Overall result (1 = pass, 0 = fail)
nbUserInteractions Number of user interactions
taskExecutionTime Time in milliseconds
firstReactionTime Time to first interaction in milliseconds
resultVariables Array of class results with names, values, and labels

Scoring results from previous task executions can be retrieved using getOldScoringResult with additional scope, item, and task parameters.

7.7.9 Taskplayer API: Collecting Snapshots

Snapshots represent the complete state of a running task. They are included in the trace data (entry type Snapshot) and are essential for resuming tasks. When a task is stopped, the snapshot is cached by the task player. If the same task is started again with the same scope, the cached snapshot is used to restore the task state.

For external persistence (e.g., to survive browser restarts), snapshots must be extracted from the trace data and provided back to the task player when resuming.

7.7.10 Taskplayer API: Complete Integration Example

The following example shows the minimal JavaScript code required to embed a single CBA ItemBuilder task into a web page, collect scoring results, and receive trace data:

<!DOCTYPE html>
<html>
<head><title>CBA ItemBuilder Assessment</title></head>
<body>
    <iframe id="player" src="frameContent.html"
            style="width:1024px; height:768px; border:none;"></iframe>

    <script>
    var player = document.getElementById('player');
    var items = {};  // Store item configs

    // Listen for messages from the task player
    window.addEventListener('message', function(event) {
        var msg;
        try { msg = JSON.parse(event.data); } catch(e) { return; }

        switch(msg.eventType) {

            case 'taskPlayerReady':
                // Step 1: Configure trace transmission
                send({ eventType: 'setTraceContextId',
                       contextId: 'session_' + Date.now() });
                send({ eventType: 'setTraceLogTransmissionChannel',
                       channel: 'postMessage',
                       targetWindowType: 'parent',
                       targetOrigin: '*', interval: 5000 });

                // Step 2: Set user
                send({ eventType: 'setUserId', userId: 'testUser' });

                // Step 3: Configure scaling
                send({ eventType: 'setScalingConfiguration',
                       scalingMode: 'scale-up-down',
                       alignmentHorizontal: 'center',
                       alignmentVertical: 'center' });

                // Step 4: Register as task sequencer
                send({ eventType: 'setTaskSequencer',
                       targetWindowType: 'parent',
                       targetOrigin: '*' });

                // Step 5: Load items and start first task
                loadItemAndStart('MyItem', 'task0');
                break;

            case 'taskSwitchRequest':
                // Stop current task and get scoring
                send({ eventType: 'stopTask' });
                send({ eventType: 'getScoringResult',
                       requestId: 'score_1' });
                break;

            case 'getScoringResultReturn':
                console.log('Scoring:', msg.result);
                // Store scoring result, then start next task
                // or end the assessment
                break;

            case 'traceLogTransmission':
                // Receive and store trace data
                console.log('Trace:', msg.traceLogData);
                fetch('/api/trace', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify(msg.traceLogData)
                });
                break;
        }
    });

    function send(msg) {
        player.contentWindow.postMessage(JSON.stringify(msg), '*');
    }

    async function loadItemAndStart(itemName, taskName) {
        // Fetch the item's config.json
        var resp = await fetch('/items/' + itemName + '/config.json');
        var config = await resp.json();

        // Register item with task player
        send({
            eventType: 'addItem',
            itemConfig: config,
            resourcePath: '/items/' + itemName + '/resources/',
            externalResourcePath: '/items/' + itemName + '/external-resources/',
            translationPath: '/items/' + itemName + '/translations/'
        });

        // Start the task
        send({
            eventType: 'startTask',
            scope: 'session_001',
            item: itemName,
            task: taskName
        });
    }
    </script>
</body>
</html>

This example demonstrates the essential integration pattern. A production implementation would additionally handle error cases, implement a task sequence (administering multiple items), persist snapshots for test resumption, and store trace and scoring data on a server.