Concept
An editor that is embedded in your HTML pages can be interacted with using our JavaScript API. See Embedding an Editor in your own portals
There are many reasons a team would want to do this. Some examples are:
Updating variables on document load
Connecting to a custom widget
Building a partial or complete custom interface
Creating a “Save” button or an auto-save feature
Connection To CHILI publisher
You cannot communicate with the CHILI publisher directly due to the editor running in an iframe and browser security blocking JavaScript communication across iframes.
Therefore, if you want to utilize our JavaScript API across iframes you need to utilize our plugin: publisher-connector
You can install PublisherConnector via npm
npm i @chili-publish/publisher-connector
or through yarn
yarn add @chili-publish/publisher-connector
Or you can use the version from unpkg.com in your <script/> tags:
https://unpkg.com/@chili-publish/publisher-connector@latest/dist/PublisherConnector.min.js
Which one should I use?
If you utilize the NPM package, you will get all the benefits of TypeScript declaration files. However, you will need to use a module bundler like Webpack, Parcel, Rollup, etc. to package your JavaScript files for the web.
If you want to use this library in your integration without requiring a build phase, or you just want something simple, you can utilize the unpkg.com URL
Once you have decided, it is time to begin your integration. There are two steps:
Create an instance of the PublishConnector
Listen to the event for the CHILI document to be fully rendered
Create an instance of the PublishConnector
To create an instance of the PublisherConnector, you will need to use the .build()
method and pass in the iframe element.
Below is the code of a quite simple website where the iframe URL for CHILI publisher is already set.
<body> <iframe id="editor-iframe" style="width:1200px; height:800px" src="https://example.chili-publish.online/example/editor_html.aspx?doc=3d178228-a9b9-49d0-90d9-c1c8f8b67f05&apiKey=Sczs1ruhiZcaFiqg0G07gMFMq07X+SG2o8KlW8oAeZGvqoB1a0YvkbeZU1wJK15aIhANgZmhg+13NQlxpBEq7Q=="></iframe> <script type="module"> import {PublisherConnector} from 'https://unpkg.com/@chili-publish/publisher-connector@latest/dist/PublisherConnector.min.js'; const iframe = document.getElementById("editor-iframe"); (async () => { const publisherConnector = await PublisherConnector.build(iframe); })(); </script> </body>
Notice that the .build()
method returns a Promise. In the code above we utilize the async/await syntax, but we could also use the alternative then syntax.
<body> <iframe id="editor-iframe" style="width:1200px; height:800px" src="https://example.chili-publish.online/example/editor_html.aspx?doc=3d178228-a9b9-49d0-90d9-c1c8f8b67f05&apiKey=Sczs1ruhiZcaFiqg0G07gMFMq07X+SG2o8KlW8oAeZGvqoB1a0YvkbeZU1wJK15aIhANgZmhg+13NQlxpBEq7Q=="></iframe> <script type="module"> import {PublisherConnector} from 'https://unpkg.com/@chili-publish/publisher-connector@latest/dist/PublisherConnector.min.js'; const iframe = document.getElementById("editor-iframe"); PublisherConnector.build(iframe).then((connector)=> const publisherConnector = connector ); </script> </body>
Warning
It is important that the “build” method is called before the iframe is fully loaded. The is due to how our library relies on another library called Penpal. Failure to do this will lead to the library not being able to communicate with the iframe.
If you set the src
attribute of the iframe in your HTML and have a script tag that runs the build method all in the same (or almost) event loop, then everything will be fine. A good example of doing this wrong would be to call the “build” method after the onload event is fired off.
Listen to the event for the CHILI document to be fully rendered
As soon as the Promise from the .build()
method is resolved, you are good to begin interacting with the CHILI publisher editor. However, due to the loading of the document it may be wise to wait until the document has fully rendered before you start your interaction.
To do this, we need to add an event listener for the event DocumentFullyRendered. The event DocumentFullyRendered is fired off only when the CHILI document and workspace are completely drawn to the screen.
Now, this is not a web event, we must use a special method called .addListener()
. This method takes an event name (string) and a callback function to be called when the event is fired off.
await publisherConnector.addListener( "DocumentFullyRendered", target => console.log("document is rendered") );
The full JavaScript example would look like:
<body> <iframe id="editor-iframe" style="width:1200px; height:800px" src="https://example.chili-publish.online/example/editor_html.aspx?doc=3d178228-a9b9-49d0-90d9-c1c8f8b67f05&apiKey=Sczs1ruhiZcaFiqg0G07gMFMq07X+SG2o8KlW8oAeZGvqoB1a0YvkbeZU1wJK15aIhANgZmhg+13NQlxpBEq7Q=="></iframe> <script type="module"> import {PublisherConnector} from 'https://unpkg.com/@chili-publish/publisher-connector@latest/dist/PublisherConnector.min.js'; const iframe = document.getElementById("editor-iframe"); (async () => { const publisherConnector = await PublisherConnector.build(iframe); await publisherConnector.addListener( "DocumentFullyRendered", target => { // Do stuff } ); })(); </script> </body>
These method calls we are making will return an Exception if something goes wrong. Therefore, it is important to wrap your calls with a try/catch
<body> <iframe id="editor-iframe" style="width:1200px; height:800px" src="https://example.chili-publish.online/example/editor_html.aspx?doc=3d178228-a9b9-49d0-90d9-c1c8f8b67f05&apiKey=Sczs1ruhiZcaFiqg0G07gMFMq07X+SG2o8KlW8oAeZGvqoB1a0YvkbeZU1wJK15aIhANgZmhg+13NQlxpBEq7Q=="></iframe> <script type="module"> import {PublisherConnector} from 'https://unpkg.com/@chili-publish/publisher-connector@latest/dist/PublisherConnector.min.js'; const iframe = document.getElementById("editor-iframe"); (async () => { try { const publisherConnector = await PublisherConnector.build(iframe); await publisherConnector.addListener( "DocumentFullyRendered", target => { // Do stuff } ); } catch(e) { console.log(e); } })(); </script> </body>
There is an earlier event called DocumentFullyLoaded, this event is called when the CHILI document has been fully deserialized from the XML format into an in-memory JavaScript object. If you would like to listen for that, the method is similar:
<body> <iframe id="editor-iframe" style="width:1200px; height:800px" src="https://example.chili-publish.online/example/editor_html.aspx?doc=3d178228-a9b9-49d0-90d9-c1c8f8b67f05&apiKey=Sczs1ruhiZcaFiqg0G07gMFMq07X+SG2o8KlW8oAeZGvqoB1a0YvkbeZU1wJK15aIhANgZmhg+13NQlxpBEq7Q=="></iframe> <script type="module"> import {PublisherConnector} from 'https://unpkg.com/@chili-publish/publisher-connector@latest/dist/PublisherConnector.min.js'; const iframe = document.getElementById("editor-iframe"); (async () => { try { const publisherConnector = await PublisherConnector.build(iframe); await publisherConnector.addListener( "DocumentFullyLoaded", target => { // Do stuff - but be warned things can still change as they are being loaded } ); await publisherConnector.addListener( "DocumentFullyRendered", target => { // Do stuff } ); } catch(e) { console.log(e); } })(); </script> </body>
Working With CHILI publisher JavaScript API
If you have followed along, you should have a working example as an HTML file, and you can begin playing around with our JavaScript API.
You can check out pages like: Common JavaScript integration tasks, but you will notice they are using editor
or editorObject
and there is no Promise to be found in any of the examples. The reason it looks different is because all examples outside this one page communicate with CHILI publisher directly, meaning no iframe and no PublisherConnector. The PublisherConnector is just an interface that uses the postMessage()
method, but under the hood it utilizes the direct editorObject
.
You can find the source code that is running on the Online server here: https://github.com/chili-publish/publisher-connector/blob/main/onServer/chiliInternalWrapper.ts
Confused?
When CHILI publisher loads it adds an object to the window
called editorObject
, or its alias editor
. This object provides a series of methods that allow you to interact with the editor. So, you can access it via window.editorObject
. However, this object is unreachable if CHILI publisher is running in an iframe. Therefore, we built a postMessage()
interface called PublisherConnector which allows you to easily interact with the PublisherConnector object as though it was the editorObject
.
However, because postMessage()
works through events, there is a time delay, and therefore PublisherConnector object will return a Promise whereas the editorObject
returns the response immediately.
There are a few differences between the PublisherConnector interface and the direct editorObject
:
Naming Convention
Promise Return
Events
Naming Convention
A small, but important difference is that the methods from the PublisherConnector uses common JavaScript naming - camel case. This differs from the Pascal case used by the editorObject
.
So editorObject.GetObject()
becomes publisherConnector.getObject()
.
So editorObject.SetProperty()
becomes publisherConnector.setProperty()
.
Promise Return
While the editorObject
methods return without delay, the PublisherConnector uses postMessage()
. This means that the message must be serialized, sent, deserialized across from one window to another.
To make this easy, the PublisherConnector methods return a Promise.
So instead of:
const documentId = editorObject.GetObject("document.id"); console.log(documentId);
You would do:
publisherConnector.getObject("document.id").then( documentId => console.log(documentId) );
or use await
, in a function marked async
:
const documentId = await publisherConnector.getObject("document.id"); console.log(documentId);
Just like editorObject
methods, if something goes wrong, an Exception will be thrown.
Events
The PublisherConnector does events completely different from what is documented for the editorObject
.
To use events with the editorObject
, it involved calling the editorObject.AddListener()
method and then defining a function on window.OnEditorEvent()
method that took a string for events. The OnEditorEvent()
function would typically have a switch case or a series of if/else to determine which event was called.
PublisherConnector makes things much simpler. If you want to listen to an event and then do something, then name it and add a callback function.
await publisherConnector.addListener( "FrameMoveFinished", target => console.log("Frame moved with id " + target) );
Removing an event is pretty much the same.
await publisherConnector.removeListener("FrameMoveFinished");
Four Main Methods
Okay now that you understand the difference between the PublishConnector object and the editorObject
, let us look at the four main methods on both objects:
Listening to Events
Reading Properties
Setting Properties
Executing Functions
Listening to Events
You can listen to events using this method .addListener()
. The first parameter takes an event name (string) and a callback function to be called when the event is fired off.
PublisherConnector Object
await publisherConnector.addListener( "FrameMoveFinished", target => console.log("Frame moved with id " + target) );
editorObject Object
window.OnEventListener = (event, target) => { if (event == "FrameMovedFinished") { console.log("Frame moved with id " + target) } } editorObject.AddListener("FrameMoveFinished");
Reading Properties
To read properties of the document, you use the method .getObject()
which will return to you either a copy of the JavaScript object or a primitive. CHILI publisher converts the XML into a big JavaScript object starting at the document element with a all the child elements becoming properties.
So, if you wanted to get the full document object you could do this:
PublisherConnector Object
const chiliDoc = await publisherConnector.getObject("document");
editorObject Object
const chiliDoc = editorObject.GetObject("document");
The above calls will give you a shallow snapshot of the document. By shallow, we mean that it only goes one level deep down the key/value object tree. By snapshot, we mean that the object returned has no reference to the in-memory CHILI document object.
If you wanted to get the name, you could just then do .name
console.log("Document name is: " + chiliDoc.name)
You can also get the property directly by passing the string document.name
in the getObject()
method.
console.log("Document name is:" + await publisherConnector.getObject("document"));
The string document.name
is commonly referred to as CHILI path. It is the key/value path of the object.
For example, if you wanted to get the id of the first frame on page two.
console.log("First frame on page 2 id is:" + await publisherConnector.getObject("document.pages[1].frames[0]"));
Warning
The behavior and response of the .GetObject()
with a CHILI path is not always euqal as accessing it via the return object. For example, the below two examples do not return the same valueeditorObject.GetObject("document.pages") != editorObject.GetObject("document").pages
However, these two are the same
editorObject.GetObject("document.zoom") == editorObject.GetObject("document").zoom
Reading Properties
Okay we know how to read properties, but how do we set properties? We utilize the setProperty()
method. This method takes a CHILI path string, the property to set, and the value to set it to.
PublisherConnector Object
await publisherConnector.setProperty("document", "zoom", 100);
editorObject Object
editorObject.SetProperty("document", "zoom", 100);
Executing Functions
Remember that CHILI publisher serializes the XML to a JavaScript object. Well that object has methods/functions, which we can interact with via CHILI path.
We can do this utilizing the .executeFunction()
method. This method’s signature takes the CHILI path, the function name, and then a variable number of parameters based on the function you wish to call. The response will vary based on the function you wish to call.
So, for example, creating a new frame:
PublisherConnector Object
const newFrame = await publisherConnector.executeFunction("document.pages[1].frames","Add","rectangle","10mm","50mm","100mm","70mm");
editorObject Object
const newFrame = editorObject.ExecuteFunction("document.pages[1].frames","Add","rectangle","10mm","50mm","100mm","70mm");
While getting the XML of the document, it has no extra parameters:
const xml = await publisherConnector.executeFunction("document", "GetTempXML");
Unfortunately, unlike properties, functions on CHILI objects are not shown in the .getObject()
method. The most common function names can be found in this documentation, or contact support to see if there exist a function for what you are trying to achieve.