Cadenza JS - v10.5.5-dev

Cadenza JS is a JavaScript library to use the disy Cadenza APIs conveniently without having to deal with technical details like parameter encoding or the postMessage() Web API.

Cadenza JS is included in the Cadenza distribution in the corresponding version.

Alternatively you can install the most recent version for a particular Cadenza Release using npm:

npm install @disy/cadenza.js@~10.2.0 # For latest version for Cadenza 10.2 

The Cadenza main version is reflected in the corresponding major and minor version of Cadenza JS (e.g. 10.2.0 for Cadenza 10.2), while the last version segment is increased for both, bugfixes and functional changes.

For Cadenza 10.1 and earlier versions Cadenza JS used did use genuine semantic versioning. Please consult the Cadenza Documentation for the corresponding major and minor version of cadenza.js.

This section features usage examples for Cadenza JS. For detailed usage information, see the "API:" links.

API: cadenza()

There are two ways to use Cadenza JS: As a module and globally.

Create an instance of the Cadenza client by calling cadenza() with the base URL of your Cadenza instance. Pass an iframe in the options if you want to show Cadenza in an iframe.

<iframe id="cadenza-iframe"></iframe>
import { cadenza } from '@disy/cadenza.js';

const cadenzaClient = cadenza({
baseUrl: '<baseUrl>',
iframe: 'cadenza-iframe',
});

Tip: If you develop your application in TypeScript - Cadenza JS is typed using JSDoc and also comes with a cadenza.d.ts type definition file.

<script type="module" src="./cadenza.js">
const cadenzaClient = window.cadenza(...);

The type="module" has the effect that script execution is deferred. You might need to wait for the DOMContentLoaded event in order to use Cadenza JS.

window.addEventListener('DOMContentLoaded', () => {
const cadenzaClient = window.cadenza(...);
...
});

If for some reason you don't like the global cadenza field, you can remove it like so:

const cadenza = window.cadenza.noConflict();

If your application is opened from inside Cadenza (e.g. in a popup), you can send commands to parent Cadenza window. To do that, create CadenzaClient with no arguments:

const parentClient = window.cadenza();

Warning: Not every operation can be done when dealing with parent Cadenza. For example reload and expandNavigator work, but show, showMap etc. require an iframe. Using these functions in this mode will result in errors.

API: CadenzaClient#show

Show an embedding target in an iframe and ...

  • Hide Cadenza's main header and footer and the workbook toolbar.
  • Enable the simplified operation mode.
  • Disable the designer.
  • Set the filter variable "var1" to "foo".
cadenzaClient.show('<embeddingTargetId>', {
hideMainHeaderAndFooter: true,
hideWorkbookToolBar: true,
operationMode: 'simplified',
disabledUiFeatures: ['workbook-design'],
filter: {
var1: 'foo'
}
});

If a spatial filter is defined on the embedded target, it can be set with a dedicated variable name '$spatial':

cadenzaClient.show('<embeddingTargetId>', {
// ...
filter: {
'$spatial': {
'spatialRelation': 'overlaps', // Also: 'notOverlaps', 'contains'
'geometry': { // GeoJSON geometry; Restricted to type 'Polygon'
'coordinates': [
[
[ 9.59, 52.70 ],
[ 8.89, 51.72 ],
[ 10.90, 51.72 ],
[ 9.59, 52.70 ]
]
],
'type': "Polygon"
}
}
}
});
  • If the embedding target cannot be resolved, a 404 page is shown to the user.
  • Cadenza JS does not handle user authentication: If the user is not already logged in, the normal authentication flow of Cadenza will run. By default, the login page would be shown to the user.

Views of type "JasperReports report" can be shown in an iframe like any other view. Additionally, there is an option to show only the generated PDF without any Cadenza footers or headers. This is done by setting the "dataType" option to "pdf".

cadenzaClient.show('<embeddingTargetId>', {
dataType: 'pdf'
});

Embedding targets of type report are shown as PDF. Additionally, if the report template of the embedding target contains worksheet placeholder elements, they will be replaced by the worksheets according to worksheetPlaceholders parameter. In the example below, the worksheet placeholder with the ID "placeholder1" will be replaced with a worksheet "First worksheet" from the workbook of the embedding target.

cadenzaClient.show('<embeddingTargetId>', {
worksheetPlaceholders: {
placeholder1: 'First worksheet'
}
});

Cadenza JS uses the AbortController Web API for aborting requests. This is supported by most of the public methods.

const abortController = new AbortController();
try {
await cadenzaClient.show('<embeddingTargetId>', { signal: abortController.signal });
} catch (error) {
if (error.name === 'AbortError') {
console.log('Iframe loading was aborted');
}
}

cancelButton.onclick = () => abortController.abort();

Tip: You can use the same AbortController to abort multiple requests, e.g. when embedding Cadenza in multiple iframes.

API: CadenzaClient#setFilter

Set filter variables using a mapping of variable names to values.

cadenzaClient.setFilter({ '<variableName>': 'value' });

Passing null as value removes the assigned filter value.

If a spatial filter is defined on the embedded target, it can be set with a dedicated variable name '$spatial':

cadenzaClient.setFilter({
'$spatial': {
"spatialRelation": "overlaps", // Also: "notOverlaps", "contains"
"geometry": { // GeoJSON geometry; Restricted to type "Polygon"
"coordinates": [
[
[9.59, 52.70],
[8.89, 51.72],
[10.90, 51.72],
[9.59, 52.70]
]
],
"type": "Polygon"
}
}
});

API: CadenzaClient#showMap

Show the embedding target of a workbook map view in an iframe and ...

  • Set the initial map extent.
  • Show the given GeoJSON geometry on the map.

The coordinates of extent and geometry are in the map's SRS (useMapSrs: true).

cadenzaClient.showMap('<embeddingTargetId>', {
useMapSrs: true,
extentStrategy: {
type: 'static',
extent: [
-572_513.341856,
5_211_017.966314,
916_327.095083,
6_636_950.728974,
]
},
geometry: {
type: 'Point',
coordinates: [328_627.563458, 5_921_296.662223],
},
});
cadenzaClient.showMap('<embeddingTargetId>', {
extentStrategy: {
type: 'locationFinder',
query: 'Karlsruhe',
}
});

API: CadenzaClient#setLayerVisibility

To set the visibility of a layer in the currently shown map, pass the layer path or print name and the desired visibility.

cadenzaClient.setLayerVisibility('<layerPrintName>', false);

API: CadenzaClient#getData

The method may support multiple data types in the future. Currently, only "png" is supported to get the image of a workbook map view. You may pass the desired width and height in px and whether the resulting image should include the scale.

const canvas = document.querySelector('canvas');
const data = await cadenzaClient.getData('png', { width: 1200, height: 800, withScale: true });
canvas.drawImage(await createImageBitmap(data), 0, 0);

API: CadenzaClient#editGeometry, CadenzaClient#on

Edit a GeoJSON geometry with a workbook map view in the background. The geometry coordinates are in the map's SRS (useMapSrs: true).

const geometry = {
type: 'Point',
coordinates: [328_627.563458, 5_921_296.662223],
};
cadenzaClient.editGeometry('<embeddingTargetId>', geometry, {
useMapSrs: true,
});

cadenzaClient.on('editGeometry:update', (event) => {
console.log('Geometry was updated', event.detail);
});
cadenzaClient.on('editGeometry:ok', (event) => {
console.log('Geometry editing was completed', event.detail);
});
cadenzaClient.on('editGeometry:cancel', (event) => {
console.log('Geometry editing was cancelled');
});

API: CadenzaClient#batchEditGeometry, CadenzaClient#on

Edit a collection of geometries with a workbook map view in the background. The geometry coordinates are in the map's SRS (useMapSrs: true). The editor will create an additional layer where editable geometries are stored and selectable for editing. The layer's print name is 'Editor Layer' and can be used by other API actions. Additional geometries can also be created from within the editor once it has been initialized. When finished, all geometries are collected and returned in a single collection.

Note: The last geometry in the provided collection is initially selected for editing. At least one geometry must be provided.

const feature1 = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [328_627.563458, 5_921_296.662223],
}
};

const feature2 = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [916_327.095083, 6_636_950.728974],
}
};

const featureCollection = {
type: 'FeatureCollection',
features: [feature1 , feature2]
}

cadenzaClient.batchEditGeometry('<embeddingTargetId>', featureCollection, {
useMapSrs: true,
});

cadenzaClient.on('editGeometry:update', (event) => {
console.log('Geometry was updated', event.detail);
});
cadenzaClient.on('editGeometry:create', (event) => {
console.log('At least one geometry feature was created', event.detail);
});
cadenzaClient.on('editGeometry:edit', (event) => {
console.log('Geometry feature was edited', event.detail);
});
cadenzaClient.on('editGeometry:delete', (event) => {
const deletedFeatures = event.details.objectIds;
...
});
cadenzaClient.on('editGeometry:ok', (event) => {
console.log('Geometry editing was completed', event.detail);
});
cadenzaClient.on('editGeometry:cancel', (event) => {
console.log('Geometry editing was cancelled');
});

The on() method returns an unsubscribe function.

const unsubscribe = cadenzaClient.on('editGeometry:ok', (event) => ...);
...
unsubscribe();

API: CadenzaClient#createGeometry

Create a GeoJSON point geometry with a workbook map view in the background. The geometry coordinates are in the map's SRS (useMapSrs: true).

cadenzaClient.createGeometry('<embeddingTargetId>', 'Point', {
useMapSrs: true,
});

cadenzaClient.on('editGeometry:ok', (event) => {
console.log('Geometry editing was completed', event.detail);
});

Note: Under the hood, creating a geometry is similar to editing a geometry. That's why the events use the editGeometry prefix.

API: CadenzaClient#batchCreateGeometry, CadenzaClient#on

Create multiple geometries with a workbook map view in the background. The geometry coordinates are in the map's SRS (useMapSrs: true). The editor will create an additional layer where created geometries are stored and selectable for editing. The layer's print name is 'Editor Layer' and can be used by other API actions. When finished, all geometries are collected and returned in a single collection.


cadenzaClient.batchCreateGeometry('<embeddingTargetId>', 'Point', {
useMapSrs: true,
});

cadenzaClient.on('editGeometry:update', (event) => {
console.log('Geometry was updated', event.detail);
});
cadenzaClient.on('editGeometry:create', (event) => {
console.log('At least one geometry feature was created', event.detail);
});
cadenzaClient.on('editGeometry:edit', (event) => {
console.log('Geometry feature was edited', event.detail);
});
cadenzaClient.on('editGeometry:delete', (event) => {
const deletedFeatures = event.details.objectIds;
...
});
cadenzaClient.on('editGeometry:ok', (event) => {
console.log('Geometry editing was completed', event.detail);
});
cadenzaClient.on('editGeometry:cancel', (event) => {
console.log('Geometry editing was cancelled');
});

Create a GeoJSON polygon geometry with a workbook map view and some additional background layers.

IMPORTANT: The Cadenza referenced with cadenzaClient must be configured to support the import of GeoJSON, and the (system) privileges of the corresponding user must also be set in such a way that the import of GeoJSON is possible.

cadenzaClient.createGeometry('<embeddingTargetId>', 'Polygon', {
additionalLayers: [
{ type: 'geojson', name: 'Example', content: <FeatureCollection> },
...
]
});

API: CadenzaClient#selectObjects

Ask the user to select objects in a workbook map view. In the example the selection is restricted to specific layers. For layers in groups, pass the layer path.

cadenzaClient.selectObjects('<embeddingTargetId>', {
layers: [
[ '<layerGroupPrintName>', '<layerPrintName>' ],
[ '<layerPrintName>' ]
]
});

cadenzaClient.on('selectObjects:ok', (event) => {
console.log('Object selection was completed', event.detail.selection);
});

API: CadenzaClient#setSelection

Set the selection in the currently shown workbook map view using a list of object IDs to select. An empty list clears the selection.

cadenzaClient.setSelection(
layer: '<layerPrintName>',
values: [ 'objectId', ... ]
);

You can also add to or remove from the current selection:

API: CadenzaClient#show

Show an embedding target in an iframe and highlight an item in the navigator. Additionally, expand the navigator tree.

cadenzaClient.show('<embeddingTargetId>', {expandNavigator: true, highlightGlobalId: 'ROOT.MyFolder'});

API: CadenzaClient#show

Show Cadenza's welcome page in an iframe and highlight an item in the navigator.

cadenzaClient.show({page: 'welcome'}, {highlightGlobalId: 'ROOT.MyFolder'});

API: CadenzaClient#expandNavigator

Expand the navigator.

cadenzaClient.expandNavigator();

API: CadenzaClient#fetchData

Fetch data from a workbook view in CSV format.

const response = await cadenzaClient.fetchData('<embeddingTargetId>', 'csv');

const text = await response.text();
...

Fetch data from a workbook view in JSON format and include only the data values and the aggregation totals. (Do not include the column names.)

const response = await cadenzaClient.fetchData('<embeddingTargetId>', 'json', {
parts: ['values', 'totals']
});

const tableData = await response.json();
...

API: [CadenzaClient#fetchObjectInfo(./classes/CadenzaClient.html#fetchObjectInfo)

Fetch the object info from a workbook map view in JSON format. The result contains all information, that is shown in the object info within cadenza.

const objectInfo = await cadenzaClient.fetchObjectInfo('embeddingTargetId', 'layerPrintName', [['objectId']], {
useMapSrs: false,
fullGeometries: true
});

API: [CadenzaClient#fetchObjectInfo(./classes/CadenzaClient.html#fetchAreaIntersections)

Fetch the intersection area from a workbook map view layer in JSON format for a given area. The result contains all intersecting objects of the layer, their object ID, if defined the object name and a geometry representing the intersection area, with area size and area perimeter.

If the geometry intersection fails because a geometry is invalid, a problem detail object is returned. In all other error cases, an exception is thrown.

const geometry = {
type: 'Point',
coordinates: [328_627.563458, 5_921_296.662223],
};
const bufferSize = {
value: 100,
lengthUnit: 'm'
}

const areaIntersectionsResult = await cadenzaClient.fetchAreaIntersections('embeddingTargetId', 'layerPrintName', geometry, {
useMapSrs: true,
bufferSize: bufferSize,
useAutoCorrection: true,
includeGeometryValidationReport: true
});

Response with activated autocorrection and geometry validation report

{
"results": {
"features": [
{
"objectId": [ 333 ],
"geometry": {
"type": "Polygon",
"coordinates": [<coordinates>]
},
"area": 5209.274047788233,
"type": "Feature"
}
],
"type": "FeatureCollection"
},
"errors": {
"features": [
{
"objectId": [ 333 ],
"geometry": {
"type": "Point",
"coordinates": [ 457172.35, 5427744.68]
},
"properties": {
"category": "Ring Self-intersection",
"message": "Ring Self-intersection at or near point (457172.35, 5427744.68, NaN)"
},
"type": "Feature"
}
],
"type": "FeatureCollection"
}
}

API: CadenzaClient#downloadData

Download data from a workbook view in Excel format. This triggers the browser's download dialog.

const button = document.createElement('button');
button.textContent = 'Download Excel';
button.onclick = () => cadenzaClient.downloadData('<embeddingTargetId>', 'excel');

API: CadenzaClient#reload

To reload a worksheet:

cadenzaClient.reload();

To invalidate caches and reload:

cadenzaClient.reload({ invalidateCaches: true });

API: CadenzaClient#closeMe

Sends a message to parent Cadenza window to close the window containing this application.

cadenzaClient.closeMe();

Cadenza does not send an event when the dialog or window is closed that embeds the custom application. Depending on your use case you might listen to these browser events instead:

Note: If you need to send a request to your server when the window/dialog is closed, you should use the Navigator: sendBeacon() method. It's limited to POST requests, but in contrast to the "normal" XHR or fetch() APIs, it works reliably when the dialog/window is closed.

The development sandbox is a simple custom application (in fact a single .html file) for playing with Cadenza JS.

To run it ...

The command starts a proxy server that serves the sandbox.html and cadenza.js files and proxies all other requests to the Cadenza server. Since that way the sandbox and Cadenza run on the same origin, embedding and postMessage communication just work.

Alternatively ...

  • Set system property or environment variable "CADENZA_ENABLE_EMBEDDING_SANDBOX" in Cadenza to enable the sandbox.
  • Now, sandbox.html is served on http://localhost:8080/cadenza/sandbox (replace with custom Cadenza url /sandbox as needed).

cadenza.js is automatically served when Cadenza starts.

npm run sandbox also opens the sandbox in a new browser window. Select the Cadenza JS method you want to play with in the select box at the top. For each method there are controls to define the parameters. Hit "Go!" to call the method.

The sandbox uses Cadenza JS with the "debug" option enabled, so you can see what is going on internally in the browser's devtools console. In the "Network" tab of the devtools you can see the requests to the Cadenza server.

By default, the sandbox expects Cadenza to run on http://localhost:8080/cadenza. There are two ways to customize the Cadenza URL:

  • Pass an argument:
    npm run sandbox -- --cadenza-url http://localhost:8000/my-cadenza
    
  • Set an environment variable:
    export CADENZA_URL=http://localhost:8000/my-cadenza
    npm run sandbox

JSON is a simple data-interchange format, which is also used in the Cadenza API. It does not support all the attribute types of Cadenza objects, that's why there are some rules for representing Cadenza object data in JSON.

Cadenza Attribute Type JSON Type JSON Example Value Notes
Text (String) string "Text"
Number (Integer) number 1
Number (Long) string "1" The long value range exceeds the range of the JSON number type. So it's represented as a string.
Floating point number (Double) number 1.23
Floating point number (Big decimal) string "1.23" The big decimal value range exceeds the range of the JSON number type. So it's represented as a string.
Date string "1999-12-31T23:00:00Z" A date is represented as an ISO string in universal time (UTC).
Duration number 1 A duration is represented by its numeric value.
Geometry object
{
"type": "Point"
"coordinates": [125.6, 10.1]
}
A geometry is represented as a GeoJSON object.
Note: By default, coordinates in GeoJSON use the WGS84 projection.
IP address string "127.0.0.1"
URL string "http://example.com"
LOB string