@eodash/eodash 5.1.0 → 5.3.0
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.
- package/core/client/components/DashboardLayout.vue +1 -2
- package/core/client/components/EodashOverlay.vue +4 -5
- package/core/client/components/MobileLayout.vue +42 -21
- package/core/client/composables/index.js +54 -60
- package/core/client/eodashSTAC/EodashCollection.js +199 -108
- package/core/client/eodashSTAC/auth.js +86 -0
- package/core/client/eodashSTAC/createLayers.js +234 -4
- package/core/client/eodashSTAC/helpers.js +281 -59
- package/core/client/eodashSTAC/parquet.js +0 -13
- package/core/client/eodashSTAC/triggers.js +1 -1
- package/core/client/store/actions.js +14 -0
- package/core/client/store/stac.js +46 -8
- package/core/client/store/states.js +6 -0
- package/core/client/types.ts +206 -3
- package/core/client/utils/bands-editor/arithmetic.js +144 -0
- package/core/client/utils/bands-editor/colors.js +36 -0
- package/core/client/utils/bands-editor/dom.js +196 -0
- package/core/client/utils/bands-editor/exampleSchema.json +1320 -0
- package/core/client/utils/bands-editor/index.js +68 -0
- package/core/client/utils/bands-editor/rgb.js +102 -0
- package/core/client/utils/index.js +5 -2
- package/core/client/views/Dashboard.vue +1 -1
- package/core/client/vite-env.d.ts +122 -0
- package/dist/client/{DashboardLayout-ByVs1DrY.js → DashboardLayout-Cq15p4TH.js} +5 -6
- package/dist/client/{DynamicWebComponent-C3W7HSQm.js → DynamicWebComponent-Cv-fPRG1.js} +1 -1
- package/dist/client/{EodashDatePicker-BIAf1sMT.js → EodashDatePicker-CPlJwEIO.js} +20 -22
- package/dist/client/{EodashItemFilter-DPznh8UB.js → EodashItemFilter-Ydebgbjj.js} +46 -31
- package/dist/client/EodashLayerControl-COhrkNEs.js +1517 -0
- package/dist/client/{EodashLayoutSwitcher-C5qTEffW.js → EodashLayoutSwitcher-pnKhTRZV.js} +4 -4
- package/dist/client/EodashMapBtns-Cj0Fx119.js +301 -0
- package/dist/client/{EodashStacInfo-CSvvF2jI.js → EodashStacInfo-Dadkg_Nj.js} +1 -1
- package/dist/client/EodashTimeSlider-CpoHX0S7.js +53 -0
- package/dist/client/{EodashTools-Cv1SXQ5y.js → EodashTools-UGBG7KC9.js} +10 -7
- package/dist/client/{ExportState-D-iuwaad.js → ExportState-GtJkAqeZ.js} +145 -121
- package/dist/client/{Footer-CyF0zRAk.js → Footer-D3ZPG5c4.js} +1 -1
- package/dist/client/{Header-CgD8jDKU.js → Header-z6AK-wpN.js} +2 -3
- package/dist/client/MobileLayout-BXNsNftb.js +118 -0
- package/dist/client/{PopUp-BsYLvWch.js → PopUp-BbQdjENV.js} +79 -44
- package/dist/client/{ProcessList-C2xsLU2_.js → ProcessList-C6VsdsYI.js} +18 -12
- package/dist/client/{VImg-OHe8YTs2.js → VImg-CxaMSB99.js} +203 -5
- package/dist/client/{VMain-PryTLU4a.js → VMain-Ds7yw0wj.js} +1 -1
- package/dist/client/{VTooltip-DZ0fjpB3.js → VTooltip-Cze6CEVh.js} +2 -3
- package/dist/client/{WidgetsContainer-B9LBadcC.js → WidgetsContainer-D66bj-JJ.js} +1 -1
- package/dist/client/asWebComponent-CWbNRdf9.js +8895 -0
- package/dist/client/{async-DkSu_u2K.js → async-BA7oWCMX.js} +69 -5
- package/dist/client/easing-CH0-9wR8.js +35 -0
- package/dist/client/eo-dash.js +1 -1
- package/dist/client/{VOverlay-yUn7p-Uf.js → forwardRefs-BUfxOIo-.js} +308 -28
- package/dist/client/{handling-CgmFXkW6.js → handling-DlNTtKB-.js} +27 -6
- package/dist/client/{helpers-Dy0Q13tP.js → helpers-CtE0W7iu.js} +595 -278
- package/dist/client/{index-skjhlH8u.js → index-CeEZIjO6.js} +26 -13
- package/dist/client/{index-Ch_HchK3.js → index-CsKbRDeN.js} +238 -77
- package/dist/client/{index-Dqj4tbx2.js → index-D4_NRKrf.js} +2 -2
- package/dist/client/index-DeECc3lV.js +571 -0
- package/dist/client/material-symbols-outlined.woff2 +0 -0
- package/dist/client/material-symbols-rounded.woff2 +0 -0
- package/dist/client/material-symbols-sharp.woff2 +0 -0
- package/dist/client/material-symbols-subset.woff2 +0 -0
- package/dist/client/templates.js +106 -49
- package/dist/client/{transition-C98Yn4Vo.js → transition-Byvp3L6Y.js} +1 -1
- package/dist/node/cli.js +6 -6
- package/dist/types/core/client/eodashSTAC/EodashCollection.d.ts +24 -10
- package/dist/types/core/client/eodashSTAC/auth.d.ts +7 -0
- package/dist/types/core/client/eodashSTAC/createLayers.d.ts +15 -3
- package/dist/types/core/client/eodashSTAC/helpers.d.ts +51 -15
- package/dist/types/core/client/plugins/vuetify.d.ts +14 -14
- package/dist/types/core/client/store/actions.d.ts +2 -0
- package/dist/types/core/client/store/stac.d.ts +16 -7
- package/dist/types/core/client/store/states.d.ts +4 -0
- package/dist/types/core/client/types.d.ts +171 -3
- package/dist/types/core/client/utils/bands-editor/arithmetic.d.ts +8 -0
- package/dist/types/core/client/utils/bands-editor/colors.d.ts +15 -0
- package/dist/types/core/client/utils/bands-editor/dom.d.ts +42 -0
- package/dist/types/core/client/utils/bands-editor/index.d.ts +20 -0
- package/dist/types/core/client/utils/bands-editor/rgb.d.ts +15 -0
- package/dist/types/core/client/utils/index.d.ts +1 -1
- package/dist/types/templates/baseConfig.d.ts +87 -1
- package/dist/types/templates/compare.d.ts +0 -25
- package/dist/types/templates/expert.d.ts +17 -21
- package/dist/types/templates/explore.d.ts +67 -0
- package/dist/types/templates/index.d.ts +1 -1
- package/dist/types/templates/{light.d.ts → lite.d.ts} +9 -0
- package/dist/types/widgets/EodashItemCatalog/index.vue.d.ts +21 -0
- package/dist/types/widgets/EodashItemCatalog/methods/filters.d.ts +49 -0
- package/dist/types/widgets/EodashItemCatalog/methods/handlers.d.ts +4 -0
- package/dist/types/widgets/EodashItemCatalog/methods/map.d.ts +12 -0
- package/dist/types/widgets/EodashItemCatalog/types.d.ts +14 -0
- package/dist/types/widgets/{EodashMapBtns.vue.d.ts → EodashMap/EodashMapBtns.vue.d.ts} +6 -0
- package/dist/types/widgets/EodashMap/index.vue.d.ts +114 -0
- package/dist/types/widgets/EodashMap/methods/create-layers-config.d.ts +1 -1
- package/dist/types/widgets/EodashMap/methods/index.d.ts +1 -1
- package/dist/types/widgets/EodashProcess/methods/async.d.ts +1 -0
- package/dist/types/widgets/EodashProcess/methods/custom-endpoints/layers/eoxhub-workspaces-endpoint.d.ts +1 -1
- package/dist/types/widgets/EodashTimeSlider.vue.d.ts +7 -0
- package/dist/types/widgets/EodashTools.vue.d.ts +10 -10
- package/dist/types/widgets/ExportState.vue.d.ts +2 -0
- package/package.json +31 -28
- package/templates/baseConfig.js +10 -5
- package/templates/compare.js +2 -22
- package/templates/expert.js +19 -18
- package/templates/explore.js +62 -0
- package/templates/index.js +1 -1
- package/templates/{light.js → lite.js} +11 -2
- package/widgets/EodashDatePicker.vue +15 -18
- package/widgets/EodashItemCatalog/index.vue +161 -0
- package/widgets/EodashItemCatalog/methods/filters.js +216 -0
- package/widgets/EodashItemCatalog/methods/handlers.js +50 -0
- package/widgets/EodashItemCatalog/methods/map.js +144 -0
- package/widgets/EodashItemCatalog/types.ts +15 -0
- package/widgets/EodashItemFilter.vue +35 -28
- package/widgets/EodashLayerControl.vue +10 -6
- package/widgets/EodashLayoutSwitcher.vue +1 -1
- package/widgets/EodashMap/EodashMapBtns.vue +278 -0
- package/widgets/EodashMap/index.vue +263 -38
- package/widgets/EodashMap/methods/create-layers-config.js +9 -6
- package/widgets/EodashMap/methods/index.js +27 -13
- package/widgets/EodashProcess/ProcessList.vue +13 -1
- package/widgets/EodashProcess/index.vue +17 -1
- package/widgets/EodashProcess/methods/async.js +22 -1
- package/widgets/EodashProcess/methods/custom-endpoints/chart/veda-endpoint.js +25 -3
- package/widgets/EodashProcess/methods/handling.js +2 -0
- package/widgets/EodashProcess/methods/outputs.js +1 -0
- package/widgets/EodashProcess/methods/utils.js +45 -1
- package/widgets/EodashTimeSlider.vue +40 -0
- package/widgets/EodashTools.vue +7 -3
- package/widgets/ExportState.vue +53 -22
- package/dist/client/EodashLayerControl-Bhxjw4V2.js +0 -154
- package/dist/client/EodashMapBtns-WoGq8MuV.js +0 -173
- package/dist/client/MobileLayout-EKQ_kpSh.js +0 -1226
- package/dist/client/asWebComponent-By_7_JjS.js +0 -19193
- package/dist/client/forwardRefs-BXxrv98s.js +0 -272
- package/dist/client/index-BuhOHXKv.js +0 -199
- package/widgets/EodashMapBtns.vue +0 -155
|
@@ -0,0 +1,1517 @@
|
|
|
1
|
+
import { withAsyncContext, computed, ref, createElementBlock, openBlock, createCommentVNode, mergeProps, unref, renderSlot, createElementVNode, toDisplayString } from 'vue';
|
|
2
|
+
import 'color-legend-element';
|
|
3
|
+
import '@eox/timecontrol';
|
|
4
|
+
import { M as mapCompareEl, h as mapEl, N as getColFromLayer } from './helpers-CtE0W7iu.js';
|
|
5
|
+
import { _ as _export_sfc, E as useSTAcStore, M as layerControlFormValueCompare, N as layerControlFormValue, J as eodashCompareCollections, I as eodashCollections } from './asWebComponent-CWbNRdf9.js';
|
|
6
|
+
import { storeToRefs } from 'pinia';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Taken from jQuery 2.1.3
|
|
10
|
+
*
|
|
11
|
+
* #### NOTE
|
|
12
|
+
* Not plain objects is,
|
|
13
|
+
* - Any object or value whose internal [[Class]] property is not "[object Object]"
|
|
14
|
+
* - DOM nodes
|
|
15
|
+
* - window
|
|
16
|
+
*
|
|
17
|
+
* @param {Object} obj - Variable name
|
|
18
|
+
* @returns {Boolean}
|
|
19
|
+
*/
|
|
20
|
+
function isPlainObject (obj) {
|
|
21
|
+
if (obj === null) return false
|
|
22
|
+
|
|
23
|
+
if (typeof obj !== 'object' || obj.nodeType || (obj === obj.window)) return false
|
|
24
|
+
|
|
25
|
+
if (obj.constructor && !hasOwnProperty(obj.constructor.prototype, 'isPrototypeOf')) return false
|
|
26
|
+
|
|
27
|
+
/* Most likely |obj| is a plain object, created by {} or constructed with new Object */
|
|
28
|
+
return true
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function deepCopy (target) {
|
|
32
|
+
return isPlainObject(target) ? extend({}, target) : Array.isArray(target) ? target.map(deepCopy) : target
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function extend (destination, ...args) {
|
|
36
|
+
args.forEach(source => {
|
|
37
|
+
if (source) {
|
|
38
|
+
Object.keys(source).forEach(property => {
|
|
39
|
+
if (source[property] && isPlainObject(source[property])) {
|
|
40
|
+
if (!hasOwnProperty(destination, property)) destination[property] = {};
|
|
41
|
+
extend(destination[property], source[property]);
|
|
42
|
+
} else if (Array.isArray(source[property])) {
|
|
43
|
+
destination[property] = deepCopy(source[property]);
|
|
44
|
+
} else {
|
|
45
|
+
destination[property] = source[property];
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return destination
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Helper function to check own property key
|
|
56
|
+
*
|
|
57
|
+
* @see https://eslint.org/docs/rules/no-prototype-builtins
|
|
58
|
+
*/
|
|
59
|
+
function hasOwnProperty (obj, key) {
|
|
60
|
+
return obj && Object.prototype.hasOwnProperty.call(obj, key)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* All editors should extend from this class
|
|
65
|
+
*/
|
|
66
|
+
class AbstractEditor {
|
|
67
|
+
constructor (options, defaults) {
|
|
68
|
+
this.defaults = defaults;
|
|
69
|
+
this.jsoneditor = options.jsoneditor;
|
|
70
|
+
this.theme = this.jsoneditor.theme;
|
|
71
|
+
this.template_engine = this.jsoneditor.template;
|
|
72
|
+
this.iconlib = this.jsoneditor.iconlib;
|
|
73
|
+
this.translate = this.jsoneditor.translate || this.defaults.translate;
|
|
74
|
+
this.translateProperty = this.jsoneditor.translateProperty || this.defaults.translateProperty;
|
|
75
|
+
this.original_schema = options.schema;
|
|
76
|
+
this.schema = this.jsoneditor.expandSchema(this.original_schema);
|
|
77
|
+
this.active = true;
|
|
78
|
+
this.isUiOnly = false;
|
|
79
|
+
this.options = extend({}, (this.options || {}), (this.schema.options || {}), (options.schema.options || {}), options);
|
|
80
|
+
this.enforceConstEnabled = this.options.enforce_const ?? this.jsoneditor.options.enforce_const;
|
|
81
|
+
this.formname = this.jsoneditor.options.form_name_root || 'root';
|
|
82
|
+
|
|
83
|
+
if (!options.path && !this.schema.id) this.schema.id = this.formname;
|
|
84
|
+
this.path = options.path || this.formname;
|
|
85
|
+
this.formname = options.formname || this.path.replace(/\.([^.]+)/g, '[$1]');
|
|
86
|
+
|
|
87
|
+
this.parent = options.parent;
|
|
88
|
+
this.key = this.parent !== undefined ? this.path.split('.').slice(this.parent.path.split('.').length).join('.') : this.path;
|
|
89
|
+
|
|
90
|
+
this.link_watchers = [];
|
|
91
|
+
this.watchLoop = false;
|
|
92
|
+
this.optInWidget = this.options.opt_in_widget ?? this.jsoneditor.options.opt_in_widget;
|
|
93
|
+
|
|
94
|
+
if (options.container) this.setContainer(options.container);
|
|
95
|
+
this.registerDependencies();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
onChildEditorChange (editor, eventData) {
|
|
99
|
+
this.onChange(true, false, eventData);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
notify () {
|
|
103
|
+
if (this.path) this.jsoneditor.notifyWatchers(this.path);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
change (eventData) {
|
|
107
|
+
if (this.parent) this.parent.onChildEditorChange(this, eventData);
|
|
108
|
+
else if (this.jsoneditor) this.jsoneditor.onChange(eventData);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
onChange (bubble, fromTemplate, eventData) {
|
|
112
|
+
this.notify();
|
|
113
|
+
|
|
114
|
+
if (!fromTemplate) {
|
|
115
|
+
if (this.watch_listener) this.watch_listener();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (bubble) this.change(eventData);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
register () {
|
|
122
|
+
this.jsoneditor.registerEditor(this);
|
|
123
|
+
if (this.input && !this.label) {
|
|
124
|
+
const ariaLabel = this.getTitle() || this.formname;
|
|
125
|
+
this.input.setAttribute('aria-label', ariaLabel);
|
|
126
|
+
}
|
|
127
|
+
this.onChange();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
unregister () {
|
|
131
|
+
if (!this.jsoneditor) return
|
|
132
|
+
this.jsoneditor.unregisterEditor(this);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
getNumColumns () {
|
|
136
|
+
return 12
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
isActive () {
|
|
140
|
+
return this.active
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
activate () {
|
|
144
|
+
this.active = true;
|
|
145
|
+
this.optInCheckbox.checked = true;
|
|
146
|
+
this.enable();
|
|
147
|
+
this.change();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
deactivate () {
|
|
151
|
+
/* only non required properties can be deactivated. */
|
|
152
|
+
if (!this.isRequired()) {
|
|
153
|
+
this.active = false;
|
|
154
|
+
this.optInCheckbox.checked = false;
|
|
155
|
+
this.disable();
|
|
156
|
+
this.change();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
registerDependencies () {
|
|
161
|
+
this.dependenciesFulfilled = true;
|
|
162
|
+
const deps = this.options.dependencies;
|
|
163
|
+
if (!deps) {
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
Object.keys(deps).forEach(dependency => {
|
|
168
|
+
let path;
|
|
169
|
+
const isFullPath = dependency.startsWith(this.jsoneditor.root.path);
|
|
170
|
+
|
|
171
|
+
if (isFullPath) {
|
|
172
|
+
path = dependency;
|
|
173
|
+
} else {
|
|
174
|
+
path = this.path.split('.');
|
|
175
|
+
path[path.length - 1] = dependency;
|
|
176
|
+
path = path.join('.');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
this.jsoneditor.watch(path, () => {
|
|
180
|
+
this.evaluateDependencies();
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
evaluateDependencies () {
|
|
186
|
+
const wrapper = this.container || this.control;
|
|
187
|
+
if (!wrapper || this.jsoneditor === null) {
|
|
188
|
+
return
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const deps = this.options.dependencies;
|
|
192
|
+
if (!deps) {
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Assume true and set to false if any unmet dependencies are found
|
|
197
|
+
const previousStatus = this.dependenciesFulfilled;
|
|
198
|
+
this.dependenciesFulfilled = true;
|
|
199
|
+
|
|
200
|
+
Object.keys(deps).forEach(dependency => {
|
|
201
|
+
let path;
|
|
202
|
+
const isFullPath = dependency.startsWith(this.jsoneditor.root.path);
|
|
203
|
+
|
|
204
|
+
if (isFullPath) {
|
|
205
|
+
path = dependency;
|
|
206
|
+
} else {
|
|
207
|
+
path = this.path.split('.');
|
|
208
|
+
path[path.length - 1] = dependency;
|
|
209
|
+
path = path.join('.');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const choices = deps[dependency];
|
|
213
|
+
this.checkDependency(path, choices);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
if (this.dependenciesFulfilled !== previousStatus) {
|
|
217
|
+
this.notify();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
let displayMode = this.dependenciesFulfilled ? 'block' : 'none';
|
|
221
|
+
|
|
222
|
+
if (this.options.hidden) {
|
|
223
|
+
displayMode = 'none';
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (wrapper.tagName === 'TD') {
|
|
227
|
+
Object.keys(wrapper.childNodes).forEach(child => (wrapper.childNodes[child].style.display = displayMode));
|
|
228
|
+
} else wrapper.style.display = displayMode;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
checkDependency (path, choices) {
|
|
232
|
+
if (this.path === path || this.jsoneditor === null) {
|
|
233
|
+
return
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const editor = this.jsoneditor.getEditor(path);
|
|
237
|
+
const value = editor ? editor.getValue() : undefined;
|
|
238
|
+
|
|
239
|
+
if (!editor || !editor.dependenciesFulfilled || !value) {
|
|
240
|
+
this.dependenciesFulfilled = false;
|
|
241
|
+
} else if (Array.isArray(choices)) {
|
|
242
|
+
this.dependenciesFulfilled = choices.some(choice => {
|
|
243
|
+
if (JSON.stringify(value) === JSON.stringify(choice)) {
|
|
244
|
+
return true
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
} else if (typeof choices === 'object') {
|
|
248
|
+
if (typeof value !== 'object') {
|
|
249
|
+
this.dependenciesFulfilled = choices === value;
|
|
250
|
+
} else {
|
|
251
|
+
Object.keys(choices).some(key => {
|
|
252
|
+
if (!hasOwnProperty(choices, key)) {
|
|
253
|
+
return false
|
|
254
|
+
}
|
|
255
|
+
if (!hasOwnProperty(value, key) || choices[key] !== value[key]) {
|
|
256
|
+
this.dependenciesFulfilled = false;
|
|
257
|
+
return true
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
} else if (typeof choices === 'string' || typeof choices === 'number') {
|
|
262
|
+
this.dependenciesFulfilled = this.dependenciesFulfilled && value === choices;
|
|
263
|
+
} else if (typeof choices === 'boolean') {
|
|
264
|
+
if (choices) {
|
|
265
|
+
this.dependenciesFulfilled = this.dependenciesFulfilled && (value || value.length > 0);
|
|
266
|
+
} else {
|
|
267
|
+
this.dependenciesFulfilled = this.dependenciesFulfilled && (!value || value.length === 0);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
setContainer (container) {
|
|
273
|
+
this.container = container;
|
|
274
|
+
this.setContainerAttributes();
|
|
275
|
+
if (this.schema.id) this.container.setAttribute('data-schemaid', this.schema.id);
|
|
276
|
+
if (this.schema.type && typeof this.schema.type === 'string') this.container.setAttribute('data-schematype', this.schema.type);
|
|
277
|
+
this.container.setAttribute('data-schemapath', this.path);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
setOptInCheckbox () {
|
|
281
|
+
let optIn;
|
|
282
|
+
|
|
283
|
+
if (this.optInWidget === 'switch') {
|
|
284
|
+
optIn = this.theme.getOptInSwitch(this.formname);
|
|
285
|
+
} else {
|
|
286
|
+
optIn = this.theme.getOptInCheckbox(this.formname);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
this.optInCheckbox = optIn.checkbox;
|
|
290
|
+
this.optInContainer = optIn.container;
|
|
291
|
+
|
|
292
|
+
this.optInCheckbox.addEventListener('click', () => {
|
|
293
|
+
if (this.isActive()) {
|
|
294
|
+
this.deactivate();
|
|
295
|
+
} else {
|
|
296
|
+
this.activate();
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
/* append active/deactive checkbox if show_opt_in is true */
|
|
301
|
+
const globalOptIn = this.jsoneditor.options.show_opt_in;
|
|
302
|
+
const parentOptInDefined = (typeof this.parent.options.show_opt_in !== 'undefined');
|
|
303
|
+
const parentOptInEnabled = (parentOptInDefined && this.parent.options.show_opt_in === true);
|
|
304
|
+
const parentOptInDisabled = (parentOptInDefined && this.parent.options.show_opt_in === false);
|
|
305
|
+
|
|
306
|
+
if (parentOptInEnabled || (!parentOptInDisabled && globalOptIn) || (!parentOptInDefined && globalOptIn)) {
|
|
307
|
+
/* and control to type object editors if they are not required */
|
|
308
|
+
if (this.parent && this.parent.schema.type === 'object' && !this.isRequired() && this.header) {
|
|
309
|
+
this.header.insertBefore(this.optInContainer, this.header.firstChild);
|
|
310
|
+
this.optInAppended = true;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
preBuild () {
|
|
316
|
+
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
build () {
|
|
320
|
+
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
postBuild () {
|
|
324
|
+
this.setupWatchListeners();
|
|
325
|
+
this.addLinks();
|
|
326
|
+
this.register();
|
|
327
|
+
this.setValue(this.getDefault(), true);
|
|
328
|
+
this.updateHeaderText();
|
|
329
|
+
this.onWatchedFieldChange();
|
|
330
|
+
|
|
331
|
+
if (this.options.titleHidden) {
|
|
332
|
+
this.theme.visuallyHidden(this.label);
|
|
333
|
+
this.theme.visuallyHidden(this.header);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (this.enforceConstEnabled && this.schema.const) {
|
|
337
|
+
this.disable();
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
setupWatchListeners () {
|
|
342
|
+
/* Watched fields */
|
|
343
|
+
this.watched = {};
|
|
344
|
+
if (this.schema.vars) this.schema.watch = this.schema.vars;
|
|
345
|
+
this.watched_values = {};
|
|
346
|
+
this.watch_listener = () => {
|
|
347
|
+
if (this.refreshWatchedFieldValues()) {
|
|
348
|
+
this.onWatchedFieldChange();
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
if (hasOwnProperty(this.schema, 'watch')) {
|
|
353
|
+
let path; let pathParts; let first; let root; let adjustedPath;
|
|
354
|
+
const myPath = this.container.getAttribute('data-schemapath');
|
|
355
|
+
|
|
356
|
+
Object.keys(this.schema.watch).forEach(name => {
|
|
357
|
+
path = this.schema.watch[name];
|
|
358
|
+
if (Array.isArray(path)) {
|
|
359
|
+
if (path.length < 2) return
|
|
360
|
+
pathParts = [path[0]].concat(path[1].split('.'));
|
|
361
|
+
} else {
|
|
362
|
+
pathParts = path.split('.');
|
|
363
|
+
if (!this.theme.closest(this.container, `[data-schemaid="${pathParts[0]}"]`)) pathParts.unshift('#');
|
|
364
|
+
}
|
|
365
|
+
first = pathParts.shift();
|
|
366
|
+
|
|
367
|
+
if (first === '#') first = this.jsoneditor.schema.id || this.jsoneditor.root.formname;
|
|
368
|
+
|
|
369
|
+
/* Find the root node for this template variable */
|
|
370
|
+
root = this.theme.closest(this.container, `[data-schemaid="${first}"]`);
|
|
371
|
+
if (!root) throw new Error(`Could not find ancestor node with id ${first}`)
|
|
372
|
+
|
|
373
|
+
/* Keep track of the root node and path for use when rendering the template */
|
|
374
|
+
adjustedPath = `${root.getAttribute('data-schemapath')}.${pathParts.join('.')}`;
|
|
375
|
+
|
|
376
|
+
if (myPath.startsWith(adjustedPath)) this.watchLoop = true;
|
|
377
|
+
this.jsoneditor.watch(adjustedPath, this.watch_listener);
|
|
378
|
+
|
|
379
|
+
this.watched[name] = adjustedPath;
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/* Dynamic header */
|
|
384
|
+
if (this.schema.headerTemplate) {
|
|
385
|
+
this.header_template = this.jsoneditor.compileTemplate(this.schema.headerTemplate, this.template_engine);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
addLinks () {
|
|
390
|
+
/* Add links */
|
|
391
|
+
if (!this.no_link_holder) {
|
|
392
|
+
this.link_holder = this.theme.getLinksHolder();
|
|
393
|
+
/* if description element exists, insert the link before */
|
|
394
|
+
if (typeof this.description !== 'undefined') this.description.parentNode.insertBefore(this.link_holder, this.description);
|
|
395
|
+
/* otherwise just insert link at bottom of container */
|
|
396
|
+
else this.container.appendChild(this.link_holder);
|
|
397
|
+
if (this.schema.links) {
|
|
398
|
+
for (let i = 0; i < this.schema.links.length; i++) {
|
|
399
|
+
this.addLink(this.getLink(this.schema.links[i]));
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
onMove () {}
|
|
406
|
+
|
|
407
|
+
getButton (text, icon, title, args = []) {
|
|
408
|
+
const btnClass = `json-editor-btn-${icon}`;
|
|
409
|
+
if (!this.iconlib) icon = null;
|
|
410
|
+
else icon = this.iconlib.getIcon(icon);
|
|
411
|
+
|
|
412
|
+
text = this.translate(text, args);
|
|
413
|
+
title = this.translate(title, args);
|
|
414
|
+
|
|
415
|
+
if (!icon && title) {
|
|
416
|
+
text = title;
|
|
417
|
+
title = null;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const btn = this.theme.getButton(text, icon, title);
|
|
421
|
+
btn.classList.add(btnClass);
|
|
422
|
+
return btn
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
setButtonText (button, text, icon, title, args = []) {
|
|
426
|
+
if (!this.iconlib) icon = null;
|
|
427
|
+
else icon = this.iconlib.getIcon(icon);
|
|
428
|
+
|
|
429
|
+
text = this.translate(text, args);
|
|
430
|
+
title = this.translate(title, args);
|
|
431
|
+
|
|
432
|
+
if (!icon && title) {
|
|
433
|
+
text = title;
|
|
434
|
+
title = null;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return this.theme.setButtonText(button, text, icon, title)
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
addLink (link) {
|
|
441
|
+
if (this.link_holder) this.link_holder.appendChild(link);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
getLink (data) {
|
|
445
|
+
let holder;
|
|
446
|
+
let link;
|
|
447
|
+
|
|
448
|
+
/* Get mime type of the link */
|
|
449
|
+
const mime = data.mediaType || 'application/javascript';
|
|
450
|
+
const type = mime.split('/')[0];
|
|
451
|
+
|
|
452
|
+
/* Template to generate the link href */
|
|
453
|
+
const href = this.jsoneditor.compileTemplate(data.href, this.template_engine);
|
|
454
|
+
const relTemplate = this.jsoneditor.compileTemplate(data.rel ? data.rel : data.href, this.template_engine);
|
|
455
|
+
|
|
456
|
+
/* Template to generate the link's download attribute */
|
|
457
|
+
let download = null;
|
|
458
|
+
if (data.download) download = data.download;
|
|
459
|
+
|
|
460
|
+
if (download && download !== true) {
|
|
461
|
+
download = this.jsoneditor.compileTemplate(download, this.template_engine);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/* Image links */
|
|
465
|
+
if (type === 'image') {
|
|
466
|
+
holder = this.theme.getBlockLinkHolder();
|
|
467
|
+
link = document.createElement('a');
|
|
468
|
+
link.setAttribute('target', '_blank');
|
|
469
|
+
const image = document.createElement('img');
|
|
470
|
+
|
|
471
|
+
this.theme.createImageLink(holder, link, image);
|
|
472
|
+
|
|
473
|
+
/* When a watched field changes, update the url */
|
|
474
|
+
this.link_watchers.push(vars => {
|
|
475
|
+
const url = href(vars);
|
|
476
|
+
const rel = relTemplate(vars);
|
|
477
|
+
link.setAttribute('href', url);
|
|
478
|
+
link.setAttribute('title', rel || url);
|
|
479
|
+
image.setAttribute('src', url);
|
|
480
|
+
});
|
|
481
|
+
/* Audio/Video links */
|
|
482
|
+
} else if (['audio', 'video'].includes(type)) {
|
|
483
|
+
holder = this.theme.getBlockLinkHolder();
|
|
484
|
+
|
|
485
|
+
link = this.theme.getBlockLink();
|
|
486
|
+
link.setAttribute('target', '_blank');
|
|
487
|
+
|
|
488
|
+
const media = document.createElement(type);
|
|
489
|
+
media.setAttribute('controls', 'controls');
|
|
490
|
+
|
|
491
|
+
this.theme.createMediaLink(holder, link, media);
|
|
492
|
+
|
|
493
|
+
/* When a watched field changes, update the url */
|
|
494
|
+
this.link_watchers.push(vars => {
|
|
495
|
+
const url = href(vars);
|
|
496
|
+
const rel = relTemplate(vars);
|
|
497
|
+
link.setAttribute('href', url);
|
|
498
|
+
link.textContent = rel || url;
|
|
499
|
+
media.setAttribute('src', url);
|
|
500
|
+
});
|
|
501
|
+
/* Text links or blank link */
|
|
502
|
+
} else {
|
|
503
|
+
link = holder = this.theme.getBlockLink();
|
|
504
|
+
holder.setAttribute('target', '_blank');
|
|
505
|
+
holder.textContent = data.rel;
|
|
506
|
+
holder.style.display = 'none'; /* Prevent blank links from showing up when using custom view */
|
|
507
|
+
|
|
508
|
+
/* When a watched field changes, update the url */
|
|
509
|
+
this.link_watchers.push(vars => {
|
|
510
|
+
const url = href(vars);
|
|
511
|
+
const rel = relTemplate(vars);
|
|
512
|
+
if (url) holder.style.display = '';
|
|
513
|
+
holder.setAttribute('href', url);
|
|
514
|
+
holder.textContent = rel || url;
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (download && link) {
|
|
519
|
+
if (download === true) {
|
|
520
|
+
link.setAttribute('download', '');
|
|
521
|
+
} else {
|
|
522
|
+
this.link_watchers.push(vars => {
|
|
523
|
+
link.setAttribute('download', download(vars));
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (data.class) {
|
|
529
|
+
const classNames = data.class.split(' ');
|
|
530
|
+
|
|
531
|
+
classNames.forEach((className) => {
|
|
532
|
+
link.classList.add(className);
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return holder
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
refreshWatchedFieldValues () {
|
|
540
|
+
if (!this.watched_values) return
|
|
541
|
+
const watched = {};
|
|
542
|
+
let changed = false;
|
|
543
|
+
|
|
544
|
+
if (this.watched) {
|
|
545
|
+
Object.keys(this.watched).forEach(name => {
|
|
546
|
+
const editor = this.jsoneditor.getEditor(this.watched[name]);
|
|
547
|
+
const val = editor ? editor.getValue() : null;
|
|
548
|
+
if (this.watched_values[name] !== val) changed = true;
|
|
549
|
+
watched[name] = val;
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
watched.self = this.getValue();
|
|
554
|
+
if (this.watched_values.self !== watched.self) changed = true;
|
|
555
|
+
|
|
556
|
+
this.watched_values = watched;
|
|
557
|
+
|
|
558
|
+
return changed
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
getWatchedFieldValues () {
|
|
562
|
+
return this.watched_values
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
updateHeaderText () {
|
|
566
|
+
if (this.header) {
|
|
567
|
+
const headerText = this.getHeaderText();
|
|
568
|
+
/* If the header has children, only update the text node's value */
|
|
569
|
+
if (this.header.children.length) {
|
|
570
|
+
for (let i = 0; i < this.header.childNodes.length; i++) {
|
|
571
|
+
if (this.header.childNodes[i].nodeType === 3) {
|
|
572
|
+
this.header.childNodes[i].nodeValue = this.cleanText(headerText);
|
|
573
|
+
break
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
/* Otherwise, just update the entire node */
|
|
577
|
+
} else {
|
|
578
|
+
if (window.DOMPurify) this.header.innerHTML = window.DOMPurify.sanitize(headerText);
|
|
579
|
+
else this.header.textContent = this.cleanText(headerText);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
getHeaderText (titleOnly) {
|
|
585
|
+
if (this.header_text) return this.header_text
|
|
586
|
+
else if (titleOnly) return this.translateProperty(this.schema.title)
|
|
587
|
+
else return this.getTitle()
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
getPathDepth () {
|
|
591
|
+
return this.path.split('.').length
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
cleanText (txt) {
|
|
595
|
+
/* Clean out HTML tags from txt */
|
|
596
|
+
const tmp = document.createElement('div');
|
|
597
|
+
tmp.innerHTML = txt;
|
|
598
|
+
return (tmp.textContent || tmp.innerText)
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
onWatchedFieldChange () {
|
|
602
|
+
let vars;
|
|
603
|
+
|
|
604
|
+
if (this.header_template) {
|
|
605
|
+
vars = extend(this.getWatchedFieldValues(), {
|
|
606
|
+
key: this.key,
|
|
607
|
+
i: this.key,
|
|
608
|
+
i0: (this.key * 1),
|
|
609
|
+
i1: (this.key * 1 + 1),
|
|
610
|
+
title: this.getTitle()
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
// object properties
|
|
614
|
+
if (this.editors && Object.keys(this.editors).length) {
|
|
615
|
+
vars.properties = {};
|
|
616
|
+
|
|
617
|
+
Object.keys(this.editors).forEach((key) => {
|
|
618
|
+
const editor = this.editors[key];
|
|
619
|
+
|
|
620
|
+
if (editor.schema && editor.schema.enum && editor.schema.options && editor.schema.options.enum_titles) {
|
|
621
|
+
const enumIndex = editor.schema.enum.indexOf(editor.value);
|
|
622
|
+
const enumTitle = editor.options.enum_titles[enumIndex];
|
|
623
|
+
vars.properties[key] = {
|
|
624
|
+
enumTitle
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const headerText = this.header_template(vars);
|
|
631
|
+
|
|
632
|
+
if (headerText !== this.header_text) {
|
|
633
|
+
this.header_text = headerText;
|
|
634
|
+
this.updateHeaderText();
|
|
635
|
+
this.notify();
|
|
636
|
+
/* this.fireChangeHeaderEvent(); */
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
if (this.link_watchers.length) {
|
|
640
|
+
vars = this.getWatchedFieldValues();
|
|
641
|
+
for (let i = 0; i < this.link_watchers.length; i++) {
|
|
642
|
+
this.link_watchers[i](vars);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
setValue (value) {
|
|
648
|
+
value = this.applyConstFilter(value);
|
|
649
|
+
this.value = value;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
applyConstFilter (value) {
|
|
653
|
+
if (this.enforceConstEnabled && typeof this.schema.const !== 'undefined') {
|
|
654
|
+
value = this.schema.const;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
return value
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
getValue () {
|
|
661
|
+
if (!this.dependenciesFulfilled) {
|
|
662
|
+
return undefined
|
|
663
|
+
}
|
|
664
|
+
return this.value
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
refreshValue () {
|
|
668
|
+
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
getChildEditors () {
|
|
672
|
+
return false
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
destroy () {
|
|
676
|
+
this.unregister(this);
|
|
677
|
+
if (this.watched) {
|
|
678
|
+
Object.values(this.watched).forEach(adjustedPath => this.jsoneditor.unwatch(adjustedPath, this.watch_listener));
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
this.watched = null;
|
|
682
|
+
this.watched_values = null;
|
|
683
|
+
this.watch_listener = null;
|
|
684
|
+
this.header_text = null;
|
|
685
|
+
this.header_template = null;
|
|
686
|
+
this.value = null;
|
|
687
|
+
if (this.container && this.container.parentNode) this.container.parentNode.removeChild(this.container);
|
|
688
|
+
this.container = null;
|
|
689
|
+
this.jsoneditor = null;
|
|
690
|
+
this.schema = null;
|
|
691
|
+
this.path = null;
|
|
692
|
+
this.key = null;
|
|
693
|
+
this.parent = null;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
isDefaultRequired () {
|
|
697
|
+
return this.isRequired() || !!this.jsoneditor.options.use_default_values
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
getDefault () {
|
|
701
|
+
if (this.enforceConstEnabled && this.schema.const) {
|
|
702
|
+
return this.schema.const
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (typeof this.schema.default !== 'undefined') {
|
|
706
|
+
return this.schema.default
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
if (typeof this.schema.enum !== 'undefined') {
|
|
710
|
+
return this.schema.enum[0]
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
let type = this.schema.type || this.schema.oneOf;
|
|
714
|
+
if (type && Array.isArray(type)) type = type[0];
|
|
715
|
+
if (type && typeof type === 'object') type = type.type;
|
|
716
|
+
if (type && Array.isArray(type)) type = type[0];
|
|
717
|
+
|
|
718
|
+
if (typeof type === 'string') {
|
|
719
|
+
if (type === 'number') return this.isDefaultRequired() ? 0.0 : undefined
|
|
720
|
+
if (type === 'boolean') return this.isDefaultRequired() ? false : undefined
|
|
721
|
+
if (type === 'integer') return this.isDefaultRequired() ? 0 : undefined
|
|
722
|
+
if (type === 'string') return this.isDefaultRequired() ? '' : undefined
|
|
723
|
+
if (type === 'null') return null
|
|
724
|
+
if (type === 'object') return {}
|
|
725
|
+
if (type === 'array') return []
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
return undefined
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
getTitle () {
|
|
732
|
+
return this.translateProperty(this.schema.title || this.key || this.formname)
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
enable () {
|
|
736
|
+
this.disabled = false;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
disable () {
|
|
740
|
+
this.disabled = true;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
isEnabled () {
|
|
744
|
+
return !this.disabled
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
isRequired () {
|
|
748
|
+
if (typeof this.schema.required === 'boolean') return this.schema.required
|
|
749
|
+
else if (this.parent && this.parent.schema && Array.isArray(this.parent.schema.required)) return this.parent.schema.required.includes(this.key)
|
|
750
|
+
else if (this.jsoneditor.options.required_by_default) return true
|
|
751
|
+
else return false
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
getDisplayText (arr) {
|
|
755
|
+
const disp = [];
|
|
756
|
+
const used = {};
|
|
757
|
+
|
|
758
|
+
/* Determine how many times each attribute name is used. */
|
|
759
|
+
/* This helps us pick the most distinct display text for the schemas. */
|
|
760
|
+
arr.forEach(el => {
|
|
761
|
+
if (el.title) {
|
|
762
|
+
used[el.title] = used[el.title] || 0;
|
|
763
|
+
used[el.title]++;
|
|
764
|
+
}
|
|
765
|
+
if (el.description) {
|
|
766
|
+
used[el.description] = used[el.description] || 0;
|
|
767
|
+
used[el.description]++;
|
|
768
|
+
}
|
|
769
|
+
if (el.format) {
|
|
770
|
+
used[el.format] = used[el.format] || 0;
|
|
771
|
+
used[el.format]++;
|
|
772
|
+
}
|
|
773
|
+
if (el.type) {
|
|
774
|
+
used[el.type] = used[el.type] || 0;
|
|
775
|
+
used[el.type]++;
|
|
776
|
+
}
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
/* Determine display text for each element of the array */
|
|
780
|
+
arr.forEach(el => {
|
|
781
|
+
let name;
|
|
782
|
+
|
|
783
|
+
/* If it's a simple string */
|
|
784
|
+
if (typeof el === 'string') name = el;
|
|
785
|
+
/* Object */
|
|
786
|
+
else if (el.title && used[el.title] <= 1) name = el.title;
|
|
787
|
+
else if (el.format && used[el.format] <= 1) name = el.format;
|
|
788
|
+
else if (el.type && used[el.type] <= 1) name = el.type;
|
|
789
|
+
else if (el.description && used[el.description] <= 1) name = el.description;
|
|
790
|
+
else if (el.title) name = el.title;
|
|
791
|
+
else if (el.format) name = el.format;
|
|
792
|
+
else if (el.type) name = el.type;
|
|
793
|
+
else if (el.description) name = el.description;
|
|
794
|
+
else if (JSON.stringify(el).length < 500) name = JSON.stringify(el);
|
|
795
|
+
else name = 'type';
|
|
796
|
+
|
|
797
|
+
disp.push(name);
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
/* Replace identical display text with "text 1", "text 2", etc. */
|
|
801
|
+
const inc = {};
|
|
802
|
+
disp.forEach((name, i) => {
|
|
803
|
+
inc[name] = inc[name] || 0;
|
|
804
|
+
inc[name]++;
|
|
805
|
+
|
|
806
|
+
if (used[name] > 1) disp[i] = `${name} ${inc[name]}`;
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
return disp
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/* Replace space(s) with "-" to create valid id value */
|
|
813
|
+
getValidId (id) {
|
|
814
|
+
id = id === undefined ? '' : id.toString();
|
|
815
|
+
return id.replace(/\s+/g, '-')
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
setInputAttributes (inputAttribute, input) {
|
|
819
|
+
if (this.schema.options && this.schema.options.inputAttributes) {
|
|
820
|
+
const inputAttributes = this.schema.options.inputAttributes;
|
|
821
|
+
const protectedAttributes = ['name', 'type'].concat(inputAttribute);
|
|
822
|
+
const workingInput = input || this.input;
|
|
823
|
+
Object.keys(inputAttributes).forEach(key => {
|
|
824
|
+
if (!protectedAttributes.includes(key.toLowerCase())) {
|
|
825
|
+
workingInput.setAttribute(key, inputAttributes[key]);
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
setContainerAttributes () {
|
|
832
|
+
if (this.schema.options && this.schema.options.containerAttributes) {
|
|
833
|
+
const containerAttributes = this.schema.options.containerAttributes;
|
|
834
|
+
const protectedAttributes = ['data-schemapath', 'data-schematype', 'data-schemaid'];
|
|
835
|
+
Object.keys(containerAttributes).forEach(key => {
|
|
836
|
+
if (!protectedAttributes.includes(key.toLowerCase())) {
|
|
837
|
+
this.container.setAttribute(key, containerAttributes[key]);
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
expandCallbacks (scope, options) {
|
|
844
|
+
const callback = this.defaults.callbacks[scope];
|
|
845
|
+
Object.entries(options).forEach(([key, value]) => {
|
|
846
|
+
if (value === Object(value)) {
|
|
847
|
+
options[key] = this.expandCallbacks(scope, value);
|
|
848
|
+
} else if (typeof value === 'string' &&
|
|
849
|
+
typeof callback === 'object' &&
|
|
850
|
+
typeof callback[value] === 'function') {
|
|
851
|
+
options[key] = callback[value].bind(null, this);
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
return options
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
showValidationErrors (errors) {
|
|
858
|
+
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* Generate band colors for the editor
|
|
864
|
+
* @param {Record<string,any>} schema - JSON schema
|
|
865
|
+
* @param {string} format - Format type ("bands" or "bands-arithmetic")
|
|
866
|
+
* @returns {string[]} Array of color strings
|
|
867
|
+
*/
|
|
868
|
+
function generateBandColors(schema, format) {
|
|
869
|
+
const bands = format === "bands" ? schema.items?.enum : schema.enum || [];
|
|
870
|
+
const colors =
|
|
871
|
+
format === "bands"
|
|
872
|
+
? schema.items?.options?.colors
|
|
873
|
+
: schema.options?.colors || [];
|
|
874
|
+
if (colors && colors.length === bands.length) {
|
|
875
|
+
return colors;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
return bands.map(
|
|
879
|
+
() =>
|
|
880
|
+
"#" +
|
|
881
|
+
Math.floor(Math.random() * 16777215)
|
|
882
|
+
.toString(16)
|
|
883
|
+
.padStart(6, "0"),
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/**
|
|
888
|
+
* Get color for a specific band
|
|
889
|
+
* @param {string} band - Band identifier
|
|
890
|
+
* @param {string[]} bands - Array of band identifiers
|
|
891
|
+
* @param {string[]} colors - Array of color strings
|
|
892
|
+
* @returns {string} Color string
|
|
893
|
+
*/
|
|
894
|
+
function getBandColor(band, bands, colors) {
|
|
895
|
+
const index = bands.indexOf(band);
|
|
896
|
+
return index !== -1 ? colors[index] : "#000000";
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
/**
|
|
900
|
+
* Create a draggable band element.
|
|
901
|
+
* @param {string} enumValue
|
|
902
|
+
* @param {string} title
|
|
903
|
+
*/
|
|
904
|
+
function createBandDiv(enumValue, title) {
|
|
905
|
+
const div = document.createElement("div");
|
|
906
|
+
div.dataset.band = enumValue;
|
|
907
|
+
div.textContent = title;
|
|
908
|
+
div.draggable = true;
|
|
909
|
+
div.ondragstart = (e) => {
|
|
910
|
+
e.dataTransfer?.setData("band", enumValue);
|
|
911
|
+
};
|
|
912
|
+
return div;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Add draggable band elements
|
|
917
|
+
* @param {import("./index.js").BandsEditor} editor - The editor instance
|
|
918
|
+
* @param {Array<string>} bands - Array of band identifiers
|
|
919
|
+
* @param {Array<string>} bandTitles - Array of band titles
|
|
920
|
+
*/
|
|
921
|
+
function addDraggableBands(editor, bands, bandTitles) {
|
|
922
|
+
bands.forEach((band, index) => {
|
|
923
|
+
const title = bandTitles[index];
|
|
924
|
+
const bandDiv = createBandDiv(band, title);
|
|
925
|
+
|
|
926
|
+
// createBandDiv already sets up drag functionality
|
|
927
|
+
editor.control?.appendChild(bandDiv);
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
/**
|
|
932
|
+
* Create unified styles for all slot types
|
|
933
|
+
* @param {string[]} bands - Array of band identifiers
|
|
934
|
+
* @param {string[]} colors - Array of color strings
|
|
935
|
+
* @returns {HTMLStyleElement} Style element with unified slot styles
|
|
936
|
+
*/
|
|
937
|
+
function createSlotStyles(bands, colors) {
|
|
938
|
+
const style = document.createElement("style");
|
|
939
|
+
style.innerHTML = `
|
|
940
|
+
/* Base styles for all band elements */
|
|
941
|
+
[data-band] {
|
|
942
|
+
display: inline-flex;
|
|
943
|
+
border: 1px solid darkgrey;
|
|
944
|
+
border-radius: 50%;
|
|
945
|
+
height: 40px;
|
|
946
|
+
aspect-ratio: 1/1;
|
|
947
|
+
padding: 4px;
|
|
948
|
+
margin: 2px;
|
|
949
|
+
align-items: center;
|
|
950
|
+
justify-content: center;
|
|
951
|
+
cursor: move;
|
|
952
|
+
font-size: 10px;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/* Band color styles */
|
|
956
|
+
${bands
|
|
957
|
+
.map(
|
|
958
|
+
(band) =>
|
|
959
|
+
`[data-band="${band}"] { background: ${getBandColor(
|
|
960
|
+
band,
|
|
961
|
+
bands,
|
|
962
|
+
colors,
|
|
963
|
+
)}; color: black; }`,
|
|
964
|
+
)
|
|
965
|
+
.join("\n")}
|
|
966
|
+
|
|
967
|
+
/* RGB slot styles */
|
|
968
|
+
[data-slot] {
|
|
969
|
+
display: inline-flex;
|
|
970
|
+
width: 50px;
|
|
971
|
+
height: 50px;
|
|
972
|
+
aspect-ratio: 1/1;
|
|
973
|
+
padding: 1px;
|
|
974
|
+
border: 2px solid #666;
|
|
975
|
+
background: #f0f0f0;
|
|
976
|
+
border-radius: 50%;
|
|
977
|
+
align-items: center;
|
|
978
|
+
justify-content: center;
|
|
979
|
+
cursor: pointer;
|
|
980
|
+
margin: 2px;
|
|
981
|
+
position: relative;
|
|
982
|
+
box-sizing: border-box;
|
|
983
|
+
}
|
|
984
|
+
[data-slot]:hover {
|
|
985
|
+
border-color: #333;
|
|
986
|
+
background: #f9f9f9;
|
|
987
|
+
}
|
|
988
|
+
[data-slot]::before {
|
|
989
|
+
content: attr(data-slot);
|
|
990
|
+
position: absolute;
|
|
991
|
+
font-size: 12px;
|
|
992
|
+
font-weight: bold;
|
|
993
|
+
color: #666;
|
|
994
|
+
z-index: 0;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
/* container */
|
|
998
|
+
.slots-container {
|
|
999
|
+
font-family: monospace;
|
|
1000
|
+
font-size: 18px;
|
|
1001
|
+
padding: 16px;
|
|
1002
|
+
background: #f0f0f0;
|
|
1003
|
+
border: 1px solid #ccc;
|
|
1004
|
+
border-radius: 4px;
|
|
1005
|
+
margin: 8px 0;
|
|
1006
|
+
display: flex;
|
|
1007
|
+
align-items: center;
|
|
1008
|
+
justify-content: center;
|
|
1009
|
+
flex-wrap: wrap;
|
|
1010
|
+
gap: 4px;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
.formula-text {
|
|
1014
|
+
font-size: 18px;
|
|
1015
|
+
margin: 0 2px;
|
|
1016
|
+
}
|
|
1017
|
+
`;
|
|
1018
|
+
return style;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
/**
|
|
1022
|
+
* Create a unified slot element for RGB or arithmetic use
|
|
1023
|
+
* @param {string} identifier - RGB letter ("R", "G", "B") or variable name for arithmetic
|
|
1024
|
+
* @param {(e: DragEvent) => void} onDrop - Drop handler function
|
|
1025
|
+
* @returns {HTMLDivElement} Slot element
|
|
1026
|
+
*/
|
|
1027
|
+
function createSlot(identifier, onDrop) {
|
|
1028
|
+
const slotDiv = document.createElement("div");
|
|
1029
|
+
// Use data-slot
|
|
1030
|
+
slotDiv.dataset.slot = identifier;
|
|
1031
|
+
|
|
1032
|
+
// Add drag & drop functionality
|
|
1033
|
+
slotDiv.ondrop = onDrop;
|
|
1034
|
+
slotDiv.ondragover = (e) => e.preventDefault();
|
|
1035
|
+
|
|
1036
|
+
return slotDiv;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* Fill a slot with a band
|
|
1041
|
+
* @param {HTMLElement} slot - Slot element
|
|
1042
|
+
* @param {string} enumValue - Band value
|
|
1043
|
+
* @param {string} title - Band title
|
|
1044
|
+
*/
|
|
1045
|
+
function fillSlotWithBand(slot, enumValue, title) {
|
|
1046
|
+
// clear existing band and add new one
|
|
1047
|
+
const existingBand = slot.querySelector("[data-band]");
|
|
1048
|
+
if (existingBand) {
|
|
1049
|
+
existingBand.remove();
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
const bandDiv = createBandDiv(enumValue, title);
|
|
1053
|
+
slot.appendChild(bandDiv);
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
/**
|
|
1057
|
+
* Build the traditional RGB bands interface
|
|
1058
|
+
* @param {import("./index.js").BandsEditor} editor - The editor instance
|
|
1059
|
+
* @param {Array<string>} colors - Array of color strings
|
|
1060
|
+
* @param {Array<string>} bands - Array of band identifiers
|
|
1061
|
+
* @param {Array<string>} bandTitles - Array of band titles
|
|
1062
|
+
*/
|
|
1063
|
+
function buildRGBInterface(editor, colors, bands, bandTitles) {
|
|
1064
|
+
// Use unified styles instead of creating separate styles
|
|
1065
|
+
const style = createSlotStyles(bands, colors);
|
|
1066
|
+
editor.control?.appendChild(style);
|
|
1067
|
+
|
|
1068
|
+
// Add draggable bands
|
|
1069
|
+
addDraggableBands(editor, bands, bandTitles);
|
|
1070
|
+
|
|
1071
|
+
editor.control?.appendChild(document.createElement("hr"));
|
|
1072
|
+
|
|
1073
|
+
// Add RGB slots using unified slot system
|
|
1074
|
+
addRGBSlots(editor, bands, bandTitles);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
/**
|
|
1078
|
+
* Add RGB slots for traditional bands interface using unified slot system
|
|
1079
|
+
* @param {import("./index.js").BandsEditor} editor - The editor instance
|
|
1080
|
+
* @param {Array<string>} bands - Array of band identifiers
|
|
1081
|
+
* @param {Array<string>} bandTitles - Array of band titles
|
|
1082
|
+
*/
|
|
1083
|
+
function addRGBSlots(editor, bands, bandTitles) {
|
|
1084
|
+
// Create a container for RGB slots
|
|
1085
|
+
const rgbContainer = document.createElement("div");
|
|
1086
|
+
rgbContainer.classList.add("slots-container");
|
|
1087
|
+
["R", "G", "B"].forEach((slot, index) => {
|
|
1088
|
+
const onDrop = (/** @type {DragEvent} */ e) => {
|
|
1089
|
+
e.preventDefault();
|
|
1090
|
+
const enumValue = e.dataTransfer?.getData("band");
|
|
1091
|
+
if (!enumValue) return;
|
|
1092
|
+
|
|
1093
|
+
const enumIndex = bands.indexOf(enumValue);
|
|
1094
|
+
const title = bandTitles[enumIndex] || enumValue;
|
|
1095
|
+
|
|
1096
|
+
fillSlotWithBand(slotDiv, enumValue, title);
|
|
1097
|
+
// Get current value as array or create new one
|
|
1098
|
+
const currentValue = editor.getValue() || [];
|
|
1099
|
+
currentValue[index] = enumValue;
|
|
1100
|
+
editor.setValue(currentValue);
|
|
1101
|
+
editor.onChange(true);
|
|
1102
|
+
};
|
|
1103
|
+
|
|
1104
|
+
const slotDiv = createSlot(slot, onDrop);
|
|
1105
|
+
addRGBSlotStyle(slotDiv);
|
|
1106
|
+
rgbContainer.appendChild(slotDiv);
|
|
1107
|
+
|
|
1108
|
+
// Initialize with existing value
|
|
1109
|
+
setTimeout(() => {
|
|
1110
|
+
const currentValue = editor.getValue();
|
|
1111
|
+
if (currentValue?.[index]) {
|
|
1112
|
+
const enumValue = currentValue[index];
|
|
1113
|
+
const enumIndex = bands.indexOf(enumValue);
|
|
1114
|
+
const title = bandTitles[enumIndex] || enumValue;
|
|
1115
|
+
if (enumValue) {
|
|
1116
|
+
fillSlotWithBand(slotDiv, enumValue, title);
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
});
|
|
1120
|
+
});
|
|
1121
|
+
|
|
1122
|
+
editor.control?.appendChild(rgbContainer);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
/**
|
|
1126
|
+
* Create specific styles for RGB slots
|
|
1127
|
+
* @param {HTMLElement} rgbSlot - Array of RGB slot elements
|
|
1128
|
+
*/
|
|
1129
|
+
function addRGBSlotStyle(rgbSlot) {
|
|
1130
|
+
rgbSlot.style.border = "2px dashed";
|
|
1131
|
+
switch (rgbSlot.dataset.slot) {
|
|
1132
|
+
case "R": {
|
|
1133
|
+
rgbSlot.style.borderColor = "#F88";
|
|
1134
|
+
rgbSlot.style.background = "#FEE";
|
|
1135
|
+
break;
|
|
1136
|
+
}
|
|
1137
|
+
case "G": {
|
|
1138
|
+
rgbSlot.style.borderColor = "#8F8";
|
|
1139
|
+
rgbSlot.style.background = "#EFE";
|
|
1140
|
+
break;
|
|
1141
|
+
}
|
|
1142
|
+
case "B": {
|
|
1143
|
+
rgbSlot.style.borderColor = "#88F";
|
|
1144
|
+
rgbSlot.style.background = "#EEF";
|
|
1145
|
+
break;
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
const MUSTACHE_REGEX = /\{\{([^}]+)\}\}/g;
|
|
1151
|
+
/**
|
|
1152
|
+
* Build the arithmetic expression interface
|
|
1153
|
+
* @param {import("./index.js").BandsEditor} editor - The editor instance
|
|
1154
|
+
* @param {Array<string>} colors - Array of color strings
|
|
1155
|
+
* @param {Array<string>} bands - Array of band identifiers
|
|
1156
|
+
* @param {Array<string>} bandTitles - Array of band titles
|
|
1157
|
+
*/
|
|
1158
|
+
function buildArithmeticInterface(editor, colors, bands, bandTitles) {
|
|
1159
|
+
const formulaTemplate = editor.schema.formulaTemplate || "{{A}}";
|
|
1160
|
+
|
|
1161
|
+
const style = createSlotStyles(bands, colors);
|
|
1162
|
+
editor.control?.appendChild(style);
|
|
1163
|
+
|
|
1164
|
+
addDraggableBands(editor, bands, bandTitles);
|
|
1165
|
+
|
|
1166
|
+
editor.control?.appendChild(document.createElement("hr"));
|
|
1167
|
+
|
|
1168
|
+
// Add formula display with embedded slots after the bands
|
|
1169
|
+
addFormulaSlots(editor, formulaTemplate, bands, bandTitles);
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
/**
|
|
1173
|
+
* Generate the final formula string with band values substituted
|
|
1174
|
+
* @param {import("./index.js").BandsEditor} editor - The editor instance
|
|
1175
|
+
* @returns {string} Formula string with substituted values
|
|
1176
|
+
*/
|
|
1177
|
+
function generateFormulaString(editor) {
|
|
1178
|
+
/** @type {string} */
|
|
1179
|
+
const formulaTemplate = editor.schema.formulaTemplate || "{{A}}";
|
|
1180
|
+
|
|
1181
|
+
const variableValues = editor.variableValues || {};
|
|
1182
|
+
|
|
1183
|
+
return formulaTemplate.replace(MUSTACHE_REGEX, (match, variable) => {
|
|
1184
|
+
const bandValue = variableValues[variable.trim()];
|
|
1185
|
+
return bandValue || match; // Keep placeholder if no value assigned
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
/**
|
|
1190
|
+
* Add formula display with embedded circular slots
|
|
1191
|
+
* @param {import("./index.js").BandsEditor} editor - The editor instance
|
|
1192
|
+
* @param {string} template - Formula template string
|
|
1193
|
+
* @param {Array<string>} bands - Array of band identifiers
|
|
1194
|
+
* @param {Array<string>} bandTitles - Array of band titles
|
|
1195
|
+
*/
|
|
1196
|
+
function addFormulaSlots(editor, template, bands, bandTitles) {
|
|
1197
|
+
const formulaContainer = document.createElement("div");
|
|
1198
|
+
formulaContainer.classList.add("slots-container");
|
|
1199
|
+
|
|
1200
|
+
// Initialize slots tracking
|
|
1201
|
+
editor.variableSlots = {};
|
|
1202
|
+
|
|
1203
|
+
// Split the template into parts and create elements
|
|
1204
|
+
const parts = template.split(/(\{\{[^}]+\}\})/);
|
|
1205
|
+
|
|
1206
|
+
parts.forEach((part) => {
|
|
1207
|
+
if (!part) {
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
if (!part.match(MUSTACHE_REGEX)) {
|
|
1212
|
+
// This is formula text
|
|
1213
|
+
part = part.trim();
|
|
1214
|
+
if (part) {
|
|
1215
|
+
const textElement = document.createElement("span");
|
|
1216
|
+
textElement.classList.add("formula-text");
|
|
1217
|
+
textElement.textContent = part;
|
|
1218
|
+
formulaContainer.appendChild(textElement);
|
|
1219
|
+
}
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
// This is a variable placeholder
|
|
1224
|
+
const variable = part.replace(/[{}]/g, "").trim();
|
|
1225
|
+
const slotElement = createSlot(variable, (e) => {
|
|
1226
|
+
e.preventDefault();
|
|
1227
|
+
const enumValue = e.dataTransfer?.getData("band");
|
|
1228
|
+
if (!enumValue) return;
|
|
1229
|
+
|
|
1230
|
+
const enumIndex = bands.indexOf(enumValue);
|
|
1231
|
+
const title = bandTitles[enumIndex] || enumValue;
|
|
1232
|
+
|
|
1233
|
+
editor.variableValues[variable] = enumValue;
|
|
1234
|
+
|
|
1235
|
+
// Update ALL slots for this variable using unified system
|
|
1236
|
+
updateAllSlotsForVariable(editor, variable, enumValue, title);
|
|
1237
|
+
|
|
1238
|
+
// final formula string as the value
|
|
1239
|
+
//@ts-expect-error todo
|
|
1240
|
+
editor.value = generateFormulaString(editor);
|
|
1241
|
+
editor.onChange(true);
|
|
1242
|
+
});
|
|
1243
|
+
formulaContainer.appendChild(slotElement);
|
|
1244
|
+
|
|
1245
|
+
// Track all slots for this variable
|
|
1246
|
+
if (!editor.variableSlots[variable]) {
|
|
1247
|
+
editor.variableSlots[variable] = [];
|
|
1248
|
+
}
|
|
1249
|
+
editor.variableSlots[variable].push(slotElement);
|
|
1250
|
+
});
|
|
1251
|
+
|
|
1252
|
+
editor.control?.appendChild(formulaContainer);
|
|
1253
|
+
|
|
1254
|
+
// Initialize slots with existing values after all slots are created
|
|
1255
|
+
setTimeout(() => {
|
|
1256
|
+
initializeSlots(editor);
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
/**
|
|
1261
|
+
* Initialize all slots with existing values
|
|
1262
|
+
* @param {import("./index.js").BandsEditor} editor - The editor instance
|
|
1263
|
+
*/
|
|
1264
|
+
function initializeSlots(editor) {
|
|
1265
|
+
if (editor.variableValues && editor.variableSlots) {
|
|
1266
|
+
Object.keys(editor.variableValues).forEach((variable) => {
|
|
1267
|
+
const enumValue = editor.variableValues[variable];
|
|
1268
|
+
const bands = editor.bands || editor.schema.enum || [];
|
|
1269
|
+
const bandTitles =
|
|
1270
|
+
editor.bandTitles || editor.schema.options?.enum_titles || bands;
|
|
1271
|
+
const enumIndex = bands.indexOf(enumValue);
|
|
1272
|
+
const title = bandTitles[enumIndex] || enumValue;
|
|
1273
|
+
updateAllSlotsForVariable(editor, variable, enumValue, title);
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
/**
|
|
1279
|
+
* Update all slots for a specific variable with band circle
|
|
1280
|
+
* @param {import("./index.js").BandsEditor} editor - The editor instance
|
|
1281
|
+
* @param {string} variable - Variable name
|
|
1282
|
+
* @param {string} enumValue - Band value
|
|
1283
|
+
* @param {string} title - Band title
|
|
1284
|
+
*/
|
|
1285
|
+
function updateAllSlotsForVariable(editor, variable, enumValue, title) {
|
|
1286
|
+
if (editor.variableSlots && editor.variableSlots[variable]) {
|
|
1287
|
+
editor.variableSlots[variable].forEach((slot) => {
|
|
1288
|
+
fillSlotWithBand(slot, enumValue, title);
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
/**
|
|
1294
|
+
* Custom band combination editor interface for eox-jsonform
|
|
1295
|
+
*/
|
|
1296
|
+
|
|
1297
|
+
class BandsEditor extends AbstractEditor {
|
|
1298
|
+
/** @type {Record<string, HTMLElement[]>} */
|
|
1299
|
+
variableSlots = {};
|
|
1300
|
+
/** @type {Record<string, string>} */
|
|
1301
|
+
variableValues = {};
|
|
1302
|
+
/** @type {string[]} */
|
|
1303
|
+
bands = [];
|
|
1304
|
+
/** @type {string[]} */
|
|
1305
|
+
bandTitles = [];
|
|
1306
|
+
/** @type {string[]} */
|
|
1307
|
+
colors = [];
|
|
1308
|
+
|
|
1309
|
+
build() {
|
|
1310
|
+
super.build();
|
|
1311
|
+
// Determine the format type
|
|
1312
|
+
const format = this.schema.format || "bands";
|
|
1313
|
+
|
|
1314
|
+
this.bands =
|
|
1315
|
+
format === "bands" ? this.schema.items?.enum : this.schema.enum || [];
|
|
1316
|
+
this.bandTitles =
|
|
1317
|
+
format === "bands"
|
|
1318
|
+
? this.schema.items?.options?.enum_titles
|
|
1319
|
+
: this.schema.options.enum_titles || this.bands;
|
|
1320
|
+
this.colors = generateBandColors(this.schema, format);
|
|
1321
|
+
|
|
1322
|
+
// control
|
|
1323
|
+
this.control = document.createElement("div");
|
|
1324
|
+
this.control.classList.add("form-control");
|
|
1325
|
+
|
|
1326
|
+
if (format === "bands") {
|
|
1327
|
+
buildRGBInterface(this, this.colors, this.bands, this.bandTitles);
|
|
1328
|
+
} else if (format === "bands-arithmetic") {
|
|
1329
|
+
buildArithmeticInterface(this, this.colors, this.bands, this.bandTitles);
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
// label
|
|
1333
|
+
this.label = document.createElement("span");
|
|
1334
|
+
this.label.classList.add("je-header");
|
|
1335
|
+
this.label.textContent = this.schema.title ?? "";
|
|
1336
|
+
|
|
1337
|
+
// appends
|
|
1338
|
+
this.container?.appendChild(this.label);
|
|
1339
|
+
this.container?.appendChild(this.control);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
const bandsEditorInterface = [
|
|
1344
|
+
{
|
|
1345
|
+
type: "array",
|
|
1346
|
+
format: "bands",
|
|
1347
|
+
func: BandsEditor,
|
|
1348
|
+
},
|
|
1349
|
+
{
|
|
1350
|
+
type: "string",
|
|
1351
|
+
format: "bands-arithmetic",
|
|
1352
|
+
func: BandsEditor,
|
|
1353
|
+
},
|
|
1354
|
+
];
|
|
1355
|
+
|
|
1356
|
+
const _style_0 = "eox-layercontrol[data-v-219e33f9]{overflow:auto}";
|
|
1357
|
+
|
|
1358
|
+
const _hoisted_1 = { class: "d-flex flex-column" };
|
|
1359
|
+
const _hoisted_2 = ["for", ".customEditorInterfaces"];
|
|
1360
|
+
const _hoisted_3 = {
|
|
1361
|
+
key: 0,
|
|
1362
|
+
class: "mt-2 mb-2"
|
|
1363
|
+
};
|
|
1364
|
+
|
|
1365
|
+
|
|
1366
|
+
const _sfc_main = {
|
|
1367
|
+
__name: 'EodashLayerControl',
|
|
1368
|
+
props: {
|
|
1369
|
+
map: {
|
|
1370
|
+
/** @type {import("vue").PropType<"first" | "second">} */
|
|
1371
|
+
//@ts-expect-error todo
|
|
1372
|
+
type: String,
|
|
1373
|
+
default: "first",
|
|
1374
|
+
},
|
|
1375
|
+
tools: {
|
|
1376
|
+
type: Array,
|
|
1377
|
+
default: () => ["datetime", "info", "config", "legend", "opacity"],
|
|
1378
|
+
},
|
|
1379
|
+
title: {
|
|
1380
|
+
type: String || Boolean,
|
|
1381
|
+
default: "Layers",
|
|
1382
|
+
},
|
|
1383
|
+
cssVars: {
|
|
1384
|
+
type: Object,
|
|
1385
|
+
},
|
|
1386
|
+
},
|
|
1387
|
+
async setup(__props) {
|
|
1388
|
+
|
|
1389
|
+
let __temp, __restore;
|
|
1390
|
+
|
|
1391
|
+
if (!customElements.get("eox-layercontrol")) {
|
|
1392
|
+
(
|
|
1393
|
+
([__temp,__restore] = withAsyncContext(() => import('@eox/layercontrol'))),
|
|
1394
|
+
await __temp,
|
|
1395
|
+
__restore()
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
if (!customElements.get("eox-jsonform")) {
|
|
1399
|
+
(
|
|
1400
|
+
([__temp,__restore] = withAsyncContext(() => import('@eox/jsonform'))),
|
|
1401
|
+
await __temp,
|
|
1402
|
+
__restore()
|
|
1403
|
+
);
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
const props = __props;
|
|
1407
|
+
|
|
1408
|
+
const config = {
|
|
1409
|
+
tools: props.tools,
|
|
1410
|
+
style: props.cssVars,
|
|
1411
|
+
};
|
|
1412
|
+
|
|
1413
|
+
const { selectedCompareStac, selectedStac } = storeToRefs(useSTAcStore());
|
|
1414
|
+
const showControls = computed(() => {
|
|
1415
|
+
if (props.map === "second") {
|
|
1416
|
+
return mapCompareEl.value !== null && selectedCompareStac.value !== null;
|
|
1417
|
+
}
|
|
1418
|
+
return mapEl.value !== null && selectedStac.value !== null;
|
|
1419
|
+
});
|
|
1420
|
+
|
|
1421
|
+
const eodashCols =
|
|
1422
|
+
props.map === "second" ? eodashCompareCollections : eodashCollections;
|
|
1423
|
+
const mapElement = props.map === "second" ? mapCompareEl : mapEl;
|
|
1424
|
+
|
|
1425
|
+
/** @type { import("vue").Ref<HTMLElement & Record<string,any> | null>} */
|
|
1426
|
+
const eoxLayercontrol = ref(null);
|
|
1427
|
+
|
|
1428
|
+
/** @param {CustomEvent<{layer:import('ol/layer').Layer; datetime:string;}>} evt */
|
|
1429
|
+
const handleDatetimeUpdate = async (evt) => {
|
|
1430
|
+
const { layer, datetime } = evt.detail;
|
|
1431
|
+
|
|
1432
|
+
const ec = await getColFromLayer(eodashCols, layer);
|
|
1433
|
+
|
|
1434
|
+
/** @type {Record<string,any>[] | undefined} */
|
|
1435
|
+
let updatedLayers = [];
|
|
1436
|
+
|
|
1437
|
+
if (ec) {
|
|
1438
|
+
updatedLayers = await ec.updateLayerJson(
|
|
1439
|
+
datetime,
|
|
1440
|
+
layer.get("id"),
|
|
1441
|
+
props.map,
|
|
1442
|
+
);
|
|
1443
|
+
}
|
|
1444
|
+
/** @type {Record<String,any>[] | undefined} */
|
|
1445
|
+
const dataLayers = updatedLayers?.find(
|
|
1446
|
+
(l) => l?.properties?.id === "AnalysisGroup",
|
|
1447
|
+
)?.layers;
|
|
1448
|
+
|
|
1449
|
+
if (dataLayers?.length) {
|
|
1450
|
+
// Add expand to all analysis layers
|
|
1451
|
+
dataLayers?.forEach((dl) => {
|
|
1452
|
+
dl.properties.layerControlExpand = true;
|
|
1453
|
+
dl.properties.layerControlToolsExpand = true;
|
|
1454
|
+
});
|
|
1455
|
+
// assign layers to the map
|
|
1456
|
+
/** @type {HTMLElement & Record<string,any>} */
|
|
1457
|
+
(mapElement.value).layers = updatedLayers;
|
|
1458
|
+
}
|
|
1459
|
+
};
|
|
1460
|
+
|
|
1461
|
+
// ----- debounce logic
|
|
1462
|
+
/** @type {NodeJS.Timeout | undefined} */
|
|
1463
|
+
let timeout;
|
|
1464
|
+
|
|
1465
|
+
/**
|
|
1466
|
+
* @param {CustomEvent<{layer:import('ol/layer').Layer; datetime:string;}>} evt
|
|
1467
|
+
**/
|
|
1468
|
+
const debouncedHandleDateTime = (evt) => {
|
|
1469
|
+
clearTimeout(timeout);
|
|
1470
|
+
timeout = setTimeout(() => {
|
|
1471
|
+
handleDatetimeUpdate(evt);
|
|
1472
|
+
}, 500);
|
|
1473
|
+
};
|
|
1474
|
+
// ------
|
|
1475
|
+
/**
|
|
1476
|
+
*
|
|
1477
|
+
* @param {Event & {detail:{layer:import("ol/layer").Layer;jsonformValue:Record<string,any>}}} evt
|
|
1478
|
+
*/
|
|
1479
|
+
const onLayerConfigChange = (evt) => {
|
|
1480
|
+
if (props.map === "second") {
|
|
1481
|
+
layerControlFormValueCompare.value = evt.detail.jsonformValue;
|
|
1482
|
+
} else {
|
|
1483
|
+
layerControlFormValue.value = evt.detail.jsonformValue;
|
|
1484
|
+
}
|
|
1485
|
+
};
|
|
1486
|
+
|
|
1487
|
+
return (_ctx, _cache) => {
|
|
1488
|
+
return (openBlock(), createElementBlock("span", _hoisted_1, [
|
|
1489
|
+
(showControls.value)
|
|
1490
|
+
? (openBlock(), createElementBlock("eox-layercontrol", mergeProps({ key: unref(mapElement) }, config, {
|
|
1491
|
+
for: unref(mapElement),
|
|
1492
|
+
".customEditorInterfaces": unref(bandsEditorInterface),
|
|
1493
|
+
"onDatetime:updated": debouncedHandleDateTime,
|
|
1494
|
+
toolsAsList: "true",
|
|
1495
|
+
ref_key: "eoxLayercontrol",
|
|
1496
|
+
ref: eoxLayercontrol,
|
|
1497
|
+
"on:layerConfig:change": onLayerConfigChange
|
|
1498
|
+
}), [
|
|
1499
|
+
renderSlot(_ctx.$slots, "layerstitle", {}, () => [
|
|
1500
|
+
createElementVNode("div", null, [
|
|
1501
|
+
(__props.title)
|
|
1502
|
+
? (openBlock(), createElementBlock("p", _hoisted_3, [
|
|
1503
|
+
createElementVNode("strong", null, toDisplayString(__props.title), 1 /* TEXT */)
|
|
1504
|
+
]))
|
|
1505
|
+
: createCommentVNode("v-if", true)
|
|
1506
|
+
])
|
|
1507
|
+
], true)
|
|
1508
|
+
], 48 /* FULL_PROPS, NEED_HYDRATION */, _hoisted_2))
|
|
1509
|
+
: createCommentVNode("v-if", true)
|
|
1510
|
+
]))
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
};
|
|
1515
|
+
const EodashLayerControl = /*#__PURE__*/_export_sfc(_sfc_main, [['styles',[_style_0]],['__scopeId',"data-v-219e33f9"]]);
|
|
1516
|
+
|
|
1517
|
+
export { EodashLayerControl as default };
|