@panoramax/web-viewer 4.1.0-develop-381f3f1a → 4.1.0-develop-22cdb9e7

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.
@@ -0,0 +1,15 @@
1
+ <a name="Panoramax.components.menus.SemanticsMetadata"></a>
2
+
3
+ ## Panoramax.components.menus.SemanticsMetadata ⇐ <code>[lit.LitElement](https://lit.dev/docs/api/LitElement/)</code>
4
+ **Kind**: static class of <code>Panoramax.components.menus</code>
5
+ **Extends**: <code>[lit.LitElement](https://lit.dev/docs/api/LitElement/)</code>
6
+ **Element**: pnx-semantics-metadata
7
+ <a name="new_Panoramax.components.menus.SemanticsMetadata_new"></a>
8
+
9
+ ### new SemanticsMetadata()
10
+ Semantics metadata displays detailed info about semantic attributes of a single picture.
11
+
12
+ **Example**
13
+ ```html
14
+ <pnx-semantics-metadata ._parent=${viewer}></pnx-semantics-metadata>
15
+ ```
@@ -25,6 +25,7 @@
25
25
  * [.searchUsers(query)](#Panoramax.utils.API+searchUsers) ⇒ <code>Promise</code>
26
26
  * [.getUserName(userId)](#Panoramax.utils.API+getUserName) ⇒ <code>Promise</code>
27
27
  * [.sendReport(data)](#Panoramax.utils.API+sendReport) ⇒ <code>Promise</code>
28
+ * [.sendPictureSemantics(picMeta, semanticsDiff)](#Panoramax.utils.API+sendPictureSemantics) ⇒ <code>Promise</code>
28
29
  * ["broken"](#Panoramax.utils.API+event_broken)
29
30
  * ["ready"](#Panoramax.utils.API+event_ready)
30
31
  * _static_
@@ -284,6 +285,19 @@ Send a report to API
284
285
  | --- | --- | --- |
285
286
  | data | <code>object</code> | The input form data |
286
287
 
288
+ <a name="Panoramax.utils.API+sendPictureSemantics"></a>
289
+
290
+ ### api.sendPictureSemantics(picMeta, semanticsDiff) ⇒ <code>Promise</code>
291
+ Send picture semantics change to origin API.
292
+
293
+ **Kind**: instance method of [<code>API</code>](#Panoramax.utils.API)
294
+ **Fulfil**: <code>object</code> The JSON API response
295
+
296
+ | Param | Type | Description |
297
+ | --- | --- | --- |
298
+ | picMeta | <code>object</code> | The picture metadata |
299
+ | semanticsDiff | <code>object</code> | The difference in semantics compared to original data |
300
+
287
301
  <a name="Panoramax.utils.API+event_broken"></a>
288
302
 
289
303
  ### "broken"
package/docs/reference.md CHANGED
@@ -36,6 +36,7 @@ All-in-one, ready-to-use menus for complex operations. Note that they don't embe
36
36
  - [PlayerOptions](./reference/components/menus/PlayerOptions.md) : speed and constrast settings for play sequence feature.
37
37
  - [QualityScoreDoc](./reference/components/menus/QualityScoreDoc.md) : details about quality score computation.
38
38
  - [ReportForm](./reference/components/menus/ReportForm.md) : picture issue reporting form.
39
+ - [SemanticsMetadata](./reference/components/menus/SemanticsMetadata.md) : display full details about a picture semantics.
39
40
  - [ShareMenu](./reference/components/menus/ShareMenu.md) : links and iframe sharing.
40
41
 
41
42
  ## `components.ui`
@@ -83,4 +84,5 @@ General helpers outside of single component scope:
83
84
 
84
85
  - [API](./reference/utils/API.md) : the Panoramax API helper (many get & post HTTP helpers).
85
86
  - [InitParameters](./reference/utils/InitParameters.md) : helper for merging URL and component parameters.
87
+ - [PresetsManager](./reference/utils/PresetsManager.md) : the semantics attributes presets manager.
86
88
  - [URLHandler](./reference/utils/URLHandler.md) : the window URL manager (changes query part).
package/mkdocs.yml CHANGED
@@ -76,6 +76,7 @@ nav:
76
76
  - PlayerOptions: 'reference/components/menus/PlayerOptions.md'
77
77
  - QualityScoreDoc: 'reference/components/menus/QualityScoreDoc.md'
78
78
  - ReportForm: 'reference/components/menus/ReportForm.md'
79
+ - SemanticsMetadata: 'reference/components/menus/SemanticsMetadata.md'
79
80
  - ShareMenu: 'reference/components/menus/ShareMenu.md'
80
81
  - ui:
81
82
  - widgets:
@@ -110,4 +111,5 @@ nav:
110
111
  - utils:
111
112
  - API: 'reference/utils/API.md'
112
113
  - InitParameters: 'reference/utils/InitParameters.md'
114
+ - PresetsManager: 'reference/utils/PresetsManager.md'
113
115
  - URLHandler: 'reference/utils/URLHandler.md'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@panoramax/web-viewer",
3
- "version": "4.1.0-develop-381f3f1a",
3
+ "version": "4.1.0-develop-22cdb9e7",
4
4
  "description": "Panoramax web viewer for geolocated pictures",
5
5
  "main": "build/index.js",
6
6
  "author": "Panoramax team",
@@ -345,7 +345,11 @@ export default class Basic extends LitElement {
345
345
  else if(typeof value === "object" || Array.isArray(value)) { return value; }
346
346
  else { return JSON5.parse(value); }
347
347
  },
348
- toAttribute: (value) => JSON.stringify(value)
348
+ toAttribute: (value) => {
349
+ if(value === null || value === "") { return ""; }
350
+ else if(typeof value === "string") { return value; }
351
+ else { return JSON5.stringify(value); }
352
+ }
349
353
  };
350
354
  }
351
355
  }
@@ -9,12 +9,10 @@ import { faImages } from "@fortawesome/free-solid-svg-icons/faImages";
9
9
  import { faScroll } from "@fortawesome/free-solid-svg-icons/faScroll";
10
10
  import { faQuestion } from "@fortawesome/free-solid-svg-icons/faQuestion";
11
11
  import { faInfoCircle } from "@fortawesome/free-solid-svg-icons/faInfoCircle";
12
- import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown";
13
- import { faChevronUp } from "@fortawesome/free-solid-svg-icons/faChevronUp";
14
12
  import { faTags } from "@fortawesome/free-solid-svg-icons/faTags";
15
- import { faSvg, titles, textarea, hidden } from "../styles";
13
+ import { faSvg, titles, textarea, hidden, dataBlocks } from "../styles";
16
14
  import { createWebComp } from "../../utils/widgets";
17
- import { getGPSPrecision, getHashTags } from "../../utils/picture";
15
+ import { getGPSPrecision } from "../../utils/picture";
18
16
  import { PanoramaxMetaCatalogURL } from "../../utils/services";
19
17
  import {
20
18
  getGrade, QUALITYSCORE_GPS_VALUES, QUALITYSCORE_RES_360_VALUES,
@@ -35,30 +33,14 @@ const missing = () => fa(faQuestion, {styles: {height: "16px"}});
35
33
  */
36
34
  export default class PictureMetadata extends LitElement {
37
35
  /** @private */
38
- static styles = [ faSvg, titles, textarea, hidden, css`
36
+ static styles = [ faSvg, titles, textarea, hidden, dataBlocks, css`
39
37
  div[slot="content"] {
40
38
  padding: 5px 10px;
41
39
  background-color: #ededed;
42
40
  }
43
41
 
44
- /* Small data blocks */
45
42
  .data-block {
46
- display: inline-block;
47
43
  min-width: 50%;
48
- margin: 8px 0;
49
- box-sizing: border-box;
50
- vertical-align: top;
51
- }
52
- .data-block h5 {
53
- font-size: 0.8em;
54
- font-weight: 400;
55
- color: var(--blue-dark);
56
- margin: 0 0 5px 0;
57
- }
58
- .data-block h5 pnx-button { vertical-align: middle; }
59
- .data-block h5 svg.svg-inline--fa { height: 14px; }
60
- .data-block div {
61
- font-size: 0.8em;
62
44
  }
63
45
 
64
46
  pnx-semantics-table {
@@ -70,14 +52,8 @@ export default class PictureMetadata extends LitElement {
70
52
  /** @private */
71
53
  static properties = {
72
54
  _meta: {state: true},
73
- _semanticsPicShowAll: {state: true},
74
55
  };
75
56
 
76
- constructor() {
77
- super();
78
- this._semanticsPicShowAll = false;
79
- }
80
-
81
57
  /** @private */
82
58
  connectedCallback() {
83
59
  super.connectedCallback();
@@ -238,61 +214,6 @@ export default class PictureMetadata extends LitElement {
238
214
  ];
239
215
  }
240
216
 
241
- // Semantics data
242
- const hasSemantics = (
243
- (this._meta.properties.semantics || []).length > 0
244
- || (this._meta.properties.annotations || []).length > 0
245
- );
246
- let semanticsData = [];
247
- if(hasSemantics) {
248
- // Hashtags
249
- const hashtags = getHashTags(this._meta);
250
- if(hashtags.length > 0) {
251
- semanticsData.push({
252
- title: this._parent?._t.pnx.semantics_hashtags,
253
- style: "width: 100%",
254
- content: hashtags.join(" ")
255
- });
256
- }
257
-
258
- // Full list of picture tags
259
- semanticsData.push({
260
- title: this._parent?._t.pnx.semantics_tags_picture,
261
- style: "width: 100%",
262
- content: html`${this._meta.properties.semantics?.length > 0
263
- ? html`
264
- <pnx-button
265
- kind="outline"
266
- size="sm"
267
- style="width: 100%"
268
- @click=${() => this._semanticsPicShowAll = !this._semanticsPicShowAll}
269
- >
270
- ${this._semanticsPicShowAll ? fa(faChevronUp) : fa(faChevronDown)}
271
- ${this._semanticsPicShowAll ? this._parent?._t.pnx.semantics_hide_all_tags : this._parent?._t.pnx.semantics_show_all_tags}
272
- </pnx-button>
273
- <pnx-semantics-table
274
- ._t=${this._parent?._t}
275
- .source=${this._meta.properties}
276
- style="margin-top: 5px"
277
- class=${this._semanticsPicShowAll ? "":"pnx-hidden"}
278
- />
279
- `
280
- : this._parent?._t.pnx.semantics_tags_picture_none
281
- }`
282
- });
283
-
284
- // Annotations (features in picture)
285
- semanticsData.push({
286
- title: this._parent?._t.pnx.semantics_features,
287
- style: "width: 100%",
288
- content: html`
289
- ${this._meta.properties.annotations?.length > 0
290
- ? html`<pnx-annotations-list ._parent=${this._parent} />`
291
- : this._parent?._t.pnx.semantics_features_none}
292
- `
293
- });
294
- }
295
-
296
217
  // EXIF data
297
218
  const exifData = Object.entries(this._meta.properties.exif)
298
219
  .sort()
@@ -426,10 +347,11 @@ export default class PictureMetadata extends LitElement {
426
347
  qualityData
427
348
  ) : nothing}
428
349
 
429
- ${hasSemantics ? this._toTab( // Semantics
430
- html`${fa(faTags)} ${this._parent?._t.pnx.semantics_title}`,
431
- semanticsData
432
- ) : nothing}
350
+ <h4 slot="title">${fa(faTags)} ${this._parent?._t.pnx.semantics_title}</h4>
351
+ <div slot="content" class="data-blocks">
352
+ <pnx-semantics-metadata ._parent=${this._parent}></pnx-semantics-metadata>
353
+ </div>
354
+ </div>
433
355
 
434
356
  ${this._meta.properties?.exif ? this._toTab( // EXIF
435
357
  html`${fa(faScroll)} ${this._parent?._t.pnx.metadata_exif}`,
@@ -0,0 +1,186 @@
1
+ import { LitElement, html, nothing, css } from "lit";
2
+ import { fa, onceParentAvailable } from "../../utils/widgets";
3
+ import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown";
4
+ import { faChevronUp } from "@fortawesome/free-solid-svg-icons/faChevronUp";
5
+ import { faPen } from "@fortawesome/free-solid-svg-icons/faPen";
6
+ import { faFloppyDisk } from "@fortawesome/free-solid-svg-icons/faFloppyDisk";
7
+ import { faRotateLeft } from "@fortawesome/free-solid-svg-icons/faRotateLeft";
8
+ import { getHashTags } from "../../utils/picture";
9
+ import { hidden, dataBlocks } from "../styles";
10
+ import { getUserAccount } from "../../utils/utils";
11
+
12
+ /**
13
+ * Semantics metadata displays detailed info about semantic attributes of a single picture.
14
+ * @class Panoramax.components.menus.SemanticsMetadata
15
+ * @element pnx-semantics-metadata
16
+ * @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
17
+ * @example
18
+ * ```html
19
+ * <pnx-semantics-metadata ._parent=${viewer}></pnx-semantics-metadata>
20
+ * ```
21
+ */
22
+ export default class SemanticsMetadata extends LitElement {
23
+ /** @private */
24
+ static styles = [ hidden, dataBlocks, css`
25
+ .data-block {
26
+ width: 100%;
27
+ }
28
+ ` ];
29
+
30
+ /** @private */
31
+ static properties = {
32
+ _meta: {state: true},
33
+ _picShowAll: {state: true},
34
+ _editPicSem: {state: true},
35
+ _editedPicSemTags: {state: true},
36
+ };
37
+
38
+ constructor() {
39
+ super();
40
+ this._meta = {};
41
+ this._picShowAll = false;
42
+ this._editPicSem = false;
43
+ this._editedPicSemTags = null;
44
+ }
45
+
46
+ /** @private */
47
+ connectedCallback() {
48
+ super.connectedCallback();
49
+
50
+ onceParentAvailable(this).then(() => {
51
+ this._meta = this._parent?.psv?.getPictureMetadata();
52
+ this._parent?.oncePSVReady?.().then(() => {
53
+ this._parent.psv.addEventListener("picture-loaded", () => {
54
+ this._meta = this._parent.psv.getPictureMetadata();
55
+ });
56
+ this._parent.psv.addEventListener("annotation-click", () => {
57
+ const tabs = this.shadowRoot.querySelector("pnx-tabs");
58
+ if(tabs) { tabs.setAttribute("activeTabIndex", 4); }
59
+ });
60
+ });
61
+ });
62
+ }
63
+
64
+ /** @private */
65
+ _onStartEditPicSem() {
66
+ this._editedPicSemTags = null;
67
+ this._editPicSem = true;
68
+ }
69
+
70
+ /** @private */
71
+ _onPicSemEditorChange(e) {
72
+ this._editedPicSemTags = e.detail;
73
+ }
74
+
75
+ /** @private */
76
+ _onSavePicSem() {
77
+ const field = this.renderRoot.querySelector("#pnx-sem-pic-editor");
78
+
79
+ // Check field validity
80
+ if(!field || !field.checkValidity()) {
81
+ alert(this._parent?._t.pnx.semantics_cantsave_invalid);
82
+ return;
83
+ }
84
+
85
+ // Send changes to API
86
+ this._parent?.api.sendPictureSemantics(this._meta, this._editedPicSemTags.delta).then(newPic => {
87
+ const newMeta = Object.assign({}, this._meta);
88
+ newMeta.properties.semantics = newPic.properties.semantics;
89
+ this._meta = newMeta;
90
+ this._editPicSem = false;
91
+ alert(this._parent?._t.pnx.semantics_send_ok);
92
+ }).catch(e => {
93
+ this._editPicSem = false;
94
+ alert(this._parent?._t.pnx.semantics_send_fail);
95
+ console.error("Can't send semantics", e);
96
+ });
97
+ }
98
+
99
+ /** @private */
100
+ render() {
101
+ /* eslint-disable indent */
102
+ if(!this._meta?.properties) { return; }
103
+
104
+ const hashtags = getHashTags(this._meta);
105
+ const canEdit = !this._meta.origInstance && getUserAccount() !== null;
106
+
107
+ return html`
108
+ ${hashtags.length > 0 ? html`
109
+ <div class="data-block">
110
+ <h5>${this._parent?._t.pnx.semantics_hashtags}</h5>
111
+ <div>${hashtags.join(" ")}</div>
112
+ </div>
113
+ ` : nothing}
114
+
115
+ <div class="data-block">
116
+ <h5>
117
+ ${this._parent?._t.pnx.semantics_tags_picture}
118
+
119
+ ${canEdit && this._editPicSem ?
120
+ html`<pnx-button-group style="display: inline-block; vertical-align: middle;">
121
+ <pnx-button
122
+ kind="superinline"
123
+ title=${this._parent?._t.pnx.semantics_undo}
124
+ @click=${() => this._editPicSem = false}
125
+ >
126
+ ${fa(faRotateLeft)}
127
+ </pnx-button>
128
+ <pnx-button
129
+ kind="superinline"
130
+ title=${this._parent?._t.pnx.semantics_save}
131
+ @click=${this._onSavePicSem}
132
+ >
133
+ ${fa(faFloppyDisk)}
134
+ </pnx-button>
135
+ </pnx-button-group>`
136
+ : (canEdit ? html`<pnx-button
137
+ kind="superinline"
138
+ title=${this._parent?._t.pnx.semantics_edit}
139
+ @click=${this._onStartEditPicSem}
140
+ >
141
+ ${fa(faPen)}
142
+ </pnx-button>` : "")
143
+ }
144
+ </h5>
145
+ <div>
146
+ ${this._editPicSem ? html`
147
+ <pnx-semantics-editor
148
+ id="pnx-sem-pic-editor"
149
+ .semantics=${this._editedPicSemTags ? this._editedPicSemTags.semantics : this._meta.properties.semantics}
150
+ ._t=${this._parent._t}
151
+ @change=${this._onPicSemEditorChange}
152
+ ></pnx-semantics-editor>
153
+ ` : html`
154
+ ${this._meta.properties.semantics?.length > 0 ? html`
155
+ <pnx-button
156
+ kind="outline"
157
+ size="sm"
158
+ style="width: 100%"
159
+ @click=${() => this._picShowAll = !this._picShowAll}
160
+ >
161
+ ${this._picShowAll ? fa(faChevronUp) : fa(faChevronDown)}
162
+ ${this._picShowAll ? this._parent?._t.pnx.semantics_hide_all_tags : this._parent?._t.pnx.semantics_show_all_tags}
163
+ </pnx-button>
164
+ <pnx-semantics-table
165
+ ._t=${this._parent?._t}
166
+ .source=${this._meta.properties}
167
+ style="margin-top: 5px"
168
+ class=${this._picShowAll ? "":"pnx-hidden"}
169
+ />`
170
+ : this._parent?._t.pnx.semantics_tags_picture_none}
171
+ `}
172
+ </div>
173
+ </div>
174
+
175
+ <div class="data-block">
176
+ <h5>${this._parent?._t.pnx.semantics_features}</h5>
177
+ <div>${this._meta.properties.annotations?.length > 0
178
+ ? html`<pnx-annotations-list ._parent=${this._parent} />`
179
+ : this._parent?._t.pnx.semantics_features_none}
180
+ </div>
181
+ </div>
182
+ `;
183
+ }
184
+ }
185
+
186
+ customElements.define("pnx-semantics-metadata", SemanticsMetadata);
@@ -14,4 +14,5 @@ export {default as PictureMetadata} from "./PictureMetadata";
14
14
  export {default as PlayerOptions} from "./PlayerOptions";
15
15
  export {default as QualityScoreDoc} from "./QualityScoreDoc";
16
16
  export {default as ReportForm} from "./ReportForm";
17
+ export {default as SemanticsMetadata} from "./SemanticsMetadata";
17
18
  export {default as Share} from "./Share";
@@ -149,6 +149,7 @@ export const btn = css`
149
149
  .pnx-btn ::slotted(.svg-inline--fa),
150
150
  .pnx-btn slot .svg-inline--fa {
151
151
  height: 16px;
152
+ pointer-events: none;
152
153
  }
153
154
 
154
155
  /* Sizing */
@@ -487,3 +488,24 @@ export const iconify = css`
487
488
  height: 1em;
488
489
  }
489
490
  `;
491
+
492
+ // Tabbed-legend data blocks
493
+ export const dataBlocks = css`
494
+ .data-block {
495
+ display: inline-block;
496
+ margin: 8px 0;
497
+ box-sizing: border-box;
498
+ vertical-align: top;
499
+ }
500
+ .data-block h5 {
501
+ font-size: 0.8em;
502
+ font-weight: 400;
503
+ color: var(--blue-dark);
504
+ margin: 0 0 5px 0;
505
+ }
506
+ .data-block h5 pnx-button { vertical-align: middle; }
507
+ .data-block h5 svg.svg-inline--fa { height: 14px; }
508
+ .data-block div {
509
+ font-size: 0.8em;
510
+ }
511
+ `;
@@ -82,7 +82,7 @@ export default class SemanticsEditor extends LitElement {
82
82
  getDiff() {
83
83
  return computeDiffTags(this._firstSemantics || [], this.semantics);
84
84
  }
85
-
85
+
86
86
  /**
87
87
  * Check if input is having a valid value.
88
88
  * @memberof Panoramax.components.ui.SemanticsEditor#
@@ -16,6 +16,7 @@ import { groupByPrefix } from "../../utils/semantics";
16
16
  export default class SemanticsTable extends LitElement {
17
17
  /** @private */
18
18
  static styles = [ table, css`
19
+ :host { display: block; }
19
20
  th:first-child, td:first-child { width: 30%; }
20
21
  td { vertical-align: top; }
21
22
 
@@ -201,6 +201,12 @@
201
201
  },
202
202
  "semantics_editor_error": "The syntax is invalid. Your tags may look like:\nkey=value\nprefix|key=value\nprefix|key[qualif_key=qualif_val]=value\n\nMax key length is 256 characters, max value length 2048 characters.",
203
203
  "semantics_editor_example": "key=value\nprefix|key=value",
204
+ "semantics_edit": "Edit attributes",
205
+ "semantics_save": "Save your edits",
206
+ "semantics_undo": "Cancel your edits",
207
+ "semantics_cantsave_invalid": "Can't save your attributes, input is not valid",
208
+ "semantics_send_fail": "We can't send your edits for now, please retry later.",
209
+ "semantics_send_ok": "Your edits were successfully sent",
204
210
  "report": "Report",
205
211
  "report_auth": "This report will be sent using your account \"{a}\"",
206
212
  "report_nature_label": "Nature of the issue",
@@ -201,6 +201,12 @@
201
201
  },
202
202
  "semantics_editor_error": "La syntaxe est invalide. Vos attributs doivent avoir cette forme:\nclé=valeur\npréfixe|clé=valeur\npréfixe|clé[qualif_clé=qualif_val]=valeur\n\nLongueur max des clés : 256 caractères, max des valeurs 2048 caractères.",
203
203
  "semantics_editor_example": "clé=valeur\npréfixe|clé=valeur",
204
+ "semantics_edit": "Modifier les attributs",
205
+ "semantics_save": "Enregistrer vos modifications",
206
+ "semantics_undo": "Annuler vos modifications",
207
+ "semantics_cantsave_invalid": "Impossible de sauvegarder les modifications, les attributs ne sont pas valides",
208
+ "semantics_send_fail": "Nous n'avons pas pu enregistrer vos modifications, merci de réessayer plus tard",
209
+ "semantics_send_ok": "Vos modifications ont bien été enregistrées",
204
210
  "report": "Signaler",
205
211
  "report_auth": "Ce signalement sera envoyé en utilisant votre compte \"{a}\"",
206
212
  "report_nature_label": "Nature du problème",
package/src/utils/API.js CHANGED
@@ -789,6 +789,46 @@ export default class API extends EventTarget {
789
789
  });
790
790
  }
791
791
 
792
+ /**
793
+ * Send picture semantics change to origin API.
794
+ * @memberOf Panoramax.utils.API#
795
+ * @param {object} picMeta The picture metadata
796
+ * @param {object} semanticsDiff The difference in semantics compared to original data
797
+ * @returns {Promise}
798
+ * @fulfil {object} The JSON API response
799
+ */
800
+ sendPictureSemantics(picMeta, semanticsDiff) {
801
+ /* eslint-disable */
802
+ if(!this.isReady()) { throw new Error("API is not ready to use"); }
803
+ if(!picMeta?.sequence?.id || !picMeta?.id) { throw new Error("Missing IDs from picture"); }
804
+
805
+ // Check if it's from metacatalog
806
+ let picLink = this.getPictureMetadataUrl(picMeta.id, picMeta.sequence.id);
807
+ if(picMeta?.origInstance) {
808
+ picLink = `${picMeta.origInstance.href}/api/collections/${picMeta.sequence.id}/items/${picMeta.id}`;
809
+ }
810
+
811
+ const opts = {
812
+ ...this._getFetchOptions(),
813
+ method: "PATCH",
814
+ body: JSON.stringify({ semantics: semanticsDiff }),
815
+ headers: { "Content-Type": "application/json" },
816
+ };
817
+
818
+ return fetch(picLink, opts)
819
+ .then(async res => {
820
+ if(res.status >= 400) {
821
+ let txt = await res.text();
822
+ try {
823
+ txt = JSON.parse(txt)["message"];
824
+ }
825
+ catch(e) {} // eslint-disable-line no-empty
826
+ return Promise.reject(txt);
827
+ }
828
+ return res.json();
829
+ });
830
+ }
831
+
792
832
  /**
793
833
  * Checks URL string validity
794
834
  * @memberOf Panoramax.utils.API