Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Last revision Both sides next revision
blocks:api:javascript [2023-04-17 12:33]
admin ↷ Page name changed from blocks:api:web-block to blocks:api:javascript
blocks:api:javascript [2023-04-18 16:14]
admin Complete
Line 1: Line 1:
-====== Javascript API ====== +====== Javascript APIs ====== 
-You can use a Web Block to show a web page by specifying its URL. If you have control over the code running in that web page, you can do some interaction from your custom web page hosted inside the //IFRAME// of the Web Block into Blocks itselfAlternatively, your custom web page can be presented independently of other Blocks contentwhile still being able to talk to your Blocks server.+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.
  
-:!: **HINT** If you have made the complete web page specifically for use from within a Web Blockyou may host that web page on the Blocks server. Just create a directory somewhere under /public and put your web page and associated CSS and JS files there, then access it using a URL specifying the path to the web page. If you placed your custom web page in a directory named MyWebPage under /publicand it contains a customary //index.html// file, you can show it by setting the URL of the Web Block to /public/MyWebPage.+The APIs described here provide access to all the same Blocks properties that you can use with buttonssliders, text fields and other controls inside Blocksand use the same websocket-basedbidirectional communication mechanism as used internally by Blocks.
  
-All interactions with Blocks from within your custom web page are done using the browser's //window.parent.postMessage// functionpassing various messages and parameters.+There are two separatebut similar, such Javascript APIs available:
  
-==== action ====+  * 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: 
 + 
 +<code> 
 +public/block/GROUP_NAME/BLOCK_NAME 
 +</code> 
 + 
 +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: 
 + 
 +<code> 
 +~/custom-html 
 +</code> 
 + 
 +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: 
 + 
 +  - Create a separate directory somewhere under the //public// directory found inside your Blocks root directory.  
 +  - Put your web page and any associated CSS and JS files inside that directory, making sure the main HTML file is called //index.html//
 +  - Open a browser with a URL like this: 
 + 
 +<code> 
 +http://NAME_OR_IP/public/YOUR_CUSTOM_WEB_DIRECTORY 
 +</code> 
 + 
 +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. 
 + 
 +<code> 
 +subscribe( 
 +    path: string, // Property path 
 +    dataCallback: DataCallback, 
 +    synchronousCallback: boolean // Pass true to allow synchronous dataCallback 
 +): any; 
 +</code> 
 + 
 +where DataCallback is an object that has a dataReceived function, thus implementing the following interface. 
 + 
 +<code> 
 +interface DataCallback { 
 +    dataReceived(newValue: any, path: string): void 
 +
 +</code> 
 + 
 +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: 
 + 
 +  - By the return value from the //subscribe// function. Unless this is //undefined// it holds the current value of the property 
 +  - 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. 
 + 
 +<code> 
 +unsubscribe( 
 +    path: string, // Property path - string 
 +    dataCallback: DataCallback // The very same object as previously passed to subscribe 
 +): void; 
 +</code> 
 + 
 +:!: **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. 
 + 
 +<code> 
 +set( 
 +    path: string, // Property path 
 +    value: any // value to set (number, string, boolean matching the property's type) 
 +): void; 
 +</code> 
 + 
 +=== add === 
 +Add (number) or append (string) value to property. 
 + 
 +<code> 
 +add( 
 +    path: string, // Property path 
 +    value: any // value to add (number) or append (string) 
 +): void; 
 +</code> 
 + 
 +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 
 + 
 +<code> 
 +// Create our single host API connection 
 +const pubSub = new PubSubWebBlock(); 
 +</code> 
 + 
 +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: 
 + 
 +<code> 
 +// Make available as a global to use from elsewhere 
 +window.PIXILAB_BLOCKS.pubSub = pubSub; 
 +</code> 
 + 
 +==== 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:  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: 
  
Line 13: Line 145:
 </code> </code>
  
-==== set-location ====+=== 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). 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).
  
Line 22: Line 154:
 Alternatively, pass a Spot name or dot-separated spot path (if the target is inside spot groups), as the //data// parameter. Alternatively, pass a Spot name or dot-separated spot path (if the target is inside spot groups), as the //data// parameter.
  
-==== goto-block ====+=== goto-block ===
 Navigate to specified block path inside the current root block. Navigate to specified block path inside the current root block.
  
Line 29: Line 161:
 </code> </code>
  
-==== go-back ====+=== go-back ===
 Navigate back, just like the browser's BACK button. Navigate back, just like the browser's BACK button.
  
Line 36: Line 168:
 </code> </code>
  
-==== set-tags ====+=== set-tags ===
 Set tags on the Spot, for use with Tag Selector. Set tags on the Spot, for use with Tag Selector.
  
Line 43: Line 175:
 </code> </code>
  
 +===== 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 [[blocks:app-note:interacting-from-a-custom-web-page|this application note]].
 +
 +To use this method, first include the pub-sub-peer.js script into your web page:
 +
 +<code>
 +<script src="pub-sub-peer.js"></script>
 +</code>
 +
 +This load the API and establishes the global PIXILAB_BLOCKS object, which can subsequently be used to open a connection to Blocks, like this:
 +
 +<code>
 +/**
 + * 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
 +);
 +</code>
 +
 +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;
 +
 +<code>
 +const pubSubPeer = new PIXILAB_BLOCKS.PubSubPeer(
 + onServerConnectionChange,
 + "ws://<nameOrIp><:nonStandardPort>/rpc/pub-sub"
 +);
 +</code>
 +
 +Replace <nameOrIp> with the IP number or resolvable name to your Blocks server. If you're using HTTPS to access your Blockss 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 rus on 10.1.0.10 using the standard port, the call would look like this:
 +
 +<code>
 +const pubSubPeer = new PIXILAB_BLOCKS.PubSubPeer(
 + onServerConnectionChange,
 + "ws://10.1.0.10/rpc/pub-sub"
 +);
 +</code>
 +
 +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,
 +
 +<code>
 +/**
 + * 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');
 +}
 +</code>
 +
 +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.