Javascript APIs

Occasionally, project requirements may call for custom web pages to be developed and integrated with Blocks. Such a web page can run as part of a Blocks structure, using a Web Block, or can run outside (and independently of) Blocks. Regardless of how the custom web page is deployed, it can still communicate with Blocks, giving you access to all Blocks functionality using a Javascript API.

The APIs described here provide access to all the same Blocks properties that you can use with buttons, sliders, text fields and other controls inside Blocks, and use the same websocket-based, bidirectional communication mechanism as used internally by Blocks.

There are two separate, but similar, such Javascript APIs available:

  • One for use from within custom web pages designed to be hosted in a Web Block.
  • Another one that can be used regardless of whether the web pages is used inside a Web Block or as a separate, independent web page.

Hosting your Custom Web Page

If desired, you can host your custom web page on your Blocks server unless your web page has specific server side needs not provided by Blocks (such as PHP server-side scripting).

Hosting Custom Web Content Inside a Block

If your web page is designed to be used through a Web Block, then you can place it inside the directory of the root block containing the Web Block. You can find this directory here:

public/block/GROUP_NAME/BLOCK_NAME

where GROUP_NAME and BLOCK_NAME is the name of the block group and block name in question. Create a directory inside that BLOCK_NAME called, for example, custom-html. Place your custom web page content in that directory. Make sure that the main HTML file is called index.html. Once all this is in place, you can specify the following URL in your Web Block:

~/custom-html

Where the tilde character (~) represents "the current block", and custom-html is the name of your custom directory therein. One advantage of this arrangement is that your custom web page becomes part of the block itself, and will be included if you export/import that block using a ZIP file from within Blocks editor.

Hosting Custom Web Content under /public

If your custom web content isn't specific to a particular block, or is designed to be used independently as a stand-alone web page, you can still host it on your Blocks server like this:

  1. Create a separate directory somewhere under the public directory found inside your Blocks root directory.
  2. Put your web page and any associated CSS and JS files inside that directory, making sure the main HTML file is called index.html.
  3. Open a browser with a URL like this:
http://NAME_OR_IP/public/YOUR_CUSTOM_WEB_DIRECTORY

where NAME_OR_IP is the domain name or IP address of your Blocks server (possibly followed by a port number if using a non-standard port, separated by a colon) and YOUR_CUSTOM_WEB_DIRECTORY is the name of the directory you created under the public directory, as described in point 1 above.

Publish/Subscribe Property Access

The main purpose of the Javascript APIs is to provide property access. Properties are used throughout Blocks to control most functions, so by setting such properties from your custom script, you can affect the state of all Blocks objects, such as Spots, Network Devices, Artnet/DMX devices, etc. The core functions providing such property access are common across both APIs, once you have access to the object providing this service. The method of accessing this object is different for the two APIs, and is described below. The common functions provided are:

  • subscribe to learn about when the value of a property changes.
  • unsubscribe to stop listening to value changes.
  • set for setting the value of a property. Will have no effect if the property is read-only or if access to the property is restricted to certain roles in Blocks.
  • add adjusts the value of a numeric property incrementally or appends to a string property.

subscribe

Subscribe to value of property at path, calling dataCallback.dataReceived when value changes. Returns the current value of propety, if known, else undefined. If synchronousCallback and the current value is known, then call dataCallback right away (before the function returns). Function and type declarations below use TypeScript syntax.

subscribe(
    path: string, // Property path
    dataCallback: DataCallback,
    synchronousCallback: boolean // Pass true to allow synchronous dataCallback
): any;

where DataCallback is an object that has a dataReceived function, thus implementing the following interface.

interface DataCallback {
    dataReceived(newValue: any, path: string): void
}

In some cases, the value of the property subscribed to may be available right away. If so, you will get access to that value by either of the following means:

  1. By the return value from the subscribe function. Unless this is undefined it holds the current value of the property
  2. By passing true as the synchronousCallback parameter to the subscribe function, thereby causing your dataReceived function to be called immediately, before subscribe returns.

unsubscribe

Stops dataCallback from receiving callbacks for changes to property at path.

unsubscribe(
    path: string,	// Property path - string
    dataCallback: DataCallback // The very same object as previously passed to subscribe
): void;

:!: NOTE: The dataCallback object passed to unsubscribe must be the same object that you previously passed to the corresponding subscribe function.

set

Set the value of property. The type of the value passed to this function must match the type of the property being set.

set(
    path: string, // Property path
    value: any // value to set (number, string, boolean matching the property's type)
): void;

add

Add (number) or append (string) value to property.

add(
    path: string, // Property path
    value: any // value to add (number) or append (string)
): void;

For a numeric property, specify the value by which to increase. To decrease, pass a negative value. For a string property, the value will be appended to the property. Not applicable for boolean properties.

Javascript API Usable from within a Web Block

Find the API integration script and an example HTML file here:

https://github.com/pixilab/blocks-clients/tree/master/pub-sub-iframe

To obtain the object on which you can call the functions described above, first import the PubSubWebBlock class, then instantiate an instance of this class like this

// Create our single host API connection
const pubSub = new PubSubWebBlock();

Then call the functions on the pubSub instance, as shown in the example index.html file included.

If you need access to this instance from other Javascript files/modules, you can store it in a global object, and then access it from there, as shown in the example index.html:

// Make available as a global to use from elsewhere
window.PIXILAB_BLOCKS.pubSub = pubSub;

Additional Web Block Interaction Capabilities

Custom code hosted inside a Web Block has some additional capabilities for talking to the enclosing Spot. Those are made available through the the browser's window.parent.postMessage function, passing various messages and parameters out of the IFRAME hosting the Web Block, then handled by the main Spot code.

action

Keeps any enclosing Attractor Block in its active state. This is useful since Blocks can not automatically detect user interaction, such as touch or mouse clicks, inside your custom web content. An Attractor often has a timeout that makes it revert to its passive state after some time of interactivity. Passing the action message to the enclosing Web Block prevents this from happening if all interaction is done inside the custom web page. You need to detect such interactions inside your own code in your custom web page, and then do the following when you detect activity:

window.parent.postMessage({type: 'action'}, '*');

set-location

Tells any enclosing Locator block to locate the Spot path or Location ID specified. The example shown below passes the number 12, which will be interpreted as a Location ID (since it's numeric).

window.parent.postMessage({type: 'set-location', data: "12"}, '*');

Alternatively, pass a Spot name or dot-separated spot path (if the target is inside spot groups), as the data parameter.

goto-block

Navigate to specified block path inside the current root block.

window.parent.postMessage({type: 'goto-block', data: "/path/to/block"}, '*');

go-back

Navigate back, just like the browser's BACK button.

window.parent.postMessage({type: 'go-back'}, '*');

set-tags

Set tags on the Spot, for use with Tag Selector.

window.parent.postMessage({type: 'set-tags', data: "tag1,tag2,en"}, '*');

Stand-alone Javascript API

This flavor of the API can be used independently of any surrounding Web Block; e.g., from a web page rendered outside of Blocks. Such a web page can still be served from the Blocks server, but is rendered as its own, top level web page. Since this is not running within the context of a Blocks Spot, it can not access any of the services provided by a Spot, as described above. In this case, your web page will use its own websocket connection to Blocks, as provided by this API.

:!: For a fully working example of this API, see this application note.

To use this method, first include the pub-sub-peer.js script into your web page:

<script src="pub-sub-peer.js"></script>

This load the API and establishes the global PIXILAB_BLOCKS object, which can subsequently be used to open a connection to Blocks, like this:

/**
 * Instantiate a PubSubPeer that auto-connects to the blocks server
 * using a websocket.
 *
 * The PIXILAB Websocket API is defined in the pub-sub-peer.js script, which
 * attaches its root object as a global (window) variable named PIXILAB_BLOCKS.
 */
const pubSubPeer = new PIXILAB_BLOCKS.PubSubPeer(
	onServerConnectionChange
);

This call returns a pubSubPeer that manages the websocket connection to the server.

The example shown above assumes that the custom web page is served by the Blocks server. If it is served from some other server, you must add a second parameter specifying the URL for the websocket endpoint on your Blocks server, like this;

const pubSubPeer = new PIXILAB_BLOCKS.PubSubPeer(
	onServerConnectionChange,
	"ws://<nameOrIp><:nonStandardPort>/rpc/pub-sub"
);

Replace <nameOrIp> with the IP number or resolvable name to your Blocks server. If you're using HTTPS to access your Blocks server, specify "wss:" as the protocol instead of "ws:". If you're running on a non-standard port, append the port number separated by a colon. Hence, if your Blocks server runs on 10.1.0.10 using the standard port, the call would look like this:

const pubSubPeer = new PIXILAB_BLOCKS.PubSubPeer(
	onServerConnectionChange,
	"ws://10.1.0.10/rpc/pub-sub"
);

The first parameter of the PubSubPeer constructor call is an optional callback function, in the example above called onServerConnectionChange, which will be called when the connection is established or broken. Implement this if you want to be notified of such events,

/**
 * The server connection state has changed. Here we just log that event, but you
 * may want to use it to indicate the server is temporarily offline, or similar.
 */
function onServerConnectionChange(connected) {
	console.log(connected ? 'Connected to server' : 'Disconnected from server');
}

The argument passed to this function is true when the connection is established and false if the connection is lost. The example above merely logs a message when this happens.

Once you have access to the pubSubPeer object and the connection has been established, you can call the same set, add, subscribe and unsubscribe functions as described above on the pubSubPeer object.

:!: IMPORTANT If you need to access the connection from multiple scripts/modules, you should use the same instance (called pubSubPeer above) to access the connection. You can accomplish this, for example, by storing the pubSubPeer in a global object, and then accessing it form there in other scripts/modules.