@disy/cadenza.js 2.2.4 β†’ 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +16 -5
  2. package/README.md +1 -322
  3. package/apidoc/assets/highlight.css +0 -7
  4. package/apidoc/assets/main.js +2 -2
  5. package/apidoc/assets/navigation.js +1 -1
  6. package/apidoc/assets/search.js +1 -1
  7. package/apidoc/assets/style.css +46 -15
  8. package/apidoc/classes/AbortError.html +3 -3
  9. package/apidoc/classes/CadenzaClient.html +65 -86
  10. package/apidoc/classes/CadenzaError.html +8 -6
  11. package/apidoc/functions/cadenza.html +4 -4
  12. package/apidoc/index.html +152 -9
  13. package/apidoc/interfaces/CadenzaEvent.html +6 -6
  14. package/apidoc/interfaces/ExternalLinkKey.html +6 -3
  15. package/apidoc/interfaces/Geometry.html +2 -2
  16. package/apidoc/interfaces/PageSource.html +2 -2
  17. package/apidoc/modules.html +14 -2
  18. package/apidoc/types/CadenzaChangeSelectionEvent.html +2 -0
  19. package/apidoc/types/CadenzaDrillThroughEvent.html +7 -0
  20. package/apidoc/types/CadenzaEditGeometryCancelEvent.html +2 -0
  21. package/apidoc/types/CadenzaEditGeometryOkEvent.html +2 -0
  22. package/apidoc/types/CadenzaEditGeometryUpdateEvent.html +2 -0
  23. package/apidoc/types/CadenzaErrorEvent.html +2 -2
  24. package/apidoc/types/CadenzaEventByType.html +1 -0
  25. package/apidoc/types/CadenzaEventType.html +2 -0
  26. package/apidoc/types/CadenzaObjectInfoEvent.html +2 -0
  27. package/apidoc/types/CadenzaSelectObjectsCancelEvent.html +2 -0
  28. package/apidoc/types/CadenzaSelectObjectsOkEvent.html +2 -0
  29. package/apidoc/types/DataType.html +3 -2
  30. package/apidoc/types/EmbeddingTargetId.html +9 -2
  31. package/apidoc/types/Extent.html +1 -1
  32. package/apidoc/types/FilterVariables.html +5 -2
  33. package/apidoc/types/GeometryType.html +1 -1
  34. package/apidoc/types/GlobalId.html +2 -2
  35. package/apidoc/types/OpaqueString.html +6 -0
  36. package/apidoc/types/OperationMode.html +1 -1
  37. package/apidoc/types/TablePart.html +1 -1
  38. package/apidoc/types/UiFeature.html +3 -3
  39. package/apidoc/types/WorkbookLayerPath.html +3 -0
  40. package/cadenza.d.ts +274 -50
  41. package/cadenza.js +339 -69
  42. package/package.json +6 -6
  43. package/sandbox.html +197 -31
package/cadenza.js CHANGED
@@ -24,13 +24,42 @@ globalThis.cadenza = Object.assign((/** @type Parameters<cadenza> */ ...args) =>
24
24
  return cadenza;
25
25
  },
26
26
  });
27
- /** @typedef {string} EmbeddingTargetId - The ID of an embedding target */
28
- /** @typedef {string} GlobalId - The ID of a navigator item */
27
+ /**
28
+ * @template {string} T
29
+ * @typedef {string & {__type: T}} OpaqueString - A specific `string` type that is not assignable from another string
30
+ *
31
+ * The idea is to have a specific type e.g. for the {@link EmbeddingTargetId} instead of a plain `string`.
32
+ * You don't need to _actually_ add that `__type` property. In TS code, just use a
33
+ * [type assertion](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions)
34
+ * (e.g. `cadenzaClient.show('{embeddingTargetId}' as EmbeddingTargetId)`).
35
+ */
36
+ /**
37
+ * @typedef {OpaqueString<'EmbeddingTargetId'>} EmbeddingTargetId - The ID of a Cadenza embedding target
38
+ *
39
+ * Embedding targets are called πŸ‡©πŸ‡ͺ "Einbettbarer Inhalt" / πŸ‡ΊπŸ‡Έ "Embeddable content" throughout the Cadenza UI and help.
40
+ * They're managed within the respective workbook:
41
+ *
42
+ * - πŸ‡©πŸ‡ͺ "Mehr" > "Arbeitsmappe verwalten" > "Einbettung"
43
+ * - πŸ‡ΊπŸ‡Έ "More" > "Manage workbook" > "Embedding"
44
+ *
45
+ * The name of an embedding target (as entered in the UI) is its ID.
46
+ */
47
+ /** @typedef {OpaqueString<'GlobalId'>} GlobalId - The ID of a navigator item */
29
48
  /**
30
49
  * @typedef ExternalLinkKey - A tuple qualifying a Cadenza external link
50
+ *
51
+ * You get the `repositoryName` and `externalLinkId` from the URL of the external link's page in the Cadenza management center:
52
+ * ```
53
+ * {baseUrl}/admin/repositories/{repositoryName}/external-links/{externalLinkId}?...
54
+ * ```
55
+ *
31
56
  * @property {string} repositoryName - The name of the link's repository
32
57
  * @property {string} externalLinkId - The ID of the external link
33
58
  */
59
+ /**
60
+ * @typedef {string[]} WorkbookLayerPath - Identifies a layer within a workbook map view
61
+ * using the print names of the layer and - if the layer is grouped - its ancestors
62
+ */
34
63
  /**
35
64
  * @typedef PageSource - A well-known Cadenza page
36
65
  * @property {'welcome'} page - The name of the page (Only `"welcome"` is currently supported.)
@@ -40,8 +69,8 @@ globalThis.cadenza = Object.assign((/** @type Parameters<cadenza> */ ...args) =>
40
69
  * @typedef {'workbook-design'|'workbook-view-management'} UiFeature - The name of a Cadenza UI feature
41
70
  *
42
71
  * _Note:_ Supported features are:
43
- * * 'workbook-design' - The workbook designer
44
- * * 'workbook-view-management' - Add/Edit/Remove workbook views (Is included in 'workbook-design'.)
72
+ * * `"workbook-design"` - The workbook designer
73
+ * * `"workbook-view-management"` - Add/Edit/Remove workbook views (Is included in 'workbook-design'.)
45
74
  * */
46
75
  /**
47
76
  * @typedef Geometry - A [GeoJSON](https://geojson.org/) geometry object
@@ -53,9 +82,20 @@ globalThis.cadenza = Object.assign((/** @type Parameters<cadenza> */ ...args) =>
53
82
  * _Note:_ The GeoJSON geometry type "GeometryCollection" is currently not supported.
54
83
  */
55
84
  /** @typedef {[number,number,number,number]} Extent - An array of numbers representing an extent: [minx, miny, maxx, maxy] */
56
- /** @typedef {'csv' | 'excel' | 'json' | 'pdf'} DataType - A data type */
85
+ /**
86
+ * @typedef {'csv' | 'excel' | 'json' | 'pdf' | 'png'} DataType - A data type
87
+ *
88
+ * See [JSON Representation of Cadenza Object Data](../index.html#md:json-representation-of-cadenza-object-data) for JSON data.
89
+ */
57
90
  /** @typedef {'columns' | 'values' | 'totals'} TablePart - A part of a table to export */
58
- /** @typedef {Record<string, string | number | Date>} FilterVariables - Filter variable names and values */
91
+ /**
92
+ * @typedef {Record<string, string | string[] | number | Date | null>} FilterVariables - Filter variable names and values
93
+ *
94
+ * Variables of type String, Integer, Long, Double and Date can be set.
95
+ *
96
+ * _Note:_ Since numbers in JavaScript are Double values ([more info on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#number_encoding)),
97
+ * for Long variables, the API is currently limited to the Double value range.
98
+ */
59
99
  let hasCadenzaSession = false;
60
100
  /** @type {Promise<void> | undefined} */
61
101
  let firstEmbeddingTargetShown;
@@ -81,7 +121,7 @@ export class CadenzaClient {
81
121
  #iframeElement;
82
122
  /** @readonly */
83
123
  #debug;
84
- /** @type {[ string, (event: CadenzaEvent<never>) => void ][]} */
124
+ /** @type {[ CadenzaEventType | string, (event: CadenzaEvent<never>) => void ][]} */
85
125
  #subscriptions = [];
86
126
  /**
87
127
  * @hidden
@@ -145,15 +185,12 @@ export class CadenzaClient {
145
185
  * @param {String} [options.labelSet] - The name of a label set defined in the `basicweb-config.xml` (only supported for the welcome page)
146
186
  * @param {OperationMode} [options.operationMode] - The mode in which a workbook should be operated
147
187
  * @param {AbortSignal} [options.signal] - A signal to abort the iframe loading
148
- * @return {Promise<void>} A Promise for when the iframe is loaded
188
+ * @return {Promise<void>} A `Promise` for when the iframe is loaded
149
189
  * @throws For invalid arguments
150
- * @fires `drillThrough` - When the user executed a POST message drill-through.
151
- * The event includes a row of values for each row in the workbook selection, each row consisting of the values of
152
- * the attributes that were selected for the POST message content. If the drill-through was executed from a map
153
- * view, each row includes the geometry of the select object as the last value.
190
+ * @fires {@link CadenzaDrillThroughEvent}
154
191
  */
155
192
  show(source, { dataType, disabledUiFeatures, expandNavigator, filter, hideMainHeaderAndFooter, hideWorkbookToolBar, highlightGlobalId, labelSet, operationMode, signal, } = {}) {
156
- this.#log('CadenzaClient#show', source);
193
+ this.#log('CadenzaClient#show', ...arguments);
157
194
  if (dataType) {
158
195
  assertSupportedDataType(dataType, ['pdf']);
159
196
  }
@@ -172,7 +209,6 @@ export class CadenzaClient {
172
209
  labelSet,
173
210
  dataType,
174
211
  operationMode,
175
- webApplication: this.#webApplication,
176
212
  });
177
213
  return this.#show(resolvePath(source), params, signal);
178
214
  }
@@ -193,14 +229,12 @@ export class CadenzaClient {
193
229
  * @param {OperationMode} [options.operationMode] - The mode in which a workbook should be operated
194
230
  * @param {boolean} [options.useMapSrs] - Whether the geometry and the extent are in the map's SRS (otherwise EPSG:4326 is assumed)
195
231
  * @param {AbortSignal} [options.signal] - A signal to abort the iframe loading
196
- * @return {Promise<void>} A Promise for when the iframe is loaded
232
+ * @return {Promise<void>} A `Promise` for when the iframe is loaded
197
233
  * @throws For invalid arguments
198
- * @fires `drillThrough` - When the user executed a POST message drill-through.
199
- * The event includes a row of values for each row in the workbook selection, each row consisting of the values of
200
- * the attributes that were selected for the POST message content plus the geometry of the select object as the last value.
234
+ * @fires {@link CadenzaDrillThroughEvent}
201
235
  */
202
236
  async showMap(mapView, { disabledUiFeatures, expandNavigator, filter, geometry, hideMainHeaderAndFooter, hideWorkbookToolBar, highlightGlobalId, locationFinder, mapExtent, operationMode, useMapSrs, signal, } = {}) {
203
- this.#log('CadenzaClient#showMap', mapView, geometry);
237
+ this.#log('CadenzaClient#showMap', ...arguments);
204
238
  if (geometry) {
205
239
  assertValidGeometryType(geometry.type);
206
240
  }
@@ -214,11 +248,13 @@ export class CadenzaClient {
214
248
  locationFinder,
215
249
  mapExtent,
216
250
  operationMode,
251
+ targetType: 'MAP',
217
252
  useMapSrs,
218
- webApplication: this.#webApplication,
219
253
  });
220
254
  await this.#show(resolvePath(mapView), params, signal);
221
- this.#postEvent('setGeometry', { geometry });
255
+ if (geometry) {
256
+ this.#postEvent('setGeometry', { geometry });
257
+ }
222
258
  }
223
259
  /**
224
260
  * Expand/collapse the navigator.
@@ -226,9 +262,93 @@ export class CadenzaClient {
226
262
  * @param {boolean} expanded - The expansion state of the navigator
227
263
  */
228
264
  expandNavigator(expanded = true) {
229
- this.#log('CadenzaClient#expandNavigator', expanded);
265
+ this.#log('CadenzaClient#expandNavigator', ...arguments);
230
266
  this.#postEvent('expandNavigator', { expandNavigator: Boolean(expanded) });
231
267
  }
268
+ /**
269
+ * Get data from the currently shown workbook view.
270
+ *
271
+ * Currently, only map views are supported.
272
+ *
273
+ * @hidden
274
+ * @template {DataType} T
275
+ * @param {T} dataType - The requested data type. Currently, only `"png"` is supported.
276
+ * @return {Promise<T extends 'png' ? Blob : never>}
277
+ */
278
+ async getData(dataType) {
279
+ this.#log('CadenzaClient#getData', ...arguments);
280
+ assertSupportedDataType(dataType, ['png']);
281
+ return this.#postRequest('getData', { dataType });
282
+ }
283
+ /**
284
+ * Set filter variables in the currently shown workbook.
285
+ *
286
+ * @hidden
287
+ * @param {FilterVariables} filter - The variable values
288
+ * @return {Promise<void>} A `Promise` for when the filter variables were set.
289
+ */
290
+ setFilter(filter) {
291
+ this.#log('CadenzaClient#setFilter', ...arguments);
292
+ return this.#postRequest('setFilter', { filter });
293
+ }
294
+ /**
295
+ * Set the visibility of a layer in the currently shown workbook map view.
296
+ *
297
+ * When making a layer visible, its ancestors will be made visible, too.
298
+ * When hiding a layer, the ancestors are not affected.
299
+ *
300
+ * @hidden
301
+ * @param {WorkbookLayerPath | string} layer - The layer to show or hide
302
+ * (identified using a layer path or a print name)
303
+ * @param {boolean} visible - The visibility state of the layer
304
+ * @return {Promise<void>} A `Promise` for when the layer visibility was set.
305
+ */
306
+ setLayerVisibility(layer, visible) {
307
+ this.#log('CadenzaClient#setLayerVisibility', ...arguments);
308
+ return this.#postRequest('setLayerVisibility', {
309
+ layer: array(layer),
310
+ visible,
311
+ });
312
+ }
313
+ /**
314
+ * Set the selection in the currently shown workbook map view.
315
+ *
316
+ * @hidden
317
+ * @param {WorkbookLayerPath | string} layer - The data view layer to set the selection in
318
+ * @param {unknown[]} values - The IDs of the objects to select
319
+ * @return {Promise<void>} A `Promise` for when the selection was set.
320
+ */
321
+ setSelection(layer, values) {
322
+ this.#log('CadenzaClient#setSelection', ...arguments);
323
+ return this.#postRequest('setSelection', { layer: array(layer), values });
324
+ }
325
+ /**
326
+ * Add to the selection in the currently shown workbook map view.
327
+ *
328
+ * @hidden
329
+ * @param {WorkbookLayerPath | string} layer - The data view layer to change the selection in
330
+ * @param {unknown[]} values - The IDs of the objects to select
331
+ * @return {Promise<void>} A `Promise` for when the selection was changed.
332
+ */
333
+ addSelection(layer, values) {
334
+ this.#log('CadenzaClient#addSelection', ...arguments);
335
+ return this.#postRequest('addSelection', { layer: array(layer), values });
336
+ }
337
+ /**
338
+ * Remove from the selection in the currently shown workbook map view.
339
+ *
340
+ * @hidden
341
+ * @param {WorkbookLayerPath | string} layer - The data view layer to change the selection in
342
+ * @param {unknown[]} values - The IDs of the objects to unselect
343
+ * @return {Promise<void>} A `Promise` for when the selection was changed.
344
+ */
345
+ removeSelection(layer, values) {
346
+ this.#log('CadenzaClient#removeSelection', ...arguments);
347
+ return this.#postRequest('removeSelection', {
348
+ layer: array(layer),
349
+ values,
350
+ });
351
+ }
232
352
  /**
233
353
  * Create a geometry.
234
354
  *
@@ -243,14 +363,15 @@ export class CadenzaClient {
243
363
  * @param {number} [options.minScale] - The minimum scale where the user should work on. A warning is shown when the map is zoomed out above the threshold.
244
364
  * @param {boolean} [options.useMapSrs] - Whether the created geometry should use the map's SRS (otherwise EPSG:4326 will be used)
245
365
  * @param {AbortSignal} [options.signal] - A signal to abort the iframe loading
246
- * @return {Promise<void>} A Promise for when the iframe is loaded
366
+ * @return {Promise<void>} A `Promise` for when the iframe is loaded
247
367
  * @throws For invalid arguments
248
- * @fires `editGeometry:update` - When the user changed the geometry. The event includes the edited geometry.
249
- * @fires `editGeometry:ok` - When the user completed the geometry editing. The event includes the edited geometry.
250
- * @fires `editGeometry:cancel` - When the user cancelled the geometry editing in Cadenza.
368
+ * @fires
369
+ * - {@link CadenzaEditGeometryUpdateEvent}
370
+ * - {@link CadenzaEditGeometryOkEvent}
371
+ * - {@link CadenzaEditGeometryCancelEvent}
251
372
  */
252
373
  createGeometry(backgroundMapView, geometryType, { locationFinder, mapExtent, minScale, useMapSrs, signal } = {}) {
253
- this.#log('CadenzaClient#createGeometry', backgroundMapView, geometryType);
374
+ this.#log('CadenzaClient#createGeometry', ...arguments);
254
375
  const params = createParams({
255
376
  action: 'editGeometry',
256
377
  geometryType,
@@ -258,7 +379,6 @@ export class CadenzaClient {
258
379
  mapExtent,
259
380
  minScale,
260
381
  useMapSrs,
261
- webApplication: this.#webApplication,
262
382
  });
263
383
  return this.#show(resolvePath(backgroundMapView), params, signal);
264
384
  }
@@ -273,14 +393,15 @@ export class CadenzaClient {
273
393
  * @param {number} [options.minScale] - The minimum scale where the user should work on. A warning is shown when the map is zoomed out above the threshold.
274
394
  * @param {boolean} [options.useMapSrs] - Whether the geometry is in the map's SRS (otherwise EPSG:4326 is assumed)
275
395
  * @param {AbortSignal} [options.signal] - A signal to abort the iframe loading
276
- * @return {Promise<void>} A Promise for when the iframe is loaded
396
+ * @return {Promise<void>} A `Promise` for when the iframe is loaded
277
397
  * @throws For invalid arguments
278
- * @fires `editGeometry:update` - When the user changed the geometry. The event includes the edited geometry.
279
- * @fires `editGeometry:ok` - When the user completed the geometry editing. The event includes the edited geometry.
280
- * @fires `editGeometry:cancel` - When the user cancelled the geometry editing in Cadenza.
398
+ * @fires
399
+ * - {@link CadenzaEditGeometryUpdateEvent}
400
+ * - {@link CadenzaEditGeometryOkEvent}
401
+ * - {@link CadenzaEditGeometryCancelEvent}
281
402
  */
282
403
  async editGeometry(backgroundMapView, geometry, { locationFinder, mapExtent, minScale, useMapSrs, signal } = {}) {
283
- this.#log('CadenzaClient#editGeometry', backgroundMapView, geometry);
404
+ this.#log('CadenzaClient#editGeometry', ...arguments);
284
405
  assertValidGeometryType(geometry.type);
285
406
  const params = createParams({
286
407
  action: 'editGeometry',
@@ -288,10 +409,41 @@ export class CadenzaClient {
288
409
  mapExtent,
289
410
  minScale,
290
411
  useMapSrs,
291
- webApplication: this.#webApplication,
292
412
  });
293
413
  await this.#show(resolvePath(backgroundMapView), params, signal);
294
- this.#postEvent('setGeometry', { geometry });
414
+ if (geometry) {
415
+ this.#postEvent('setGeometry', { geometry });
416
+ }
417
+ }
418
+ /**
419
+ * Select objects in a workbook map.
420
+ *
421
+ * @param {EmbeddingTargetId} backgroundMapView - The workbook map view
422
+ * @param {object} [options] - Options
423
+ * @param {(WorkbookLayerPath | string)[]} [options.layers] - Layers to restrict the selection to
424
+ * (identified using layer paths or print names)
425
+ * @param {string} [options.locationFinder] - A search query for the location finder
426
+ * @param {Extent} [options.mapExtent] - A map extent to set
427
+ * @param {boolean} [options.useMapSrs] - Whether the geometry is in the map's SRS (otherwise EPSG:4326 is assumed)
428
+ * @param {AbortSignal} [options.signal] - A signal to abort the iframe loading
429
+ * @return {Promise<void>} A `Promise` for when the iframe is loaded
430
+ * @throws For invalid arguments
431
+ * @fires
432
+ * - {@link CadenzaChangeSelectionEvent}
433
+ * - {@link CadenzaObjectInfoEvent}
434
+ * - {@link CadenzaSelectObjectsOkEvent}
435
+ * - {@link CadenzaSelectObjectsCancelEvent}
436
+ */
437
+ selectObjects(backgroundMapView, { layers, locationFinder, mapExtent, useMapSrs, signal } = {}) {
438
+ this.#log('CadenzaClient#selectObjects', ...arguments);
439
+ const params = createParams({
440
+ action: 'selectObjects',
441
+ layers: layers?.map(array),
442
+ locationFinder,
443
+ mapExtent,
444
+ useMapSrs,
445
+ });
446
+ return this.#show(resolvePath(backgroundMapView), params, signal);
295
447
  }
296
448
  /**
297
449
  * @param {string} path
@@ -331,14 +483,14 @@ export class CadenzaClient {
331
483
  }
332
484
  #getIframePromise(/** @type AbortSignal | undefined */ signal) {
333
485
  const iframe = this.#requiredIframe;
334
- /** @type {() => void} */
486
+ /** @type {EventListener} */
335
487
  let onerror;
336
- /** @type {() => void} */
488
+ /** @type {EventListener} */
337
489
  let onabort;
338
490
  /** @type {(() => void)[]} */
339
491
  let unsubscribes;
340
492
  /** @type {Promise<void>} */
341
- let promise = new Promise((resolve, reject) => {
493
+ const promise = new Promise((resolve, reject) => {
342
494
  onerror = () => reject(new CadenzaError('loading-error', 'Loading failed'));
343
495
  iframe.addEventListener('error', onerror);
344
496
  if (signal) {
@@ -349,8 +501,8 @@ export class CadenzaClient {
349
501
  signal.addEventListener('abort', onabort);
350
502
  }
351
503
  unsubscribes = [
352
- this.on('ready', () => resolve()),
353
- this.on('error', (/** @type {CadenzaErrorEvent} */ event) => {
504
+ this.#on('ready', () => resolve()),
505
+ this.#on('error', (/** @type {CadenzaErrorEvent} */ event) => {
354
506
  const { type, message } = event.detail;
355
507
  reject(new CadenzaError(type, message ?? 'Loading failed'));
356
508
  }),
@@ -368,17 +520,30 @@ export class CadenzaClient {
368
520
  /**
369
521
  * Subscribe to a `postMessage()` event.
370
522
  *
371
- * @template [T=unknown]
372
- * @param {string} type - The event type
373
- * @param {(event: CadenzaEvent<T>) => void} subscriber - The subscriber function
523
+ * @template {CadenzaEventType} TYPE
524
+ * @param {TYPE} type - The event type
525
+ * @param {(event: CadenzaEvent<TYPE, CadenzaEventByType<TYPE>['detail']>) => void} subscriber - The subscriber function
374
526
  * @return {() => void} An unsubscribe function
375
527
  */
376
528
  on(type, subscriber) {
529
+ return this.#on(type, subscriber);
530
+ }
531
+ /**
532
+ * @template {CadenzaEventType | string} TYPE
533
+ * @template [DETAIL=unknown]
534
+ * @param {TYPE} type
535
+ * @param {(event: CadenzaEvent<TYPE, DETAIL>) => void} subscriber
536
+ * @return {() => void} An unsubscribe function
537
+ */
538
+ #on(type, subscriber) {
377
539
  const subscriptions = this.#subscriptions;
378
540
  if (subscriptions.length === 0) {
379
541
  window.addEventListener('message', this.#onMessage);
380
542
  }
381
- subscriptions.push([type, subscriber]);
543
+ subscriptions.push([
544
+ type,
545
+ /** @type {(event: CadenzaEvent<never>) => void} */ (subscriber),
546
+ ]);
382
547
  return () => {
383
548
  subscriptions.forEach(([subscriptionType, subscriptionSubscriber], i) => {
384
549
  if (subscriptionType === type &&
@@ -392,7 +557,8 @@ export class CadenzaClient {
392
557
  };
393
558
  }
394
559
  // Use arrow function so that it's bound to this.
395
- #onMessage = (/** @type MessageEvent<CadenzaEvent<never>> */ event) => {
560
+ #onMessage = (
561
+ /** @type MessageEvent<CadenzaEvent<never, never>> */ event) => {
396
562
  if (event.origin !== this.#origin ||
397
563
  event.source !== this.#requiredIframe.contentWindow) {
398
564
  return;
@@ -405,26 +571,63 @@ export class CadenzaClient {
405
571
  }
406
572
  });
407
573
  };
408
- #postEvent(/** @type string */ type, /** @type unknown */ detail) {
574
+ /**
575
+ * Posts an event to Cadenza and returns a `Promise` for the response.
576
+ *
577
+ * It is guaranteed that a response refers to a specific request,
578
+ * even if multiple request are executed in parallel.
579
+ * @template [T=void]
580
+ * @param {string} type
581
+ * @param {unknown} [detail]
582
+ * @returns {Promise<T>}
583
+ */
584
+ #postRequest(type, detail) {
585
+ const { port1, port2 } = new MessageChannel();
586
+ const promise = new Promise((resolve, reject) => {
587
+ port1.onmessage = (
588
+ /** @type MessageEvent<CadenzaEvent<never, never>> */ event) => {
589
+ const cadenzaEvent = event.data;
590
+ if (cadenzaEvent.type === `${type}:success`) {
591
+ resolve(cadenzaEvent.detail);
592
+ }
593
+ else if (cadenzaEvent.type === `${type}:error`) {
594
+ reject();
595
+ }
596
+ };
597
+ });
598
+ this.#postEvent(type, detail, [port2]);
599
+ return promise;
600
+ }
601
+ /**
602
+ * @param {string} type
603
+ * @param {unknown} [detail]
604
+ * @param {Transferable[]} [transfer]
605
+ */
606
+ #postEvent(type, detail, transfer) {
409
607
  const cadenzaEvent = { type, detail };
410
608
  this.#log('postMessage', cadenzaEvent);
411
609
  const contentWindow = /** @type {WindowProxy} */ (this.#requiredIframe.contentWindow);
412
- contentWindow.postMessage(cadenzaEvent, { targetOrigin: this.#origin });
610
+ contentWindow.postMessage(cadenzaEvent, {
611
+ targetOrigin: this.#origin,
612
+ transfer,
613
+ });
413
614
  }
414
615
  /**
415
616
  * Fetch data from a workbook view.
416
617
  *
417
- * @param {EmbeddingTargetId} source - The workbook view to fetch data from
418
- * @param {DataType} dataType - The data type you want to get back from the server
618
+ * @param {EmbeddingTargetId} source - The workbook view to fetch data from.
619
+ * Currently only table and indicator views are supported.
620
+ * @param {DataType} dataType - The data type you want to get back from the server.
621
+ * Currently, `"csv"`, `"excel"` and `"json"` are supported.
419
622
  * @param {object} options - Options
420
623
  * @param {TablePart[]} [options.parts] - Table parts to export; If not specified, all parts are exported.
421
624
  * @param {AbortSignal} [options.signal] - A signal to abort the data fetching
422
- * @return {Promise<Response>} A Promise for the fetch response
625
+ * @return {Promise<Response>} A `Promise` for the fetch response
423
626
  * @throws For invalid arguments
424
627
  */
425
628
  fetchData(source, dataType, { parts, signal } = {}) {
426
- this.#log('CadenzaClient#fetchData', source, dataType);
427
- assertSupportedDataType(dataType);
629
+ this.#log('CadenzaClient#fetchData', ...arguments);
630
+ assertSupportedDataType(dataType, ['csv', 'excel', 'json']);
428
631
  const params = createParams({ dataType, parts });
429
632
  return this.#fetch(resolvePath(source), params, signal);
430
633
  }
@@ -450,16 +653,18 @@ export class CadenzaClient {
450
653
  *
451
654
  * _Note:_ The file name, if not provided, is generated from the name of the workbook view and the current date.
452
655
  *
453
- * @param {EmbeddingTargetId} source - The workbook view to download data from
454
- * @param {DataType} dataType - The data type you want to get back from the server
656
+ * @param {EmbeddingTargetId} source - The workbook view to download data from.
657
+ * Currently only table and indicator views are supported.
658
+ * @param {DataType} dataType - The data type you want to get back from the server.
659
+ * Currently, `"csv"`, `"excel"` and `"json"` are supported.
455
660
  * @param {object} options - Options
456
661
  * @param {string} [options.fileName] - The file name to use; The file extension is appended by Cadenza.
457
662
  * @param {TablePart[]} [options.parts] - Table parts to export; If not specified, all parts are exported.
458
663
  * @throws For invalid arguments
459
664
  */
460
665
  downloadData(source, dataType, { fileName, parts }) {
461
- this.#log('CadenzaClient#downloadData', source, dataType);
462
- assertSupportedDataType(dataType);
666
+ this.#log('CadenzaClient#downloadData', ...arguments);
667
+ assertSupportedDataType(dataType, ['csv', 'excel', 'json']);
463
668
  const params = createParams({ dataType, fileName, parts });
464
669
  this.#download(resolvePath(source), params);
465
670
  }
@@ -485,7 +690,20 @@ export class CadenzaClient {
485
690
  }
486
691
  #log(/** @type unknown[] */ ...args) {
487
692
  if (this.#debug) {
488
- console.log(...args);
693
+ /** @type {unknown[]} */
694
+ const redundantValues = [undefined, '', false];
695
+ /** @type {(value: unknown) => value is object} */
696
+ const isObject = (/** @type {unknown} */ value) => value != null && typeof value === 'object';
697
+ const sanitizedArgs = args
698
+ .map((arg) => {
699
+ if (isObject(arg)) {
700
+ return Object.fromEntries(Object.entries(arg).filter(([, value]) => !redundantValues.includes(value)));
701
+ }
702
+ return arg;
703
+ })
704
+ .filter((arg) => !redundantValues.includes(arg) &&
705
+ !(isObject(arg) && Object.keys(arg).length === 0));
706
+ console.log(...sanitizedArgs);
489
707
  }
490
708
  }
491
709
  }
@@ -562,11 +780,11 @@ function validUiFeature(/** @type string */ value) {
562
780
  }
563
781
  function assertSupportedDataType(
564
782
  /** @type DataType */ type,
565
- /** @type DataType[] */ supportedTypes = ['csv', 'excel', 'json', 'pdf']) {
783
+ /** @type DataType[] */ supportedTypes) {
566
784
  assert(supportedTypes.includes(type), `Invalid data type: ${type}`);
567
785
  }
568
786
  /**
569
- * @param {object} params - Options
787
+ * @param {object} params
570
788
  * @param {string} [params.action]
571
789
  * @param {DataType} [params.dataType]
572
790
  * @param {UiFeature[]} [params.disabledUiFeatures]
@@ -578,16 +796,17 @@ function assertSupportedDataType(
578
796
  * @param {boolean} [params.hideWorkbookToolBar]
579
797
  * @param {GlobalId} [params.highlightGlobalId]
580
798
  * @param {string} [params.labelSet]
799
+ * @param {WorkbookLayerPath[]} [params.layers]
581
800
  * @param {string} [params.locationFinder]
582
801
  * @param {Extent} [params.mapExtent]
583
802
  * @param {number} [params.minScale]
584
803
  * @param {OperationMode} [params.operationMode]
585
804
  * @param {TablePart[]} [params.parts]
805
+ * @param {'MAP'} [params.targetType]
586
806
  * @param {boolean} [params.useMapSrs]
587
- * @param {ExternalLinkKey} [params.webApplication]
588
807
  * @return {URLSearchParams}
589
808
  */
590
- function createParams({ action, dataType, disabledUiFeatures, expandNavigator, fileName, filter, geometryType, hideMainHeaderAndFooter, hideWorkbookToolBar, highlightGlobalId, labelSet, locationFinder, mapExtent, minScale, parts, useMapSrs, webApplication, operationMode, }) {
809
+ function createParams({ action, dataType, disabledUiFeatures, expandNavigator, fileName, filter, geometryType, hideMainHeaderAndFooter, hideWorkbookToolBar, highlightGlobalId, labelSet, layers, locationFinder, mapExtent, minScale, operationMode, parts, targetType, useMapSrs, }) {
591
810
  if (disabledUiFeatures) {
592
811
  disabledUiFeatures.forEach((feature) => assert(validUiFeature(feature), `Invalid UI feature: ${feature}`));
593
812
  }
@@ -621,25 +840,76 @@ function createParams({ action, dataType, disabledUiFeatures, expandNavigator, f
621
840
  ...(hideWorkbookToolBar && { hideWorkbookToolBar: 'true' }),
622
841
  ...(highlightGlobalId && { highlightGlobalId }),
623
842
  ...(labelSet && { labelSet }),
843
+ ...(layers &&
844
+ layers.length && {
845
+ layers: JSON.stringify(layers),
846
+ }),
624
847
  ...(locationFinder && { locationFinder }),
625
848
  ...(mapExtent && { mapExtent: mapExtent.join() }),
626
849
  ...(minScale && { minScale: String(minScale) }),
627
850
  ...(operationMode && operationMode !== 'normal' && { operationMode }),
628
851
  ...(parts && { parts: parts.join() }),
852
+ ...(targetType && { targetType }),
629
853
  ...(useMapSrs && { useMapSrs: 'true' }),
630
- ...(webApplication && {
631
- webApplicationLink: webApplication.externalLinkId,
632
- webApplicationLinkRepository: webApplication.repositoryName,
633
- }),
634
854
  });
635
855
  }
856
+ function array(/** @type unknown */ value) {
857
+ return Array.isArray(value) ? value : [value];
858
+ }
859
+ // Please do not add internal event types like 'ready' here.
860
+ /**
861
+ * @typedef {'change:selection'
862
+ * | 'drillThrough'
863
+ * | 'editGeometry:ok'
864
+ * | 'editGeometry:update'
865
+ * | 'editGeometry:cancel'
866
+ * | 'objectInfo'
867
+ * | 'selectObjects:ok'
868
+ * | 'selectObjects:cancel'
869
+ * } CadenzaEventType - An event type to subscribe to using {@link CadenzaClient#on}
870
+ */
871
+ /**
872
+ * @template {CadenzaEventType} T
873
+ * @typedef {T extends 'change:selection' ? CadenzaChangeSelectionEvent
874
+ * : T extends 'drillThrough' ? CadenzaDrillThroughEvent
875
+ * : T extends 'editGeometry:update' ? CadenzaEditGeometryUpdateEvent
876
+ * : T extends 'editGeometry:ok' ? CadenzaEditGeometryOkEvent
877
+ * : T extends 'editGeometry:cancel' ? CadenzaEditGeometryCancelEvent
878
+ * : T extends 'objectInfo' ? CadenzaObjectInfoEvent
879
+ * : T extends 'selectObjects:ok' ? CadenzaSelectObjectsOkEvent
880
+ * : T extends 'selectObjects:cancel' ? CadenzaSelectObjectsCancelEvent
881
+ * : never
882
+ * } CadenzaEventByType
883
+ */
636
884
  /**
637
- * @template [T=unknown]
885
+ * @template {CadenzaEventType | string} TYPE
886
+ * @template [DETAIL=unknown]
638
887
  * @typedef CadenzaEvent - A Cadenza `postMessage()` event
639
- * @property {string} type - The event type
640
- * @property {T} detail - Optional event details (depending on the event type)
888
+ * @property {TYPE} type - The event type
889
+ * @property {DETAIL} detail - Optional event details (depending on the event type)
890
+ */
891
+ /*
892
+ * @hidden
893
+ * @typedef {CadenzaEvent<'change:extent', {extent: Extent}>} CadenzaChangeExtentEvent - When the user moved the map.
894
+ * The extent is transformed according to the `useMapSrs` option.
895
+ */
896
+ /** @typedef {CadenzaEvent<'change:selection', undefined | {layer: WorkbookLayerPath, values: unknown[][]}>} CadenzaChangeSelectionEvent - When the user changed the selection. */
897
+ /**
898
+ * @typedef {CadenzaEvent<'drillThrough', {values: unknown[][]}>} CadenzaDrillThroughEvent - When the user executed a POST message drill-through.
899
+ * <p>
900
+ * The event includes a data row for every item in the workbook selection, each row consisting of the values of
901
+ * the attributes that were selected for the POST message content. If the drill-through was executed from a map
902
+ * view, each row includes the geometry of the selected object as the last value.
903
+ * <p>
904
+ * See also: <a href="../index.html#md:json-representation-of-cadenza-object-data">JSON Representation of Cadenza Object Data</a>
641
905
  */
642
- /** @typedef {CadenzaEvent<{type: string, message?: string}>} CadenzaErrorEvent - An error event that is mapped to a {@link CadenzaError} */
906
+ /** @typedef {CadenzaEvent<'editGeometry:update', {geometry: Geometry}>} CadenzaEditGeometryUpdateEvent - When the user changed the geometry. */
907
+ /** @typedef {CadenzaEvent<'editGeometry:ok', {geometry: Geometry}>} CadenzaEditGeometryOkEvent - When the user submitted the geometry. */
908
+ /** @typedef {CadenzaEvent<'editGeometry:cancel'>} CadenzaEditGeometryCancelEvent - When the user cancelled the geometry editing. */
909
+ /** @typedef {CadenzaEvent<'error', {type: string, message?: string}>} CadenzaErrorEvent - An error event that is mapped to a {@link CadenzaError} */
910
+ /** @typedef {CadenzaEvent<'objectInfo', {layer: WorkbookLayerPath, objectInfos: {selectionIndex: number, formattedValues: Record<string, string>}[]}>} CadenzaObjectInfoEvent - When the user opened the object info flyout. */
911
+ /** @typedef {CadenzaEvent<'selectObjects:ok', {layer: WorkbookLayerPath, values: unknown[][]}>} CadenzaSelectObjectsOkEvent - When the user submitted the selection. */
912
+ /** @typedef {CadenzaEvent<'selectObjects:cancel'>} CadenzaSelectObjectsCancelEvent - When the user cancelled the selection. */
643
913
  export class AbortError extends DOMException {
644
914
  constructor() {
645
915
  super('Aborted', 'AbortError');