@schukai/monster 4.67.0 → 4.69.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/CHANGELOG.md +23 -0
- package/package.json +1 -1
- package/source/components/content/image-editor.mjs +3251 -0
- package/source/components/datatable/filter/date-presets.mjs +18 -6
- package/source/components/datatable/filter/date-time.mjs +7 -4
- package/source/components/datatable/filter/date.mjs +7 -4
- package/source/components/datatable/filter/text-operator.mjs +14 -5
- package/source/components/datatable/filter/time.mjs +7 -4
- package/source/components/files/file-manager.mjs +2464 -0
- package/source/components/files/stylesheet/file-manager.mjs +55 -0
- package/source/components/layout/panel.mjs +16 -1
- package/source/components/layout/split-panel.mjs +8 -0
- package/source/components/layout/tabs.mjs +48 -19
- package/source/monster.mjs +11 -5
- package/source/types/version.mjs +1 -1
- package/test/cases/monster.mjs +1 -1
- package/test/web/test.html +2 -2
- package/test/web/tests.js +62 -23
|
@@ -0,0 +1,2464 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © Volker Schukai and all contributing authors, {{copyRightYear}}. All rights reserved.
|
|
3
|
+
* Node module: @schukai/monster
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
|
|
6
|
+
* The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
|
|
7
|
+
*
|
|
8
|
+
* For those who do not wish to adhere to the AGPLv3, a commercial license is available.
|
|
9
|
+
* Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
|
|
10
|
+
* For more information about purchasing a commercial license, please contact Volker Schukai.
|
|
11
|
+
*
|
|
12
|
+
* SPDX-License-Identifier: AGPL-3.0
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { instanceSymbol } from "../../constants.mjs";
|
|
16
|
+
import { internalSymbol } from "../../constants.mjs";
|
|
17
|
+
import {
|
|
18
|
+
assembleMethodSymbol,
|
|
19
|
+
CustomElement,
|
|
20
|
+
registerCustomElement,
|
|
21
|
+
} from "../../dom/customelement.mjs";
|
|
22
|
+
import { addAttributeToken } from "../../dom/attributes.mjs";
|
|
23
|
+
import { ATTRIBUTE_ERRORMESSAGE } from "../../dom/constants.mjs";
|
|
24
|
+
import {
|
|
25
|
+
findElementWithSelectorUpwards,
|
|
26
|
+
getDocument,
|
|
27
|
+
getWindow,
|
|
28
|
+
} from "../../dom/util.mjs";
|
|
29
|
+
import { Formatter } from "../../text/formatter.mjs";
|
|
30
|
+
import { diff } from "../../data/diff.mjs";
|
|
31
|
+
import { Pathfinder } from "../../data/pathfinder.mjs";
|
|
32
|
+
import { buildTree } from "../../data/buildtree.mjs";
|
|
33
|
+
import { ID } from "../../types/id.mjs";
|
|
34
|
+
import { NodeRecursiveIterator } from "../../types/noderecursiveiterator.mjs";
|
|
35
|
+
import { Observer } from "../../types/observer.mjs";
|
|
36
|
+
import { clone } from "../../util/clone.mjs";
|
|
37
|
+
import { isArray, isFunction, isObject, isString } from "../../types/is.mjs";
|
|
38
|
+
import { DeadMansSwitch } from "../../util/deadmansswitch.mjs";
|
|
39
|
+
import { CommonStyleSheet } from "../stylesheet/common.mjs";
|
|
40
|
+
import { ThemeStyleSheet } from "../stylesheet/theme.mjs";
|
|
41
|
+
import { FileManagerStyleSheet } from "./stylesheet/file-manager.mjs";
|
|
42
|
+
import "../content/image-editor.mjs";
|
|
43
|
+
import { ATTRIBUTE_BUTTON_LABEL } from "../form/constants.mjs";
|
|
44
|
+
import { getLocaleOfDocument } from "../../dom/locale.mjs";
|
|
45
|
+
|
|
46
|
+
import "../layout/split-panel.mjs";
|
|
47
|
+
import "../layout/panel.mjs";
|
|
48
|
+
import "../layout/tabs.mjs";
|
|
49
|
+
import { State } from "../state/state.mjs";
|
|
50
|
+
import "../tree-menu/tree-menu.mjs";
|
|
51
|
+
import "../form/message-state-button.mjs";
|
|
52
|
+
import "../datatable/datasource/rest.mjs";
|
|
53
|
+
|
|
54
|
+
export { FileManager };
|
|
55
|
+
|
|
56
|
+
void State;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @private
|
|
60
|
+
* @type {symbol}
|
|
61
|
+
*/
|
|
62
|
+
const splitPanelSymbol = Symbol("splitPanel");
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @private
|
|
66
|
+
* @type {symbol}
|
|
67
|
+
*/
|
|
68
|
+
const treeMenuSymbol = Symbol("treeMenu");
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @private
|
|
72
|
+
* @type {symbol}
|
|
73
|
+
*/
|
|
74
|
+
const tabsSymbol = Symbol("tabs");
|
|
75
|
+
const tabsContainerSymbol = Symbol("tabsContainer");
|
|
76
|
+
const tabsResizeObserverSymbol = Symbol("tabsResizeObserver");
|
|
77
|
+
const hostResizeObserverSymbol = Symbol("hostResizeObserver");
|
|
78
|
+
const editorSizeRafSymbol = Symbol("editorSizeRaf");
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @private
|
|
82
|
+
* @type {symbol}
|
|
83
|
+
*/
|
|
84
|
+
const saveButtonSymbol = Symbol("saveButton");
|
|
85
|
+
const titleSymbol = Symbol("title");
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @private
|
|
89
|
+
* @type {symbol}
|
|
90
|
+
*/
|
|
91
|
+
const emptyStateSymbol = Symbol("emptyState");
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @private
|
|
95
|
+
* @type {symbol}
|
|
96
|
+
*/
|
|
97
|
+
const listDatasourceSymbol = Symbol("listDatasource");
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @private
|
|
101
|
+
* @type {symbol}
|
|
102
|
+
*/
|
|
103
|
+
const fileDatasourceSymbol = Symbol("fileDatasource");
|
|
104
|
+
const listDatasourceOwnedSymbol = Symbol("listDatasourceOwned");
|
|
105
|
+
const fileDatasourceOwnedSymbol = Symbol("fileDatasourceOwned");
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @private
|
|
109
|
+
* @type {symbol}
|
|
110
|
+
*/
|
|
111
|
+
const tabsByKeySymbol = Symbol("tabsByKey");
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @private
|
|
115
|
+
* @type {symbol}
|
|
116
|
+
*/
|
|
117
|
+
const tabsByReferenceSymbol = Symbol("tabsByReference");
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @private
|
|
121
|
+
* @type {symbol}
|
|
122
|
+
*/
|
|
123
|
+
const entryByValueSymbol = Symbol("entryByValue");
|
|
124
|
+
const treeEntriesSymbol = Symbol("treeEntries");
|
|
125
|
+
const loadedParentsSymbol = Symbol("loadedParents");
|
|
126
|
+
const listRequestParentSymbol = Symbol("listRequestParent");
|
|
127
|
+
const listRequestInFlightSymbol = Symbol("listRequestInFlight");
|
|
128
|
+
const queuedParentSymbol = Symbol("queuedParent");
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @private
|
|
132
|
+
* @type {symbol}
|
|
133
|
+
*/
|
|
134
|
+
const saveAllButtonSymbol = Symbol("saveAllButton");
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* A file manager for navigating and editing files.
|
|
138
|
+
*
|
|
139
|
+
* @fragments /fragments/components/files/file-manager/
|
|
140
|
+
*
|
|
141
|
+
* @since 4.44.0
|
|
142
|
+
* @summary A file manager control that builds a tree navigation from an API and opens files in tabs.
|
|
143
|
+
*/
|
|
144
|
+
class FileManager extends CustomElement {
|
|
145
|
+
/**
|
|
146
|
+
* This method is called by the `instanceof` operator.
|
|
147
|
+
* @return {symbol}
|
|
148
|
+
*/
|
|
149
|
+
static get [instanceSymbol]() {
|
|
150
|
+
return Symbol.for("@schukai/monster/components/files/file-manager");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* To set the options via the HTML tag, the attribute `data-monster-options` must be used.
|
|
155
|
+
* @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
|
|
156
|
+
*
|
|
157
|
+
* @property {Object} templates Template definitions
|
|
158
|
+
* @property {string} templates.main Main template
|
|
159
|
+
* @property {Object} labels Label definitions
|
|
160
|
+
* @property {string} labels.title Title label
|
|
161
|
+
* @property {string} labels.save Save button label
|
|
162
|
+
* @property {string} labels.empty Empty state label
|
|
163
|
+
* @property {Object} layout Layout configuration
|
|
164
|
+
* @property {Object} layout.split Split panel options
|
|
165
|
+
* @property {Object} mapping Mapping for list and content
|
|
166
|
+
* @property {string} mapping.data Path to list data in the datasource response
|
|
167
|
+
* @property {string} mapping.id Template or key for entry id
|
|
168
|
+
* @property {string} mapping.parent Template or key for entry parent
|
|
169
|
+
* @property {string} mapping.label Template or key for entry label
|
|
170
|
+
* @property {string} mapping.value Template or key for entry value
|
|
171
|
+
* @property {string} mapping.path Template or key for entry path
|
|
172
|
+
* @property {string} mapping.type Template or key for entry type
|
|
173
|
+
* @property {string} mapping.icon Template or key for entry icon id
|
|
174
|
+
* @property {string} mapping.contentPath Path to content in file read response
|
|
175
|
+
* @property {Object} datasource Datasource definitions
|
|
176
|
+
* @property {Object} datasource.list List datasource options
|
|
177
|
+
* @property {string} datasource.list.selector Selector for existing datasource
|
|
178
|
+
* @property {Object} datasource.list.rest Options for monster-datasource-rest
|
|
179
|
+
* @property {Object} datasource.file File datasource options
|
|
180
|
+
* @property {string} datasource.file.selector Selector for existing datasource
|
|
181
|
+
* @property {Object} datasource.file.rest Options for monster-datasource-rest
|
|
182
|
+
* @property {Object} editor Editor configuration
|
|
183
|
+
* @property {string} editor.tag Tag name for editor element
|
|
184
|
+
* @property {Object} editor.adapter Adapter with getValue/setValue/bindChange
|
|
185
|
+
* @property {Object} tabs Tabs configuration
|
|
186
|
+
* @property {string} tabs.dirtyMarker Marker appended to tab labels
|
|
187
|
+
* @property {Object} icons Icon mapping configuration
|
|
188
|
+
*/
|
|
189
|
+
get defaults() {
|
|
190
|
+
return Object.assign({}, super.defaults, {
|
|
191
|
+
templates: {
|
|
192
|
+
main: getTemplate(),
|
|
193
|
+
},
|
|
194
|
+
labels: getTranslations(),
|
|
195
|
+
layout: {
|
|
196
|
+
split: {
|
|
197
|
+
type: "vertical",
|
|
198
|
+
initial: "20%",
|
|
199
|
+
min: "15%",
|
|
200
|
+
max: "45%",
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
mapping: {
|
|
204
|
+
data: "dataset",
|
|
205
|
+
id: "id",
|
|
206
|
+
parent: "parent",
|
|
207
|
+
label: "${name}",
|
|
208
|
+
value: "${path}",
|
|
209
|
+
path: "${path}",
|
|
210
|
+
type: "${type}",
|
|
211
|
+
icon: null,
|
|
212
|
+
contentPath: "content",
|
|
213
|
+
},
|
|
214
|
+
datasource: {
|
|
215
|
+
list: {
|
|
216
|
+
selector: null,
|
|
217
|
+
rest: {
|
|
218
|
+
read: {
|
|
219
|
+
url: null,
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
file: {
|
|
224
|
+
selector: null,
|
|
225
|
+
rest: {
|
|
226
|
+
read: {
|
|
227
|
+
url: null,
|
|
228
|
+
},
|
|
229
|
+
write: {
|
|
230
|
+
url: null,
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
editor: {
|
|
236
|
+
readOnly: false,
|
|
237
|
+
defaultTag: "textarea",
|
|
238
|
+
byMimeType: {
|
|
239
|
+
"image/png": "monster-image-editor",
|
|
240
|
+
"image/jpeg": "monster-image-editor",
|
|
241
|
+
"image/gif": "monster-image-editor",
|
|
242
|
+
"image/svg+xml": "monster-image-editor",
|
|
243
|
+
"image/webp": "monster-image-editor",
|
|
244
|
+
"image/bmp": "monster-image-editor",
|
|
245
|
+
"image/x-icon": "monster-image-editor",
|
|
246
|
+
"text/plain": "textarea",
|
|
247
|
+
"text/markdown": "textarea",
|
|
248
|
+
"text/css": "textarea",
|
|
249
|
+
"text/html": "textarea",
|
|
250
|
+
"text/javascript": "textarea",
|
|
251
|
+
"application/json": "textarea",
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
tabs: {
|
|
255
|
+
dirtyMarker: "*",
|
|
256
|
+
},
|
|
257
|
+
persistence: {
|
|
258
|
+
enabled: true,
|
|
259
|
+
keyPrefix: "monster-file-manager-diff",
|
|
260
|
+
debounceMs: 300,
|
|
261
|
+
},
|
|
262
|
+
lazy: {
|
|
263
|
+
enabled: false,
|
|
264
|
+
parameter: "path",
|
|
265
|
+
root: null,
|
|
266
|
+
},
|
|
267
|
+
icons: {
|
|
268
|
+
default: "monster-file-icon-default",
|
|
269
|
+
code: "monster-file-icon-code",
|
|
270
|
+
image: "monster-file-icon-image",
|
|
271
|
+
sound: "monster-file-icon-sound",
|
|
272
|
+
pdf: "monster-file-icon-pdf",
|
|
273
|
+
css: "monster-file-icon-css",
|
|
274
|
+
csv: "monster-file-icon-csv",
|
|
275
|
+
doc: "monster-file-icon-doc",
|
|
276
|
+
jpg: "monster-file-icon-jpg",
|
|
277
|
+
html: "monster-file-icon-html",
|
|
278
|
+
gif: "monster-file-icon-gif",
|
|
279
|
+
mp4: "monster-file-icon-mp4",
|
|
280
|
+
mov: "monster-file-icon-mov",
|
|
281
|
+
mp3: "monster-file-icon-mp3",
|
|
282
|
+
txt: "monster-file-icon-txt",
|
|
283
|
+
png: "monster-file-icon-png",
|
|
284
|
+
byExtension: {
|
|
285
|
+
js: "code",
|
|
286
|
+
ts: "code",
|
|
287
|
+
json: "code",
|
|
288
|
+
css: "css",
|
|
289
|
+
scss: "css",
|
|
290
|
+
html: "html",
|
|
291
|
+
htm: "html",
|
|
292
|
+
txt: "txt",
|
|
293
|
+
md: "txt",
|
|
294
|
+
csv: "csv",
|
|
295
|
+
pdf: "pdf",
|
|
296
|
+
png: "png",
|
|
297
|
+
jpg: "jpg",
|
|
298
|
+
jpeg: "jpg",
|
|
299
|
+
gif: "gif",
|
|
300
|
+
svg: "image",
|
|
301
|
+
mp3: "mp3",
|
|
302
|
+
wav: "sound",
|
|
303
|
+
ogg: "sound",
|
|
304
|
+
mp4: "mp4",
|
|
305
|
+
m4p: "mp4",
|
|
306
|
+
mov: "mov",
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
debug: false,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* @return {CSSStyleSheet[]}
|
|
315
|
+
*/
|
|
316
|
+
static getCSSStyleSheet() {
|
|
317
|
+
return [CommonStyleSheet, ThemeStyleSheet, FileManagerStyleSheet];
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* @return {string}
|
|
322
|
+
*/
|
|
323
|
+
static getTag() {
|
|
324
|
+
return "monster-file-manager";
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* @param {object} options
|
|
329
|
+
* @return {FileManager}
|
|
330
|
+
*/
|
|
331
|
+
setOptions(options) {
|
|
332
|
+
super.setOptions(options);
|
|
333
|
+
queueMicrotask(() => {
|
|
334
|
+
syncDatasources.call(this);
|
|
335
|
+
refreshLabels.call(this);
|
|
336
|
+
});
|
|
337
|
+
return this;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* @return {void}
|
|
342
|
+
*/
|
|
343
|
+
[assembleMethodSymbol]() {
|
|
344
|
+
super[assembleMethodSymbol]();
|
|
345
|
+
|
|
346
|
+
this[tabsByKeySymbol] = new Map();
|
|
347
|
+
this[tabsByReferenceSymbol] = new Map();
|
|
348
|
+
this[entryByValueSymbol] = new Map();
|
|
349
|
+
this[treeEntriesSymbol] = [];
|
|
350
|
+
this[loadedParentsSymbol] = new Set();
|
|
351
|
+
|
|
352
|
+
queueMicrotask(() => {
|
|
353
|
+
initControlReferences.call(this);
|
|
354
|
+
initSplitPanel.call(this);
|
|
355
|
+
initTabs.call(this);
|
|
356
|
+
initSaveButton.call(this);
|
|
357
|
+
initTreeMenu.call(this);
|
|
358
|
+
initDatasources.call(this);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
this[internalSymbol].attachObserver(
|
|
362
|
+
new Observer(() => {
|
|
363
|
+
syncDatasources.call(this);
|
|
364
|
+
refreshLabels.call(this);
|
|
365
|
+
}),
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* @private
|
|
372
|
+
* @return {FileManager}
|
|
373
|
+
*/
|
|
374
|
+
function initControlReferences() {
|
|
375
|
+
if (!this.shadowRoot) {
|
|
376
|
+
throw new Error("no shadow-root is defined");
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
this[titleSymbol] = this.shadowRoot.querySelector(
|
|
380
|
+
"[data-monster-role=title]",
|
|
381
|
+
);
|
|
382
|
+
this[splitPanelSymbol] = this.shadowRoot.querySelector(
|
|
383
|
+
"[data-monster-role=split-panel]",
|
|
384
|
+
);
|
|
385
|
+
this[treeMenuSymbol] = this.shadowRoot.querySelector(
|
|
386
|
+
"[data-monster-role=tree]",
|
|
387
|
+
);
|
|
388
|
+
this[tabsSymbol] = this.shadowRoot.querySelector("[data-monster-role=tabs]");
|
|
389
|
+
this[tabsContainerSymbol] = this.shadowRoot.querySelector(
|
|
390
|
+
"[data-monster-role=tabs-container]",
|
|
391
|
+
);
|
|
392
|
+
this[saveButtonSymbol] = this.shadowRoot.querySelector(
|
|
393
|
+
"[data-monster-role=save-button]",
|
|
394
|
+
);
|
|
395
|
+
this[saveAllButtonSymbol] = this.shadowRoot.querySelector(
|
|
396
|
+
"[data-monster-role=save-all-button]",
|
|
397
|
+
);
|
|
398
|
+
this[emptyStateSymbol] = this.shadowRoot.querySelector(
|
|
399
|
+
"[data-monster-role=empty]",
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
return this;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* @private
|
|
407
|
+
* @return {void}
|
|
408
|
+
*/
|
|
409
|
+
function initSplitPanel() {
|
|
410
|
+
if (!this[splitPanelSymbol]) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const split = this.getOption("layout.split", {});
|
|
415
|
+
const initial = split.initial || "20%";
|
|
416
|
+
this[splitPanelSymbol].setOptions({
|
|
417
|
+
splitType: split.type || "vertical",
|
|
418
|
+
dimension: {
|
|
419
|
+
initial: initial,
|
|
420
|
+
min: split.min || "15%",
|
|
421
|
+
max: split.max || "45%",
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
this[splitPanelSymbol].setDimension(initial);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* @private
|
|
429
|
+
* @return {void}
|
|
430
|
+
*/
|
|
431
|
+
function initSaveButton() {
|
|
432
|
+
if (!this[saveButtonSymbol]) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
this[saveButtonSymbol].setOption(
|
|
437
|
+
"labels.button",
|
|
438
|
+
this.getOption("labels.save"),
|
|
439
|
+
);
|
|
440
|
+
this[saveButtonSymbol].setOption("disabled", true);
|
|
441
|
+
|
|
442
|
+
this[saveButtonSymbol].setOption("actions.click", () => {
|
|
443
|
+
saveActiveTab.call(this);
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
if (this[saveAllButtonSymbol]) {
|
|
447
|
+
this[saveAllButtonSymbol].setOption(
|
|
448
|
+
"labels.button",
|
|
449
|
+
this.getOption("labels.saveAll"),
|
|
450
|
+
);
|
|
451
|
+
this[saveAllButtonSymbol].setOption("disabled", true);
|
|
452
|
+
this[saveAllButtonSymbol].setOption("actions.click", () => {
|
|
453
|
+
saveAllTabs.call(this);
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* @private
|
|
460
|
+
* @return {void}
|
|
461
|
+
*/
|
|
462
|
+
function initTabs() {
|
|
463
|
+
if (!this[tabsSymbol]) {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
this[tabsSymbol].setOption("debug", this.getOption("debug") === true);
|
|
468
|
+
this[tabsSymbol].setOption("features.openFirst", false);
|
|
469
|
+
|
|
470
|
+
this[tabsSymbol].addEventListener("monster-tab-changed", (event) => {
|
|
471
|
+
const reference = event?.detail?.reference;
|
|
472
|
+
if (!reference) {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
updateEmptyState.call(this);
|
|
477
|
+
updateSaveButtonState.call(this);
|
|
478
|
+
scheduleUpdateEditorSizes.call(this);
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
this[tabsSymbol].addEventListener("monster-tab-remove", (event) => {
|
|
482
|
+
const reference = event?.detail?.reference;
|
|
483
|
+
if (!reference) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const tabData = this[tabsByReferenceSymbol].get(reference);
|
|
488
|
+
if (tabData?.unbindChange) {
|
|
489
|
+
tabData.unbindChange();
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
this[tabsByReferenceSymbol].delete(reference);
|
|
493
|
+
if (tabData?.key) {
|
|
494
|
+
this[tabsByKeySymbol].delete(tabData.key);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
updateEmptyState.call(this);
|
|
498
|
+
updateSaveButtonState.call(this);
|
|
499
|
+
scheduleUpdateEditorSizes.call(this);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
initHostResizeObserver.call(this);
|
|
503
|
+
scheduleUpdateEditorSizes.call(this);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* @private
|
|
508
|
+
* @return {void}
|
|
509
|
+
*/
|
|
510
|
+
function initTreeMenu() {
|
|
511
|
+
if (!this[treeMenuSymbol]) {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
this[treeMenuSymbol].setOptions({
|
|
516
|
+
mapping: getTreeMenuMapping.call(this),
|
|
517
|
+
actions: {
|
|
518
|
+
select: (entry) => {
|
|
519
|
+
const key = entry?.value;
|
|
520
|
+
if (!key) {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const fileEntry = this[entryByValueSymbol].get(key);
|
|
525
|
+
if (!fileEntry) {
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
openFile.call(this, fileEntry);
|
|
530
|
+
},
|
|
531
|
+
open: (entry) => {
|
|
532
|
+
handleTreeOpen.call(this, entry);
|
|
533
|
+
},
|
|
534
|
+
},
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* @private
|
|
540
|
+
* @return {void}
|
|
541
|
+
*/
|
|
542
|
+
function initDatasources() {
|
|
543
|
+
initListDatasource.call(this);
|
|
544
|
+
initFileDatasource.call(this);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* @private
|
|
549
|
+
* @return {void}
|
|
550
|
+
*/
|
|
551
|
+
function initListDatasource() {
|
|
552
|
+
const selector = this.getOption("datasource.list.selector");
|
|
553
|
+
if (isString(selector) && selector.trim() !== "") {
|
|
554
|
+
const element = findElementWithSelectorUpwards(this, selector.trim());
|
|
555
|
+
if (element instanceof HTMLElement) {
|
|
556
|
+
customElements
|
|
557
|
+
.whenDefined(element.tagName.toLowerCase())
|
|
558
|
+
.then(() => {
|
|
559
|
+
logDebug.call(this, "list datasource found via selector", selector);
|
|
560
|
+
this[listDatasourceSymbol] = element;
|
|
561
|
+
attachListDatasourceListeners.call(this);
|
|
562
|
+
requestList.call(this, this.getOption("lazy.root", null));
|
|
563
|
+
})
|
|
564
|
+
.catch((err) => {
|
|
565
|
+
addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, err.message);
|
|
566
|
+
});
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
addAttributeToken(
|
|
571
|
+
this,
|
|
572
|
+
ATTRIBUTE_ERRORMESSAGE,
|
|
573
|
+
"list datasource selector not found",
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const restConfig = this.getOption("datasource.list.rest", {});
|
|
578
|
+
if (!isObject(restConfig)) {
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const rest = getDocument().createElement("monster-datasource-rest");
|
|
583
|
+
rest.setOptions(restConfig);
|
|
584
|
+
rest.setOption("features.autoInit", true);
|
|
585
|
+
rest.setOption("autoInit.oneTime", true);
|
|
586
|
+
this.appendChild(rest);
|
|
587
|
+
this[listDatasourceSymbol] = rest;
|
|
588
|
+
this[listDatasourceOwnedSymbol] = true;
|
|
589
|
+
logDebug.call(this, "list datasource created", restConfig?.read?.url);
|
|
590
|
+
|
|
591
|
+
attachListDatasourceListeners.call(this);
|
|
592
|
+
requestList.call(this, this.getOption("lazy.root", null));
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* @private
|
|
597
|
+
* @return {void}
|
|
598
|
+
*/
|
|
599
|
+
function initFileDatasource() {
|
|
600
|
+
const selector = this.getOption("datasource.file.selector");
|
|
601
|
+
if (isString(selector) && selector.trim() !== "") {
|
|
602
|
+
const element = findElementWithSelectorUpwards(this, selector.trim());
|
|
603
|
+
if (element instanceof HTMLElement) {
|
|
604
|
+
customElements
|
|
605
|
+
.whenDefined(element.tagName.toLowerCase())
|
|
606
|
+
.then(() => {
|
|
607
|
+
this[fileDatasourceSymbol] = element;
|
|
608
|
+
})
|
|
609
|
+
.catch((err) => {
|
|
610
|
+
addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, err.message);
|
|
611
|
+
});
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
addAttributeToken(
|
|
616
|
+
this,
|
|
617
|
+
ATTRIBUTE_ERRORMESSAGE,
|
|
618
|
+
"file datasource selector not found",
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
const restConfig = this.getOption("datasource.file.rest", {});
|
|
623
|
+
if (!isObject(restConfig)) {
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
const rest = getDocument().createElement("monster-datasource-rest");
|
|
628
|
+
rest.setOptions(restConfig);
|
|
629
|
+
this.appendChild(rest);
|
|
630
|
+
this[fileDatasourceSymbol] = rest;
|
|
631
|
+
this[fileDatasourceOwnedSymbol] = true;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* @private
|
|
636
|
+
* @return {void}
|
|
637
|
+
*/
|
|
638
|
+
function attachListDatasourceListeners() {
|
|
639
|
+
const datasource = this[listDatasourceSymbol];
|
|
640
|
+
if (!(datasource instanceof HTMLElement)) {
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const hasObserver = !!datasource.datasource?.attachObserver;
|
|
645
|
+
if (!hasObserver) {
|
|
646
|
+
datasource.addEventListener("monster-datasource-fetched", () => {
|
|
647
|
+
logDebug.call(this, "list datasource fetched");
|
|
648
|
+
applyListData.call(this);
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
datasource.addEventListener("monster-datasource-error", (event) => {
|
|
652
|
+
logDebug.call(this, "list datasource error", event?.detail?.error?.message);
|
|
653
|
+
clearListRequest.call(this);
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
if (datasource.datasource && hasObserver) {
|
|
657
|
+
datasource.datasource.attachObserver(
|
|
658
|
+
new Observer(() => {
|
|
659
|
+
logDebug.call(this, "list datasource fetched");
|
|
660
|
+
applyListData.call(this);
|
|
661
|
+
}),
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* @private
|
|
668
|
+
* @return {void}
|
|
669
|
+
*/
|
|
670
|
+
function applyListData() {
|
|
671
|
+
const datasource = this[listDatasourceSymbol];
|
|
672
|
+
if (!datasource || !datasource.data) {
|
|
673
|
+
logDebug.call(this, "list datasource has no data");
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
let data = datasource.data;
|
|
678
|
+
const dataPath = this.getOption("mapping.data");
|
|
679
|
+
|
|
680
|
+
if (isString(dataPath) && dataPath.trim() !== "") {
|
|
681
|
+
const pathfinder = new Pathfinder(data);
|
|
682
|
+
if (pathfinder.exists(dataPath)) {
|
|
683
|
+
data = pathfinder.getVia(dataPath);
|
|
684
|
+
} else {
|
|
685
|
+
logDebug.call(
|
|
686
|
+
this,
|
|
687
|
+
"list datasource path not found, falling back to root data",
|
|
688
|
+
dataPath,
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
if (isObject(data)) {
|
|
694
|
+
data = Object.values(data);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
if (!isArray(data)) {
|
|
698
|
+
data = [];
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
const lazyEnabled = this.getOption("lazy.enabled", false) === true;
|
|
702
|
+
const listParent = lazyEnabled
|
|
703
|
+
? (this[listRequestParentSymbol] ?? this.getOption("lazy.root", null))
|
|
704
|
+
: null;
|
|
705
|
+
|
|
706
|
+
const entries = [];
|
|
707
|
+
const entryByValue = new Map();
|
|
708
|
+
|
|
709
|
+
for (const entry of data) {
|
|
710
|
+
const normalized = normalizeEntry.call(this, entry);
|
|
711
|
+
if (lazyEnabled) {
|
|
712
|
+
if (!matchesParent(normalized.parent, listParent)) {
|
|
713
|
+
continue;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
entries.push(normalized);
|
|
717
|
+
entryByValue.set(normalized.value, normalized);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
logDebug.call(
|
|
721
|
+
this,
|
|
722
|
+
"list entries normalized",
|
|
723
|
+
lazyEnabled
|
|
724
|
+
? `${entries.length} (parent ${listParent ?? "root"})`
|
|
725
|
+
: entries.length,
|
|
726
|
+
);
|
|
727
|
+
|
|
728
|
+
if (lazyEnabled) {
|
|
729
|
+
mergeLazyEntries.call(this, entries, listParent);
|
|
730
|
+
} else {
|
|
731
|
+
this[entryByValueSymbol] = entryByValue;
|
|
732
|
+
const treeEntries = buildTreeEntries.call(this, entries);
|
|
733
|
+
this[treeEntriesSymbol] = treeEntries;
|
|
734
|
+
if (this[treeMenuSymbol]) {
|
|
735
|
+
this[treeMenuSymbol].setOption("entries", treeEntries);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
clearListRequest.call(this);
|
|
740
|
+
updateEmptyState.call(this);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* @private
|
|
745
|
+
* @param {object} entry
|
|
746
|
+
* @return {object}
|
|
747
|
+
*/
|
|
748
|
+
function normalizeEntry(entry) {
|
|
749
|
+
const mapping = this.getOption("mapping", {});
|
|
750
|
+
const formatted = {
|
|
751
|
+
id: formatEntryValue(entry, mapping.id),
|
|
752
|
+
parent: formatEntryValue(entry, mapping.parent),
|
|
753
|
+
label: formatEntryValue(entry, mapping.label),
|
|
754
|
+
value: formatEntryValue(entry, mapping.value),
|
|
755
|
+
path: formatEntryValue(entry, mapping.path),
|
|
756
|
+
type: formatEntryValue(entry, mapping.type),
|
|
757
|
+
icon: formatEntryValue(entry, mapping.icon),
|
|
758
|
+
hasChildren:
|
|
759
|
+
entry?.hasChildren === true ||
|
|
760
|
+
entry?.["has-children"] === true ||
|
|
761
|
+
entry?.has_children === true,
|
|
762
|
+
raw: entry,
|
|
763
|
+
};
|
|
764
|
+
|
|
765
|
+
if (!formatted.path && isString(formatted.value)) {
|
|
766
|
+
formatted.path = formatted.value;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if (!formatted.value && isString(formatted.path)) {
|
|
770
|
+
formatted.value = formatted.path;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
if (!formatted.id && isString(formatted.path)) {
|
|
774
|
+
formatted.id = formatted.path;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
if (!formatted.parent && isString(formatted.path)) {
|
|
778
|
+
const parent = formatted.path.split("/").slice(0, -1).join("/");
|
|
779
|
+
formatted.parent = parent === "" ? null : parent;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
if (!formatted.label) {
|
|
783
|
+
formatted.label = formatted.path || formatted.id || "file";
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
if (!formatted.type && isString(formatted.path)) {
|
|
787
|
+
formatted.type = getTypeFromPath(formatted.path);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
if (formatted.hasChildren !== true) {
|
|
791
|
+
formatted.hasChildren = formatted.type === "folder";
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if (!formatted.icon) {
|
|
795
|
+
formatted.icon = getIconIdForEntry.call(this, formatted);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
return formatted;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* @private
|
|
803
|
+
* @param {object} entry
|
|
804
|
+
* @return {string}
|
|
805
|
+
*/
|
|
806
|
+
function getIconIdForEntry(entry) {
|
|
807
|
+
const icons = this.getOption("icons", {});
|
|
808
|
+
const byExtension = icons.byExtension || {};
|
|
809
|
+
const type = isString(entry.type) ? entry.type.toLowerCase() : "";
|
|
810
|
+
const ext = getExtension(entry.path || "");
|
|
811
|
+
let iconKey = "";
|
|
812
|
+
|
|
813
|
+
if (ext && byExtension[ext]) {
|
|
814
|
+
iconKey = byExtension[ext];
|
|
815
|
+
} else if (type && icons[type]) {
|
|
816
|
+
iconKey = type;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
if (!iconKey || !icons[iconKey]) {
|
|
820
|
+
return icons.default;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
return icons[iconKey];
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* @private
|
|
828
|
+
* @param {string} path
|
|
829
|
+
* @return {string}
|
|
830
|
+
*/
|
|
831
|
+
function getTypeFromPath(path) {
|
|
832
|
+
const ext = getExtension(path);
|
|
833
|
+
if (!ext) {
|
|
834
|
+
return "file";
|
|
835
|
+
}
|
|
836
|
+
return ext.toLowerCase();
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* @private
|
|
841
|
+
* @param {string} path
|
|
842
|
+
* @return {string}
|
|
843
|
+
*/
|
|
844
|
+
function getExtension(path) {
|
|
845
|
+
if (!isString(path)) {
|
|
846
|
+
return "";
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
const last = path.split("/").pop() || "";
|
|
850
|
+
const index = last.lastIndexOf(".");
|
|
851
|
+
if (index === -1) {
|
|
852
|
+
return "";
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
return last.substring(index + 1).toLowerCase();
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* @private
|
|
860
|
+
* @param {object} entry
|
|
861
|
+
* @param {string} template
|
|
862
|
+
* @return {string}
|
|
863
|
+
*/
|
|
864
|
+
function formatEntryValue(entry, template) {
|
|
865
|
+
if (!isString(template) || template.trim() === "") {
|
|
866
|
+
return "";
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
if (template.includes("${")) {
|
|
870
|
+
try {
|
|
871
|
+
return new Formatter(entry).format(template);
|
|
872
|
+
} catch (e) {
|
|
873
|
+
return "";
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
if (entry && Object.prototype.hasOwnProperty.call(entry, template)) {
|
|
878
|
+
return entry[template];
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
return "";
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* @private
|
|
886
|
+
* @return {object}
|
|
887
|
+
*/
|
|
888
|
+
function getTreeMenuMapping() {
|
|
889
|
+
return {
|
|
890
|
+
rootReferences: [null, undefined, 0, "0", ""],
|
|
891
|
+
idTemplate: "id",
|
|
892
|
+
parentKey: "parent",
|
|
893
|
+
selector: "*",
|
|
894
|
+
labelTemplate: "${label}",
|
|
895
|
+
valueTemplate: "${value}",
|
|
896
|
+
iconTemplate: "${icon}",
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* @private
|
|
902
|
+
* @param {object} entry
|
|
903
|
+
* @return {void}
|
|
904
|
+
*/
|
|
905
|
+
function handleTreeOpen(entry) {
|
|
906
|
+
const lazyEnabled = this.getOption("lazy.enabled", false) === true;
|
|
907
|
+
if (!lazyEnabled) {
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
const key = entry?.value;
|
|
911
|
+
if (!key) {
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
const normalized = this[entryByValueSymbol].get(key);
|
|
915
|
+
if (!normalized || normalized.type !== "folder") {
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
if (normalized.hasChildren !== true) {
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
if (this[loadedParentsSymbol].has(normalized.path)) {
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
requestList.call(this, normalized.path);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
/**
|
|
930
|
+
* @private
|
|
931
|
+
* @param {Array} entries
|
|
932
|
+
* @return {Array}
|
|
933
|
+
*/
|
|
934
|
+
function buildTreeEntries(entries) {
|
|
935
|
+
const nodes = buildTree(entries, "*", "id", "parent", {
|
|
936
|
+
rootReferences: [null, undefined, ""],
|
|
937
|
+
});
|
|
938
|
+
|
|
939
|
+
const options = [];
|
|
940
|
+
for (const node of nodes) {
|
|
941
|
+
const iterator = new NodeRecursiveIterator(node);
|
|
942
|
+
for (const n of iterator) {
|
|
943
|
+
const entry = n.value || {};
|
|
944
|
+
const intend = n.level;
|
|
945
|
+
const visibility = intend > 0 ? "hidden" : "visible";
|
|
946
|
+
options.push({
|
|
947
|
+
value: entry.value,
|
|
948
|
+
label: entry.label,
|
|
949
|
+
icon: buildIconMarkup(entry.icon),
|
|
950
|
+
intend,
|
|
951
|
+
state: "close",
|
|
952
|
+
visibility,
|
|
953
|
+
["has-children"]: n.hasChildNodes(),
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
return options;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* @private
|
|
963
|
+
* @param {string} iconId
|
|
964
|
+
* @return {string}
|
|
965
|
+
*/
|
|
966
|
+
function buildIconMarkup(iconId) {
|
|
967
|
+
const id = isString(iconId) ? iconId : "";
|
|
968
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><use xlink:href="#${id}"></use></svg>`;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* @private
|
|
973
|
+
* @param {Array} entries
|
|
974
|
+
* @param {string|null} parentValue
|
|
975
|
+
* @return {void}
|
|
976
|
+
*/
|
|
977
|
+
function mergeLazyEntries(entries, parentValue) {
|
|
978
|
+
let treeEntries = Array.isArray(this[treeEntriesSymbol])
|
|
979
|
+
? this[treeEntriesSymbol].slice()
|
|
980
|
+
: [];
|
|
981
|
+
const loadedParents = this[loadedParentsSymbol] || new Set();
|
|
982
|
+
|
|
983
|
+
if (parentValue === null || parentValue === undefined || parentValue === "") {
|
|
984
|
+
treeEntries = [];
|
|
985
|
+
this[entryByValueSymbol] = new Map();
|
|
986
|
+
loadedParents.clear();
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
const parentIndex = findTreeEntryIndex(treeEntries, parentValue);
|
|
990
|
+
const parentIntend = parentIndex >= 0 ? treeEntries[parentIndex].intend : -1;
|
|
991
|
+
const parentState =
|
|
992
|
+
parentIndex >= 0 ? treeEntries[parentIndex].state : "open";
|
|
993
|
+
const baseVisibility = parentState === "open" ? "visible" : "hidden";
|
|
994
|
+
|
|
995
|
+
const insertIndex =
|
|
996
|
+
parentIndex >= 0
|
|
997
|
+
? getSubtreeEndIndex(treeEntries, parentIndex)
|
|
998
|
+
: treeEntries.length;
|
|
999
|
+
|
|
1000
|
+
const newEntries = [];
|
|
1001
|
+
let hasChildren = entries.length > 0;
|
|
1002
|
+
|
|
1003
|
+
for (const entry of entries) {
|
|
1004
|
+
if (!entry?.value) {
|
|
1005
|
+
continue;
|
|
1006
|
+
}
|
|
1007
|
+
if (this[entryByValueSymbol].has(entry.value)) {
|
|
1008
|
+
continue;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
this[entryByValueSymbol].set(entry.value, entry);
|
|
1012
|
+
|
|
1013
|
+
newEntries.push({
|
|
1014
|
+
value: entry.value,
|
|
1015
|
+
label: entry.label,
|
|
1016
|
+
icon: buildIconMarkup(entry.icon),
|
|
1017
|
+
intend: parentIntend + 1,
|
|
1018
|
+
state: "close",
|
|
1019
|
+
visibility: parentIndex >= 0 ? baseVisibility : "visible",
|
|
1020
|
+
["has-children"]: entry.hasChildren === true,
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
const nextEntries = [
|
|
1025
|
+
...treeEntries.slice(0, insertIndex),
|
|
1026
|
+
...newEntries,
|
|
1027
|
+
...treeEntries.slice(insertIndex),
|
|
1028
|
+
];
|
|
1029
|
+
|
|
1030
|
+
if (parentIndex >= 0) {
|
|
1031
|
+
nextEntries[parentIndex] = Object.assign({}, nextEntries[parentIndex], {
|
|
1032
|
+
["has-children"]: hasChildren,
|
|
1033
|
+
state: hasChildren ? nextEntries[parentIndex].state : "close",
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
this[treeEntriesSymbol] = nextEntries;
|
|
1038
|
+
loadedParents.add(parentValue ?? null);
|
|
1039
|
+
this[loadedParentsSymbol] = loadedParents;
|
|
1040
|
+
|
|
1041
|
+
if (this[treeMenuSymbol]) {
|
|
1042
|
+
this[treeMenuSymbol].setOption("entries", nextEntries);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
/**
|
|
1047
|
+
* @private
|
|
1048
|
+
* @param {Array} entries
|
|
1049
|
+
* @param {string|null} value
|
|
1050
|
+
* @return {number}
|
|
1051
|
+
*/
|
|
1052
|
+
function findTreeEntryIndex(entries, value) {
|
|
1053
|
+
if (!isString(value) || value.trim() === "") {
|
|
1054
|
+
return -1;
|
|
1055
|
+
}
|
|
1056
|
+
return entries.findIndex((entry) => entry.value === value);
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
/**
|
|
1060
|
+
* @private
|
|
1061
|
+
* @param {Array} entries
|
|
1062
|
+
* @param {number} index
|
|
1063
|
+
* @return {number}
|
|
1064
|
+
*/
|
|
1065
|
+
function getSubtreeEndIndex(entries, index) {
|
|
1066
|
+
if (index < 0 || index >= entries.length) {
|
|
1067
|
+
return entries.length;
|
|
1068
|
+
}
|
|
1069
|
+
const baseIntend = entries[index].intend;
|
|
1070
|
+
let i = index + 1;
|
|
1071
|
+
while (i < entries.length && entries[i].intend > baseIntend) {
|
|
1072
|
+
i += 1;
|
|
1073
|
+
}
|
|
1074
|
+
return i;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
/**
|
|
1078
|
+
* @private
|
|
1079
|
+
* @param {string|null|undefined} entryParent
|
|
1080
|
+
* @param {string|null|undefined} expectedParent
|
|
1081
|
+
* @return {boolean}
|
|
1082
|
+
*/
|
|
1083
|
+
function matchesParent(entryParent, expectedParent) {
|
|
1084
|
+
const normalized = entryParent ?? null;
|
|
1085
|
+
const expected = expectedParent ?? null;
|
|
1086
|
+
if (expected === null) {
|
|
1087
|
+
return normalized === null || normalized === "" || normalized === undefined;
|
|
1088
|
+
}
|
|
1089
|
+
return normalized === expected;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* @private
|
|
1094
|
+
* @param {string|null} parentValue
|
|
1095
|
+
* @return {void}
|
|
1096
|
+
*/
|
|
1097
|
+
function requestList(parentValue) {
|
|
1098
|
+
const datasource = this[listDatasourceSymbol];
|
|
1099
|
+
if (!datasource?.read) {
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
const isRestDatasource =
|
|
1104
|
+
datasource?.tagName?.toLowerCase?.() === "monster-datasource-rest";
|
|
1105
|
+
if (isRestDatasource) {
|
|
1106
|
+
const readUrl = getListReadUrl.call(this, parentValue);
|
|
1107
|
+
if (!isString(readUrl) || readUrl.trim() === "") {
|
|
1108
|
+
logDebug.call(this, "list datasource read url missing");
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
datasource.setOption("read.url", readUrl);
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
if (this[listRequestInFlightSymbol] === true) {
|
|
1115
|
+
this[queuedParentSymbol] = parentValue;
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
const paramName = this.getOption("lazy.parameter", "path");
|
|
1120
|
+
const params = {};
|
|
1121
|
+
if (isString(paramName) && paramName.trim() !== "") {
|
|
1122
|
+
params[paramName] = parentValue ?? "";
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
this[listRequestInFlightSymbol] = true;
|
|
1126
|
+
this[listRequestParentSymbol] = parentValue ?? null;
|
|
1127
|
+
logDebug.call(this, "list request", parentValue ?? "root");
|
|
1128
|
+
datasource.setOption("read.parameters", params);
|
|
1129
|
+
datasource.read();
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
/**
|
|
1133
|
+
* @private
|
|
1134
|
+
* @param {string|null} parentValue
|
|
1135
|
+
* @return {string|null}
|
|
1136
|
+
*/
|
|
1137
|
+
function getListReadUrl(parentValue) {
|
|
1138
|
+
const baseUrl = this.getOption("datasource.list.rest.read.url");
|
|
1139
|
+
const fallbackUrl = this[listDatasourceSymbol]?.getOption?.("read.url");
|
|
1140
|
+
const url =
|
|
1141
|
+
isString(baseUrl) && baseUrl.trim() !== "" ? baseUrl : fallbackUrl;
|
|
1142
|
+
|
|
1143
|
+
if (!isString(url) || url.trim() === "") {
|
|
1144
|
+
return null;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
const trimmed = url.trim();
|
|
1148
|
+
if (trimmed.includes("${")) {
|
|
1149
|
+
return trimmed;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
const lazyEnabled = this.getOption("lazy.enabled", false) === true;
|
|
1153
|
+
if (!lazyEnabled) {
|
|
1154
|
+
return trimmed;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
const paramName = this.getOption("lazy.parameter", "path");
|
|
1158
|
+
if (!isString(paramName) || paramName.trim() === "") {
|
|
1159
|
+
return trimmed;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
const encoded = encodeURIComponent(parentValue ?? "");
|
|
1163
|
+
const escaped = escapeRegExp(paramName);
|
|
1164
|
+
const hasParam = new RegExp(`([?&])${escaped}=`).test(trimmed);
|
|
1165
|
+
const glue = trimmed.includes("?") ? "&" : "?";
|
|
1166
|
+
|
|
1167
|
+
if (hasParam) {
|
|
1168
|
+
return trimmed.replace(
|
|
1169
|
+
new RegExp(`([?&])${escaped}=[^&]*`),
|
|
1170
|
+
`$1${paramName}=${encoded}`,
|
|
1171
|
+
);
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
return `${trimmed}${glue}${paramName}=${encoded}`;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
/**
|
|
1178
|
+
* @private
|
|
1179
|
+
* @param {string} value
|
|
1180
|
+
* @return {string}
|
|
1181
|
+
*/
|
|
1182
|
+
function escapeRegExp(value) {
|
|
1183
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
/**
|
|
1187
|
+
* @private
|
|
1188
|
+
* @return {void}
|
|
1189
|
+
*/
|
|
1190
|
+
function clearListRequest() {
|
|
1191
|
+
this[listRequestInFlightSymbol] = false;
|
|
1192
|
+
this[listRequestParentSymbol] = null;
|
|
1193
|
+
const queued = this[queuedParentSymbol];
|
|
1194
|
+
this[queuedParentSymbol] = null;
|
|
1195
|
+
if (queued !== null && queued !== undefined) {
|
|
1196
|
+
requestList.call(this, queued);
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
/**
|
|
1201
|
+
* @private
|
|
1202
|
+
* @param {object} entry
|
|
1203
|
+
* @return {void}
|
|
1204
|
+
*/
|
|
1205
|
+
function openFile(entry) {
|
|
1206
|
+
if (entry?.type === "folder") {
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
const key = entry.value || entry.id;
|
|
1210
|
+
if (!key) {
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
const existing = this[tabsByKeySymbol].get(key);
|
|
1215
|
+
if (existing) {
|
|
1216
|
+
this[tabsSymbol]?.activeTab(existing.reference);
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
const editor = createEditorElement.call(this, entry);
|
|
1221
|
+
editor.setAttribute("data-monster-role", "editor");
|
|
1222
|
+
|
|
1223
|
+
const tabReference = new ID("file-tab").toString();
|
|
1224
|
+
this[tabsSymbol]?.addTab(editor, {
|
|
1225
|
+
tabId: tabReference,
|
|
1226
|
+
label: entry.label,
|
|
1227
|
+
active: true,
|
|
1228
|
+
removable: true,
|
|
1229
|
+
});
|
|
1230
|
+
|
|
1231
|
+
const tabElement = this[tabsSymbol]?.querySelector(
|
|
1232
|
+
`#${CSS.escape(tabReference)}`,
|
|
1233
|
+
);
|
|
1234
|
+
if (!(tabElement instanceof HTMLElement)) {
|
|
1235
|
+
throw new Error("tab element not found");
|
|
1236
|
+
}
|
|
1237
|
+
tabElement.setAttribute("data-monster-role", "tab-content");
|
|
1238
|
+
|
|
1239
|
+
const adapter = getEditorAdapter.call(this);
|
|
1240
|
+
adapter.setReadOnly(editor, this.getOption("editor.readOnly", false));
|
|
1241
|
+
const tabData = {
|
|
1242
|
+
key,
|
|
1243
|
+
reference: tabReference,
|
|
1244
|
+
entry: entry,
|
|
1245
|
+
editor: editor,
|
|
1246
|
+
adapter,
|
|
1247
|
+
original: "",
|
|
1248
|
+
dirty: false,
|
|
1249
|
+
tabElement,
|
|
1250
|
+
label: entry.label,
|
|
1251
|
+
unbindChange: null,
|
|
1252
|
+
};
|
|
1253
|
+
|
|
1254
|
+
tabData.unbindChange = adapter.bindChange(editor, () => {
|
|
1255
|
+
handleEditorChange.call(this, tabData);
|
|
1256
|
+
});
|
|
1257
|
+
|
|
1258
|
+
this[tabsByKeySymbol].set(key, tabData);
|
|
1259
|
+
this[tabsByReferenceSymbol].set(tabReference, tabData);
|
|
1260
|
+
|
|
1261
|
+
this[tabsSymbol]?.activeTab(tabReference);
|
|
1262
|
+
updateEmptyState.call(this);
|
|
1263
|
+
updateSaveButtonState.call(this);
|
|
1264
|
+
scheduleUpdateEditorSizes.call(this);
|
|
1265
|
+
|
|
1266
|
+
loadFileContent.call(this, tabData).catch((err) => {
|
|
1267
|
+
addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, err.message);
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
/**
|
|
1272
|
+
* @private
|
|
1273
|
+
* @return {void}
|
|
1274
|
+
*/
|
|
1275
|
+
function initTabsResizeObserver() {
|
|
1276
|
+
if (this[tabsResizeObserverSymbol]) {
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
if (!this[tabsSymbol]) {
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
if (typeof ResizeObserver === "undefined") {
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
this[tabsResizeObserverSymbol] = new ResizeObserver(() => {
|
|
1287
|
+
scheduleUpdateEditorSizes.call(this);
|
|
1288
|
+
});
|
|
1289
|
+
this[tabsResizeObserverSymbol].observe(this[tabsSymbol]);
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
/**
|
|
1293
|
+
* @private
|
|
1294
|
+
* @return {void}
|
|
1295
|
+
*/
|
|
1296
|
+
function initHostResizeObserver() {
|
|
1297
|
+
if (this[hostResizeObserverSymbol]) {
|
|
1298
|
+
return;
|
|
1299
|
+
}
|
|
1300
|
+
if (typeof ResizeObserver === "undefined") {
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
this[hostResizeObserverSymbol] = new ResizeObserver(() => {
|
|
1305
|
+
scheduleUpdateEditorSizes.call(this);
|
|
1306
|
+
});
|
|
1307
|
+
this[hostResizeObserverSymbol].observe(this);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
/**
|
|
1311
|
+
* @private
|
|
1312
|
+
* @return {void}
|
|
1313
|
+
*/
|
|
1314
|
+
function scheduleUpdateEditorSizes() {
|
|
1315
|
+
const win = getWindow();
|
|
1316
|
+
if (!win) {
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
if (this[editorSizeRafSymbol]) {
|
|
1320
|
+
win.cancelAnimationFrame(this[editorSizeRafSymbol]);
|
|
1321
|
+
}
|
|
1322
|
+
this[editorSizeRafSymbol] = win.requestAnimationFrame(() => {
|
|
1323
|
+
this[editorSizeRafSymbol] = null;
|
|
1324
|
+
updateEditorSizes.call(this);
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
/**
|
|
1329
|
+
* @private
|
|
1330
|
+
* @return {void}
|
|
1331
|
+
*/
|
|
1332
|
+
function updateEditorSizes() {
|
|
1333
|
+
if (!this[tabsSymbol]) {
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
const tabsElement = this[tabsSymbol];
|
|
1338
|
+
const container = this[tabsContainerSymbol] || tabsElement;
|
|
1339
|
+
const containerRect = container.getBoundingClientRect();
|
|
1340
|
+
if (containerRect.height <= 0) {
|
|
1341
|
+
return;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
const nav = tabsElement.shadowRoot?.querySelector?.(
|
|
1345
|
+
"[data-monster-role=nav]",
|
|
1346
|
+
);
|
|
1347
|
+
const navHeight = nav?.getBoundingClientRect?.().height || 0;
|
|
1348
|
+
const available = Math.max(0, containerRect.height - navHeight);
|
|
1349
|
+
|
|
1350
|
+
for (const tabData of this[tabsByReferenceSymbol].values()) {
|
|
1351
|
+
const editor = tabData?.editor;
|
|
1352
|
+
const tabElement = tabData?.tabElement;
|
|
1353
|
+
if (tabElement instanceof HTMLElement) {
|
|
1354
|
+
tabElement.style.height = `${available}px`;
|
|
1355
|
+
tabElement.style.minHeight = `${available}px`;
|
|
1356
|
+
}
|
|
1357
|
+
if (!(editor instanceof HTMLElement)) {
|
|
1358
|
+
continue;
|
|
1359
|
+
}
|
|
1360
|
+
if (!isCanvasElement(editor)) {
|
|
1361
|
+
editor.style.height = "100%";
|
|
1362
|
+
editor.style.minHeight = "0";
|
|
1363
|
+
}
|
|
1364
|
+
if (isCanvasElement(editor)) {
|
|
1365
|
+
editor.width = Math.max(1, Math.floor(containerRect.width));
|
|
1366
|
+
editor.height = Math.max(1, Math.floor(available));
|
|
1367
|
+
renderCanvasFromValue(editor, editor.dataset.monsterValue || "");
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
/**
|
|
1373
|
+
* @private
|
|
1374
|
+
* @param {object} tabData
|
|
1375
|
+
* @return {Promise<void>}
|
|
1376
|
+
*/
|
|
1377
|
+
async function loadFileContent(tabData) {
|
|
1378
|
+
const datasource = this[fileDatasourceSymbol];
|
|
1379
|
+
if (!datasource?.read) {
|
|
1380
|
+
const content = tabData.entry?.raw?.content || "";
|
|
1381
|
+
const patched = applyPersistedDiff.call(this, content, tabData.entry);
|
|
1382
|
+
tabData.adapter.setValue(tabData.editor, patched.value);
|
|
1383
|
+
tabData.original = content;
|
|
1384
|
+
updateTabDirty.call(this, tabData, patched.dirty);
|
|
1385
|
+
return;
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
const params = buildParameters(tabData.entry);
|
|
1389
|
+
const readUrl = datasource.getOption?.("read.url");
|
|
1390
|
+
|
|
1391
|
+
if (!isString(readUrl) || readUrl.trim() === "") {
|
|
1392
|
+
const content = tabData.entry?.raw?.content || "";
|
|
1393
|
+
const patched = applyPersistedDiff.call(this, content, tabData.entry);
|
|
1394
|
+
tabData.adapter.setValue(tabData.editor, patched.value);
|
|
1395
|
+
tabData.original = content;
|
|
1396
|
+
updateTabDirty.call(this, tabData, patched.dirty);
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
datasource.setOption("read.parameters", params);
|
|
1401
|
+
|
|
1402
|
+
await datasource.read();
|
|
1403
|
+
|
|
1404
|
+
const data = datasource.data;
|
|
1405
|
+
const content = extractContent.call(this, data);
|
|
1406
|
+
const patched = applyPersistedDiff.call(this, content, tabData.entry);
|
|
1407
|
+
tabData.adapter.setValue(tabData.editor, patched.value);
|
|
1408
|
+
tabData.original = content;
|
|
1409
|
+
updateTabDirty.call(this, tabData, patched.dirty);
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
/**
|
|
1413
|
+
* @private
|
|
1414
|
+
* @param {object} data
|
|
1415
|
+
* @return {string}
|
|
1416
|
+
*/
|
|
1417
|
+
function extractContent(data) {
|
|
1418
|
+
if (isString(data)) {
|
|
1419
|
+
return data;
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
const path = this.getOption("mapping.contentPath");
|
|
1423
|
+
if (isString(path) && path.trim() !== "") {
|
|
1424
|
+
try {
|
|
1425
|
+
const value = new Pathfinder(data).getVia(path);
|
|
1426
|
+
if (isString(value)) {
|
|
1427
|
+
return value;
|
|
1428
|
+
}
|
|
1429
|
+
} catch (e) {
|
|
1430
|
+
addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
if (isString(data?.content)) {
|
|
1435
|
+
return data.content;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
if (isString(data?.file?.content)) {
|
|
1439
|
+
return data.file.content;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
return "";
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
/**
|
|
1446
|
+
* @private
|
|
1447
|
+
* @param {object} tabData
|
|
1448
|
+
* @return {void}
|
|
1449
|
+
*/
|
|
1450
|
+
function handleEditorChange(tabData) {
|
|
1451
|
+
const value = tabData.adapter.getValue(tabData.editor);
|
|
1452
|
+
const isDirty = value !== tabData.original;
|
|
1453
|
+
updateTabDirty.call(this, tabData, isDirty);
|
|
1454
|
+
schedulePersistDiff.call(this, tabData);
|
|
1455
|
+
updateSaveButtonState.call(this);
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
/**
|
|
1459
|
+
* @private
|
|
1460
|
+
* @param {object} tabData
|
|
1461
|
+
* @param {boolean} dirty
|
|
1462
|
+
* @return {void}
|
|
1463
|
+
*/
|
|
1464
|
+
function updateTabDirty(tabData, dirty) {
|
|
1465
|
+
const marker = this.getOption("tabs.dirtyMarker", "*");
|
|
1466
|
+
const label = dirty ? `${tabData.label}${marker}` : tabData.label;
|
|
1467
|
+
const current = tabData.tabElement?.getAttribute?.(ATTRIBUTE_BUTTON_LABEL);
|
|
1468
|
+
if (tabData.dirty === dirty && current === label) {
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
tabData.dirty = dirty;
|
|
1472
|
+
if (current !== label) {
|
|
1473
|
+
tabData.tabElement.setAttribute(ATTRIBUTE_BUTTON_LABEL, label);
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
/**
|
|
1478
|
+
* @private
|
|
1479
|
+
* @return {void}
|
|
1480
|
+
*/
|
|
1481
|
+
function updateEmptyState() {
|
|
1482
|
+
if (!this[emptyStateSymbol]) {
|
|
1483
|
+
return;
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
const hasTabs = this[tabsByReferenceSymbol].size > 0;
|
|
1487
|
+
if (hasTabs) {
|
|
1488
|
+
this[emptyStateSymbol].classList.add("hidden");
|
|
1489
|
+
} else {
|
|
1490
|
+
this[emptyStateSymbol].classList.remove("hidden");
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
/**
|
|
1495
|
+
* @private
|
|
1496
|
+
* @return {void}
|
|
1497
|
+
*/
|
|
1498
|
+
function updateSaveButtonState() {
|
|
1499
|
+
if (!this[saveButtonSymbol]) {
|
|
1500
|
+
return;
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
const active = getActiveTabData.call(this);
|
|
1504
|
+
const enabled = active?.dirty === true;
|
|
1505
|
+
this[saveButtonSymbol].setOption("disabled", !enabled);
|
|
1506
|
+
|
|
1507
|
+
if (this[saveAllButtonSymbol]) {
|
|
1508
|
+
const anyDirty = Array.from(this[tabsByReferenceSymbol].values()).some(
|
|
1509
|
+
(tab) => tab.dirty === true,
|
|
1510
|
+
);
|
|
1511
|
+
this[saveAllButtonSymbol].setOption("disabled", !anyDirty);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
/**
|
|
1516
|
+
* @private
|
|
1517
|
+
* @return {object|null}
|
|
1518
|
+
*/
|
|
1519
|
+
function getActiveTabData() {
|
|
1520
|
+
if (!this[tabsSymbol]) {
|
|
1521
|
+
return null;
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
const reference = this[tabsSymbol].getActiveTab?.();
|
|
1525
|
+
if (!reference) {
|
|
1526
|
+
return null;
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
return this[tabsByReferenceSymbol].get(reference) || null;
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
/**
|
|
1533
|
+
* @private
|
|
1534
|
+
* @return {void}
|
|
1535
|
+
*/
|
|
1536
|
+
function saveActiveTab() {
|
|
1537
|
+
const tabData = getActiveTabData.call(this);
|
|
1538
|
+
if (!tabData || tabData.dirty !== true) {
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
if (this[saveButtonSymbol]) {
|
|
1543
|
+
this[saveButtonSymbol].setState("activity");
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
saveTab
|
|
1547
|
+
.call(this, tabData)
|
|
1548
|
+
.then(() => {
|
|
1549
|
+
if (this[saveButtonSymbol]) {
|
|
1550
|
+
this[saveButtonSymbol].setState("successful", 1200);
|
|
1551
|
+
}
|
|
1552
|
+
})
|
|
1553
|
+
.catch((err) => {
|
|
1554
|
+
if (this[saveButtonSymbol]) {
|
|
1555
|
+
this[saveButtonSymbol].setState("failed", 1600);
|
|
1556
|
+
}
|
|
1557
|
+
addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, err.message);
|
|
1558
|
+
})
|
|
1559
|
+
.finally(() => {
|
|
1560
|
+
updateSaveButtonState.call(this);
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
/**
|
|
1565
|
+
* @private
|
|
1566
|
+
* @param {object} tabData
|
|
1567
|
+
* @return {Promise<void>}
|
|
1568
|
+
*/
|
|
1569
|
+
async function saveTab(tabData) {
|
|
1570
|
+
const datasource = this[fileDatasourceSymbol];
|
|
1571
|
+
if (!datasource?.write) {
|
|
1572
|
+
throw new Error("file datasource not available");
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
const payload = buildWritePayload.call(this, tabData);
|
|
1576
|
+
const params = buildParameters(tabData.entry);
|
|
1577
|
+
datasource.data = payload;
|
|
1578
|
+
datasource.setOption("write.parameters", params);
|
|
1579
|
+
|
|
1580
|
+
await datasource.write();
|
|
1581
|
+
|
|
1582
|
+
tabData.original = tabData.adapter.getValue(tabData.editor);
|
|
1583
|
+
updateTabDirty.call(this, tabData, false);
|
|
1584
|
+
clearPersistedDiff.call(this, tabData.entry);
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
/**
|
|
1588
|
+
* @private
|
|
1589
|
+
* @return {void}
|
|
1590
|
+
*/
|
|
1591
|
+
function saveAllTabs() {
|
|
1592
|
+
if (!this[saveAllButtonSymbol]) {
|
|
1593
|
+
return;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
const dirtyTabs = Array.from(this[tabsByReferenceSymbol].values()).filter(
|
|
1597
|
+
(tab) => tab.dirty === true,
|
|
1598
|
+
);
|
|
1599
|
+
|
|
1600
|
+
if (dirtyTabs.length === 0) {
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
this[saveAllButtonSymbol].setState("activity");
|
|
1605
|
+
|
|
1606
|
+
Promise.all(dirtyTabs.map((tab) => saveTab.call(this, tab)))
|
|
1607
|
+
.then(() => {
|
|
1608
|
+
this[saveAllButtonSymbol].setState("successful", 1200);
|
|
1609
|
+
})
|
|
1610
|
+
.catch((err) => {
|
|
1611
|
+
this[saveAllButtonSymbol].setState("failed", 1600);
|
|
1612
|
+
addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, err.message);
|
|
1613
|
+
})
|
|
1614
|
+
.finally(() => {
|
|
1615
|
+
updateSaveButtonState.call(this);
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
/**
|
|
1620
|
+
* @private
|
|
1621
|
+
* @param {object} tabData
|
|
1622
|
+
* @return {object}
|
|
1623
|
+
*/
|
|
1624
|
+
function buildWritePayload(tabData) {
|
|
1625
|
+
const custom = this.getOption("actions.buildPayload");
|
|
1626
|
+
if (isFunction(custom)) {
|
|
1627
|
+
return custom(tabData);
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
const entry = tabData.entry || {};
|
|
1631
|
+
const content = tabData.adapter.getValue(tabData.editor);
|
|
1632
|
+
return {
|
|
1633
|
+
id: entry.id,
|
|
1634
|
+
path: entry.path,
|
|
1635
|
+
name: entry.label,
|
|
1636
|
+
type: entry.type,
|
|
1637
|
+
content: content,
|
|
1638
|
+
};
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
/**
|
|
1642
|
+
* @private
|
|
1643
|
+
* @param {object} entry
|
|
1644
|
+
* @return {object}
|
|
1645
|
+
*/
|
|
1646
|
+
function buildParameters(entry) {
|
|
1647
|
+
const payload = clone(entry);
|
|
1648
|
+
payload.id = entry.id;
|
|
1649
|
+
payload.path = entry.path;
|
|
1650
|
+
payload.name = entry.label;
|
|
1651
|
+
payload.type = entry.type;
|
|
1652
|
+
return payload;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
/**
|
|
1656
|
+
* @private
|
|
1657
|
+
* @return {object}
|
|
1658
|
+
*/
|
|
1659
|
+
function getEditorAdapter() {
|
|
1660
|
+
const custom = this.getOption("editor.adapter");
|
|
1661
|
+
const base = {
|
|
1662
|
+
getValue: (element) => {
|
|
1663
|
+
if (isImageEditorElement(element)) {
|
|
1664
|
+
if (typeof element.getImageDataUrl === "function") {
|
|
1665
|
+
return element.getImageDataUrl(
|
|
1666
|
+
element.dataset.monsterOutputType || undefined,
|
|
1667
|
+
);
|
|
1668
|
+
}
|
|
1669
|
+
return element.dataset.monsterValue || "";
|
|
1670
|
+
}
|
|
1671
|
+
if (isCanvasElement(element)) {
|
|
1672
|
+
return element.dataset.monsterValue || "";
|
|
1673
|
+
}
|
|
1674
|
+
if (element && "value" in element) {
|
|
1675
|
+
return element.value ?? "";
|
|
1676
|
+
}
|
|
1677
|
+
return element?.textContent ?? "";
|
|
1678
|
+
},
|
|
1679
|
+
setValue: (element, value) => {
|
|
1680
|
+
const next = value ?? "";
|
|
1681
|
+
if (isImageEditorElement(element)) {
|
|
1682
|
+
element.dataset.monsterValue = next;
|
|
1683
|
+
if (typeof element.setImage === "function") {
|
|
1684
|
+
element.setImage(next, {
|
|
1685
|
+
contentType: element.dataset.monsterContentType,
|
|
1686
|
+
storeOriginal: true,
|
|
1687
|
+
});
|
|
1688
|
+
}
|
|
1689
|
+
return;
|
|
1690
|
+
}
|
|
1691
|
+
if (isCanvasElement(element)) {
|
|
1692
|
+
renderCanvasFromValue(element, next);
|
|
1693
|
+
return;
|
|
1694
|
+
}
|
|
1695
|
+
if (element && "value" in element) {
|
|
1696
|
+
element.value = next;
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1699
|
+
if (element) {
|
|
1700
|
+
element.textContent = next;
|
|
1701
|
+
}
|
|
1702
|
+
},
|
|
1703
|
+
bindChange: (element, callback) => {
|
|
1704
|
+
if (!element || typeof callback !== "function") {
|
|
1705
|
+
return () => {};
|
|
1706
|
+
}
|
|
1707
|
+
if (isCanvasElement(element)) {
|
|
1708
|
+
return () => {};
|
|
1709
|
+
}
|
|
1710
|
+
if (isImageEditorElement(element)) {
|
|
1711
|
+
const handler = () => callback();
|
|
1712
|
+
element.addEventListener("monster-image-editor-changed", handler);
|
|
1713
|
+
return () => {
|
|
1714
|
+
element.removeEventListener("monster-image-editor-changed", handler);
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
if (isCanvasElement(element)) {
|
|
1719
|
+
return () => {};
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
const handler = () => callback();
|
|
1723
|
+
element.addEventListener("input", handler);
|
|
1724
|
+
element.addEventListener("change", handler);
|
|
1725
|
+
return () => {
|
|
1726
|
+
element.removeEventListener("input", handler);
|
|
1727
|
+
element.removeEventListener("change", handler);
|
|
1728
|
+
};
|
|
1729
|
+
},
|
|
1730
|
+
setReadOnly: (element, readOnly) => {
|
|
1731
|
+
if (!element) {
|
|
1732
|
+
return;
|
|
1733
|
+
}
|
|
1734
|
+
if (isImageEditorElement(element)) {
|
|
1735
|
+
if (readOnly === true) {
|
|
1736
|
+
element.setAttribute("data-monster-readonly", "");
|
|
1737
|
+
} else {
|
|
1738
|
+
element.removeAttribute("data-monster-readonly");
|
|
1739
|
+
}
|
|
1740
|
+
return;
|
|
1741
|
+
}
|
|
1742
|
+
if (isCanvasElement(element)) {
|
|
1743
|
+
return;
|
|
1744
|
+
}
|
|
1745
|
+
if ("readOnly" in element) {
|
|
1746
|
+
element.readOnly = readOnly === true;
|
|
1747
|
+
return;
|
|
1748
|
+
}
|
|
1749
|
+
if (readOnly === true) {
|
|
1750
|
+
element.setAttribute("data-monster-readonly", "");
|
|
1751
|
+
} else {
|
|
1752
|
+
element.removeAttribute("data-monster-readonly");
|
|
1753
|
+
}
|
|
1754
|
+
},
|
|
1755
|
+
};
|
|
1756
|
+
|
|
1757
|
+
if (!isObject(custom)) {
|
|
1758
|
+
return base;
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
const adapter = Object.assign({}, base);
|
|
1762
|
+
if (isFunction(custom.getValue)) {
|
|
1763
|
+
adapter.getValue = custom.getValue;
|
|
1764
|
+
}
|
|
1765
|
+
if (isFunction(custom.setValue)) {
|
|
1766
|
+
adapter.setValue = custom.setValue;
|
|
1767
|
+
}
|
|
1768
|
+
if (isFunction(custom.bindChange)) {
|
|
1769
|
+
adapter.bindChange = custom.bindChange;
|
|
1770
|
+
}
|
|
1771
|
+
if (isFunction(custom.setReadOnly)) {
|
|
1772
|
+
adapter.setReadOnly = custom.setReadOnly;
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
return adapter;
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
/**
|
|
1779
|
+
* @private
|
|
1780
|
+
* @param {object} entry
|
|
1781
|
+
* @return {HTMLElement}
|
|
1782
|
+
*/
|
|
1783
|
+
function createEditorElement(entry) {
|
|
1784
|
+
const tag = resolveEditorTag.call(this, entry);
|
|
1785
|
+
const editor = getDocument().createElement(tag);
|
|
1786
|
+
const mimeType = getMimeTypeForEntry(entry);
|
|
1787
|
+
if (isImageEditorElement(editor) && isString(mimeType)) {
|
|
1788
|
+
editor.dataset.monsterContentType = mimeType;
|
|
1789
|
+
editor.dataset.monsterOutputType = mimeType;
|
|
1790
|
+
if (typeof editor.setOption === "function") {
|
|
1791
|
+
editor.setOption("source.contentType", mimeType);
|
|
1792
|
+
editor.setOption("output.type", mimeType);
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
return editor;
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
/**
|
|
1799
|
+
* @private
|
|
1800
|
+
* @param {object} entry
|
|
1801
|
+
* @return {boolean}
|
|
1802
|
+
*/
|
|
1803
|
+
function resolveEditorTag(entry) {
|
|
1804
|
+
const mimeType = getMimeTypeForEntry(entry);
|
|
1805
|
+
const map = this.getOption("editor.byMimeType", {});
|
|
1806
|
+
const tag = map?.[mimeType];
|
|
1807
|
+
if (isString(tag) && tag.trim() !== "") {
|
|
1808
|
+
return tag.trim();
|
|
1809
|
+
}
|
|
1810
|
+
const fallback = this.getOption("editor.defaultTag", "textarea");
|
|
1811
|
+
if (isString(fallback) && fallback.trim() !== "") {
|
|
1812
|
+
return fallback.trim();
|
|
1813
|
+
}
|
|
1814
|
+
return "textarea";
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
/**
|
|
1818
|
+
* @private
|
|
1819
|
+
* @param {HTMLElement} element
|
|
1820
|
+
* @return {boolean}
|
|
1821
|
+
*/
|
|
1822
|
+
function isCanvasElement(element) {
|
|
1823
|
+
return element instanceof HTMLCanvasElement;
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
/**
|
|
1827
|
+
* @private
|
|
1828
|
+
* @param {HTMLElement} element
|
|
1829
|
+
* @return {boolean}
|
|
1830
|
+
*/
|
|
1831
|
+
function isImageEditorElement(element) {
|
|
1832
|
+
return (
|
|
1833
|
+
element instanceof HTMLElement &&
|
|
1834
|
+
element.tagName.toLowerCase() === "monster-image-editor"
|
|
1835
|
+
);
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
/**
|
|
1839
|
+
* @private
|
|
1840
|
+
* @param {HTMLCanvasElement} canvas
|
|
1841
|
+
* @param {string} value
|
|
1842
|
+
* @return {void}
|
|
1843
|
+
*/
|
|
1844
|
+
function renderCanvasFromValue(canvas, value) {
|
|
1845
|
+
if (!isCanvasElement(canvas)) {
|
|
1846
|
+
return;
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
const next = value ?? "";
|
|
1850
|
+
canvas.dataset.monsterValue = next;
|
|
1851
|
+
|
|
1852
|
+
const ctx = canvas.getContext("2d");
|
|
1853
|
+
if (!ctx) {
|
|
1854
|
+
return;
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
const source = normalizeImageSource(next);
|
|
1858
|
+
if (!source) {
|
|
1859
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1860
|
+
return;
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
const win = getWindow();
|
|
1864
|
+
if (!win) {
|
|
1865
|
+
return;
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
const img = new win.Image();
|
|
1869
|
+
img.onload = () => {
|
|
1870
|
+
const targetWidth = canvas.width || img.naturalWidth;
|
|
1871
|
+
const targetHeight = canvas.height || img.naturalHeight;
|
|
1872
|
+
|
|
1873
|
+
if (canvas.width !== targetWidth) {
|
|
1874
|
+
canvas.width = targetWidth;
|
|
1875
|
+
}
|
|
1876
|
+
if (canvas.height !== targetHeight) {
|
|
1877
|
+
canvas.height = targetHeight;
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
const scale = Math.min(
|
|
1881
|
+
canvas.width / img.naturalWidth,
|
|
1882
|
+
canvas.height / img.naturalHeight,
|
|
1883
|
+
);
|
|
1884
|
+
const drawWidth = img.naturalWidth * scale;
|
|
1885
|
+
const drawHeight = img.naturalHeight * scale;
|
|
1886
|
+
const offsetX = (canvas.width - drawWidth) / 2;
|
|
1887
|
+
const offsetY = (canvas.height - drawHeight) / 2;
|
|
1888
|
+
|
|
1889
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1890
|
+
ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);
|
|
1891
|
+
};
|
|
1892
|
+
img.onerror = () => {
|
|
1893
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1894
|
+
};
|
|
1895
|
+
img.src = source;
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
/**
|
|
1899
|
+
* @private
|
|
1900
|
+
* @param {string} value
|
|
1901
|
+
* @return {string}
|
|
1902
|
+
*/
|
|
1903
|
+
function normalizeImageSource(value) {
|
|
1904
|
+
if (!isString(value)) {
|
|
1905
|
+
return "";
|
|
1906
|
+
}
|
|
1907
|
+
const trimmed = value.trim();
|
|
1908
|
+
if (trimmed === "") {
|
|
1909
|
+
return "";
|
|
1910
|
+
}
|
|
1911
|
+
if (trimmed.startsWith("data:image/")) {
|
|
1912
|
+
return trimmed;
|
|
1913
|
+
}
|
|
1914
|
+
if (trimmed.startsWith("<svg")) {
|
|
1915
|
+
return `data:image/svg+xml;utf8,${encodeURIComponent(trimmed)}`;
|
|
1916
|
+
}
|
|
1917
|
+
return trimmed;
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
/**
|
|
1921
|
+
* @private
|
|
1922
|
+
* @param {object} entry
|
|
1923
|
+
* @return {string}
|
|
1924
|
+
*/
|
|
1925
|
+
function getMimeTypeForEntry(entry) {
|
|
1926
|
+
if (isString(entry?.mimeType) && entry.mimeType.trim() !== "") {
|
|
1927
|
+
return entry.mimeType.trim().toLowerCase();
|
|
1928
|
+
}
|
|
1929
|
+
const ext = (entry?.type || "").toString().toLowerCase();
|
|
1930
|
+
switch (ext) {
|
|
1931
|
+
case "png":
|
|
1932
|
+
return "image/png";
|
|
1933
|
+
case "jpg":
|
|
1934
|
+
case "jpeg":
|
|
1935
|
+
return "image/jpeg";
|
|
1936
|
+
case "gif":
|
|
1937
|
+
return "image/gif";
|
|
1938
|
+
case "svg":
|
|
1939
|
+
return "image/svg+xml";
|
|
1940
|
+
case "webp":
|
|
1941
|
+
return "image/webp";
|
|
1942
|
+
case "bmp":
|
|
1943
|
+
return "image/bmp";
|
|
1944
|
+
case "ico":
|
|
1945
|
+
return "image/x-icon";
|
|
1946
|
+
case "md":
|
|
1947
|
+
return "text/markdown";
|
|
1948
|
+
case "css":
|
|
1949
|
+
return "text/css";
|
|
1950
|
+
case "html":
|
|
1951
|
+
case "htm":
|
|
1952
|
+
return "text/html";
|
|
1953
|
+
case "js":
|
|
1954
|
+
case "mjs":
|
|
1955
|
+
return "text/javascript";
|
|
1956
|
+
case "json":
|
|
1957
|
+
return "application/json";
|
|
1958
|
+
case "txt":
|
|
1959
|
+
return "text/plain";
|
|
1960
|
+
default:
|
|
1961
|
+
return "text/plain";
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
/**
|
|
1966
|
+
* @private
|
|
1967
|
+
* @param {object} tabData
|
|
1968
|
+
* @return {void}
|
|
1969
|
+
*/
|
|
1970
|
+
function schedulePersistDiff(tabData) {
|
|
1971
|
+
const debounceMs = this.getOption("persistence.debounceMs", 300);
|
|
1972
|
+
if (!(tabData.persistDebounce instanceof DeadMansSwitch)) {
|
|
1973
|
+
tabData.persistDebounce = new DeadMansSwitch(debounceMs, () => {
|
|
1974
|
+
persistDiff.call(this, tabData);
|
|
1975
|
+
});
|
|
1976
|
+
return;
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
try {
|
|
1980
|
+
tabData.persistDebounce.touch();
|
|
1981
|
+
} catch (_e) {
|
|
1982
|
+
tabData.persistDebounce = new DeadMansSwitch(debounceMs, () => {
|
|
1983
|
+
persistDiff.call(this, tabData);
|
|
1984
|
+
});
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
/**
|
|
1989
|
+
* @private
|
|
1990
|
+
* @return {void}
|
|
1991
|
+
*/
|
|
1992
|
+
function syncDatasources() {
|
|
1993
|
+
if (this[listDatasourceOwnedSymbol] === true && this[listDatasourceSymbol]) {
|
|
1994
|
+
const restConfig = this.getOption("datasource.list.rest", {});
|
|
1995
|
+
if (isObject(restConfig)) {
|
|
1996
|
+
logDebug.call(this, "sync list datasource", restConfig?.read?.url);
|
|
1997
|
+
this[listDatasourceSymbol].setOptions(restConfig);
|
|
1998
|
+
this[listDatasourceSymbol].setOption("features.autoInit", true);
|
|
1999
|
+
this[listDatasourceSymbol].setOption("autoInit.oneTime", true);
|
|
2000
|
+
requestList.call(this, this.getOption("lazy.root", null));
|
|
2001
|
+
}
|
|
2002
|
+
} else if (!this[listDatasourceSymbol]) {
|
|
2003
|
+
initListDatasource.call(this);
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
if (this[fileDatasourceOwnedSymbol] === true && this[fileDatasourceSymbol]) {
|
|
2007
|
+
const restConfig = this.getOption("datasource.file.rest", {});
|
|
2008
|
+
if (isObject(restConfig)) {
|
|
2009
|
+
this[fileDatasourceSymbol].setOptions(restConfig);
|
|
2010
|
+
}
|
|
2011
|
+
} else if (!this[fileDatasourceSymbol]) {
|
|
2012
|
+
initFileDatasource.call(this);
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
/**
|
|
2017
|
+
* @private
|
|
2018
|
+
* @param {string} message
|
|
2019
|
+
* @param {*=} data
|
|
2020
|
+
* @return {void}
|
|
2021
|
+
*/
|
|
2022
|
+
function logDebug(message, data) {
|
|
2023
|
+
if (this.getOption("debug") !== true) {
|
|
2024
|
+
return;
|
|
2025
|
+
}
|
|
2026
|
+
if (data !== undefined) {
|
|
2027
|
+
console.log(`[monster-file-manager] ${message}`, data);
|
|
2028
|
+
} else {
|
|
2029
|
+
console.log(`[monster-file-manager] ${message}`);
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
/**
|
|
2034
|
+
* @private
|
|
2035
|
+
* @return {void}
|
|
2036
|
+
*/
|
|
2037
|
+
function refreshLabels() {
|
|
2038
|
+
if (this[titleSymbol]) {
|
|
2039
|
+
this[titleSymbol].textContent = this.getOption("labels.title");
|
|
2040
|
+
}
|
|
2041
|
+
if (this[saveButtonSymbol]) {
|
|
2042
|
+
this[saveButtonSymbol].setOption(
|
|
2043
|
+
"labels.button",
|
|
2044
|
+
this.getOption("labels.save"),
|
|
2045
|
+
);
|
|
2046
|
+
}
|
|
2047
|
+
if (this[saveAllButtonSymbol]) {
|
|
2048
|
+
this[saveAllButtonSymbol].setOption(
|
|
2049
|
+
"labels.button",
|
|
2050
|
+
this.getOption("labels.saveAll"),
|
|
2051
|
+
);
|
|
2052
|
+
}
|
|
2053
|
+
if (this[emptyStateSymbol]) {
|
|
2054
|
+
const emptyLabel = this.getOption("labels.empty");
|
|
2055
|
+
if (isFunction(this[emptyStateSymbol].setOption)) {
|
|
2056
|
+
this[emptyStateSymbol].setOption("content.visual", getEmptyStateIcon());
|
|
2057
|
+
this[emptyStateSymbol].setOption("content.content", emptyLabel);
|
|
2058
|
+
} else {
|
|
2059
|
+
this[emptyStateSymbol].textContent = emptyLabel;
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
/**
|
|
2065
|
+
* @private
|
|
2066
|
+
* @return {string}
|
|
2067
|
+
*/
|
|
2068
|
+
function getEmptyStateIcon() {
|
|
2069
|
+
return `<svg width="4rem" height="4rem" viewBox="0 -12 512.00032 512" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path d="m455.074219 172.613281 53.996093-53.996093c2.226563-2.222657 3.273438-5.367188 2.828126-8.480469-.441407-3.113281-2.328126-5.839844-5.085938-7.355469l-64.914062-35.644531c-4.839844-2.65625-10.917969-.886719-13.578126 3.953125-2.65625 4.84375-.890624 10.921875 3.953126 13.578125l53.234374 29.230469-46.339843 46.335937-166.667969-91.519531 46.335938-46.335938 46.839843 25.722656c4.839844 2.65625 10.921875.890626 13.578125-3.953124 2.660156-4.839844.890625-10.921876-3.953125-13.578126l-53.417969-29.335937c-3.898437-2.140625-8.742187-1.449219-11.882812 1.695313l-54 54-54-54c-3.144531-3.144532-7.988281-3.832032-11.882812-1.695313l-184.929688 101.546875c-2.757812 1.515625-4.644531 4.238281-5.085938 7.355469-.445312 3.113281.601563 6.257812 2.828126 8.480469l53.996093 53.996093-53.996093 53.992188c-2.226563 2.226562-3.273438 5.367187-2.828126 8.484375.441407 3.113281 2.328126 5.839844 5.085938 7.351562l55.882812 30.6875v102.570313c0 3.652343 1.988282 7.011719 5.1875 8.769531l184.929688 101.542969c1.5.824219 3.15625 1.234375 4.8125 1.234375s3.3125-.410156 4.8125-1.234375l184.929688-101.542969c3.199218-1.757812 5.1875-5.117188 5.1875-8.769531v-102.570313l55.882812-30.683594c2.757812-1.515624 4.644531-4.242187 5.085938-7.355468.445312-3.113282-.601563-6.257813-2.828126-8.480469zm-199.074219 90.132813-164.152344-90.136719 164.152344-90.140625 164.152344 90.140625zm-62.832031-240.367188 46.332031 46.335938-166.667969 91.519531-46.335937-46.335937zm-120.328125 162.609375 166.667968 91.519531-46.339843 46.339844-166.671875-91.519531zm358.089844 184.796875-164.929688 90.5625v-102.222656c0-5.523438-4.476562-10-10-10s-10 4.476562-10 10v102.222656l-164.929688-90.5625v-85.671875l109.046876 59.878907c1.511718.828124 3.167968 1.234374 4.808593 1.234374 2.589844 0 5.152344-1.007812 7.074219-2.929687l54-54 54 54c1.921875 1.925781 4.484375 2.929687 7.074219 2.929687 1.640625 0 3.296875-.40625 4.808593-1.234374l109.046876-59.878907zm-112.09375-46.9375-46.339844-46.34375 166.667968-91.515625 46.34375 46.335938zm0 0"></path><path d="m404.800781 68.175781c2.628907 0 5.199219-1.070312 7.070313-2.933593 1.859375-1.859376 2.929687-4.4375 2.929687-7.066407 0-2.632812-1.070312-5.210937-2.929687-7.070312-1.859375-1.863281-4.441406-2.929688-7.070313-2.929688-2.640625 0-5.210937 1.066407-7.070312 2.929688-1.871094 1.859375-2.929688 4.4375-2.929688 7.070312 0 2.628907 1.058594 5.207031 2.929688 7.066407 1.859375 1.863281 4.441406 2.933593 7.070312 2.933593zm0 0"></path><path d="m256 314.925781c-2.628906 0-5.210938 1.066407-7.070312 2.929688-1.859376 1.867187-2.929688 4.4375-2.929688 7.070312 0 2.636719 1.070312 5.207031 2.929688 7.078125 1.859374 1.859375 4.441406 2.921875 7.070312 2.921875s5.210938-1.0625 7.070312-2.921875c1.859376-1.871094 2.929688-4.441406 2.929688-7.078125 0-2.632812-1.070312-5.203125-2.929688-7.070312-1.859374-1.863281-4.441406-2.929688-7.070312-2.929688zm0 0"></path></svg>`;
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
/**
|
|
2073
|
+
* @private
|
|
2074
|
+
* @returns {object}
|
|
2075
|
+
*/
|
|
2076
|
+
function getTranslations() {
|
|
2077
|
+
const locale = getLocaleOfDocument();
|
|
2078
|
+
switch (locale.language) {
|
|
2079
|
+
case "de":
|
|
2080
|
+
return {
|
|
2081
|
+
title: "Dateien",
|
|
2082
|
+
save: "Speichern",
|
|
2083
|
+
saveAll: "Alle speichern",
|
|
2084
|
+
empty: "Datei aus dem Baum auswaehlen.",
|
|
2085
|
+
};
|
|
2086
|
+
case "fr":
|
|
2087
|
+
return {
|
|
2088
|
+
title: "Fichiers",
|
|
2089
|
+
save: "Enregistrer",
|
|
2090
|
+
saveAll: "Tout enregistrer",
|
|
2091
|
+
empty: "Selectionnez un fichier dans l'arborescence.",
|
|
2092
|
+
};
|
|
2093
|
+
case "sp":
|
|
2094
|
+
return {
|
|
2095
|
+
title: "Archivos",
|
|
2096
|
+
save: "Guardar",
|
|
2097
|
+
saveAll: "Guardar todo",
|
|
2098
|
+
empty: "Seleccione un archivo del arbol.",
|
|
2099
|
+
};
|
|
2100
|
+
case "it":
|
|
2101
|
+
return {
|
|
2102
|
+
title: "File",
|
|
2103
|
+
save: "Salva",
|
|
2104
|
+
saveAll: "Salva tutto",
|
|
2105
|
+
empty: "Seleziona un file dall'albero.",
|
|
2106
|
+
};
|
|
2107
|
+
case "pl":
|
|
2108
|
+
return {
|
|
2109
|
+
title: "Pliki",
|
|
2110
|
+
save: "Zapisz",
|
|
2111
|
+
saveAll: "Zapisz wszystko",
|
|
2112
|
+
empty: "Wybierz plik z drzewa.",
|
|
2113
|
+
};
|
|
2114
|
+
case "no":
|
|
2115
|
+
case "dk":
|
|
2116
|
+
return {
|
|
2117
|
+
title: "Filer",
|
|
2118
|
+
save: "Lagre",
|
|
2119
|
+
saveAll: "Lagre alle",
|
|
2120
|
+
empty: "Velg en fil fra treet.",
|
|
2121
|
+
};
|
|
2122
|
+
case "sw":
|
|
2123
|
+
return {
|
|
2124
|
+
title: "Filer",
|
|
2125
|
+
save: "Spara",
|
|
2126
|
+
saveAll: "Spara alla",
|
|
2127
|
+
empty: "Valj en fil fran tradet.",
|
|
2128
|
+
};
|
|
2129
|
+
default:
|
|
2130
|
+
case "en":
|
|
2131
|
+
return {
|
|
2132
|
+
title: "Files",
|
|
2133
|
+
save: "Save",
|
|
2134
|
+
saveAll: "Save All",
|
|
2135
|
+
empty: "Select a file from the tree to open it.",
|
|
2136
|
+
};
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
/**
|
|
2141
|
+
* @private
|
|
2142
|
+
* @param {object} entry
|
|
2143
|
+
* @return {string|null}
|
|
2144
|
+
*/
|
|
2145
|
+
function getStorageKey(entry) {
|
|
2146
|
+
const prefix = this.getOption("persistence.keyPrefix", "");
|
|
2147
|
+
const value = entry?.path || entry?.id;
|
|
2148
|
+
if (!isString(prefix) || prefix.trim() === "") {
|
|
2149
|
+
return null;
|
|
2150
|
+
}
|
|
2151
|
+
if (!isString(value) || value.trim() === "") {
|
|
2152
|
+
return null;
|
|
2153
|
+
}
|
|
2154
|
+
return `${prefix}:${value}`;
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
/**
|
|
2158
|
+
* @private
|
|
2159
|
+
* @return {Storage|null}
|
|
2160
|
+
*/
|
|
2161
|
+
function getStorage() {
|
|
2162
|
+
try {
|
|
2163
|
+
return getWindow()?.localStorage ?? null;
|
|
2164
|
+
} catch (_e) {
|
|
2165
|
+
return null;
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
/**
|
|
2170
|
+
* @private
|
|
2171
|
+
* @param {string} text
|
|
2172
|
+
* @return {number}
|
|
2173
|
+
*/
|
|
2174
|
+
function hashText(text) {
|
|
2175
|
+
let hash = 0;
|
|
2176
|
+
for (let i = 0; i < text.length; i++) {
|
|
2177
|
+
hash = (hash + text.charCodeAt(i) * (i + 1)) % 2147483647;
|
|
2178
|
+
}
|
|
2179
|
+
return hash;
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
/**
|
|
2183
|
+
* @private
|
|
2184
|
+
* @param {string} original
|
|
2185
|
+
* @param {object} entry
|
|
2186
|
+
* @return {{value: string, dirty: boolean}}
|
|
2187
|
+
*/
|
|
2188
|
+
function applyPersistedDiff(original, entry) {
|
|
2189
|
+
const enabled = this.getOption("persistence.enabled", true);
|
|
2190
|
+
if (enabled !== true) {
|
|
2191
|
+
return { value: original, dirty: false };
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2194
|
+
const storage = getStorage.call(this);
|
|
2195
|
+
if (!storage) {
|
|
2196
|
+
return { value: original, dirty: false };
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
const key = getStorageKey.call(this, entry);
|
|
2200
|
+
if (!key) {
|
|
2201
|
+
return { value: original, dirty: false };
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
const raw = storage.getItem(key);
|
|
2205
|
+
if (!raw) {
|
|
2206
|
+
return { value: original, dirty: false };
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
let payload;
|
|
2210
|
+
try {
|
|
2211
|
+
payload = JSON.parse(raw);
|
|
2212
|
+
} catch (_e) {
|
|
2213
|
+
storage.removeItem(key);
|
|
2214
|
+
return { value: original, dirty: false };
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
if (!payload || payload.baseHash !== hashText(original)) {
|
|
2218
|
+
storage.removeItem(key);
|
|
2219
|
+
return { value: original, dirty: false };
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2222
|
+
const updated = applyDiffToLines(
|
|
2223
|
+
original.split("\n"),
|
|
2224
|
+
payload.diff || [],
|
|
2225
|
+
).join("\n");
|
|
2226
|
+
return { value: updated, dirty: updated !== original };
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
/**
|
|
2230
|
+
* @private
|
|
2231
|
+
* @param {string[]} lines
|
|
2232
|
+
* @param {Array} diffOps
|
|
2233
|
+
* @return {string[]}
|
|
2234
|
+
*/
|
|
2235
|
+
function applyDiffToLines(lines, diffOps) {
|
|
2236
|
+
const out = lines.slice();
|
|
2237
|
+
const updates = [];
|
|
2238
|
+
const deletes = [];
|
|
2239
|
+
const adds = [];
|
|
2240
|
+
|
|
2241
|
+
for (const change of diffOps) {
|
|
2242
|
+
const index = change?.path?.[0];
|
|
2243
|
+
if (!Number.isInteger(index)) {
|
|
2244
|
+
continue;
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
switch (change?.operator) {
|
|
2248
|
+
case "update":
|
|
2249
|
+
updates.push({
|
|
2250
|
+
index,
|
|
2251
|
+
value: change?.second?.value ?? "",
|
|
2252
|
+
});
|
|
2253
|
+
break;
|
|
2254
|
+
case "delete":
|
|
2255
|
+
deletes.push(index);
|
|
2256
|
+
break;
|
|
2257
|
+
case "add":
|
|
2258
|
+
adds.push({
|
|
2259
|
+
index,
|
|
2260
|
+
value: change?.second?.value ?? "",
|
|
2261
|
+
});
|
|
2262
|
+
break;
|
|
2263
|
+
default:
|
|
2264
|
+
break;
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
updates
|
|
2269
|
+
.sort((a, b) => a.index - b.index)
|
|
2270
|
+
.forEach(({ index, value }) => {
|
|
2271
|
+
out[index] = value;
|
|
2272
|
+
});
|
|
2273
|
+
|
|
2274
|
+
deletes
|
|
2275
|
+
.sort((a, b) => b - a)
|
|
2276
|
+
.forEach((index) => {
|
|
2277
|
+
if (index >= 0 && index < out.length) {
|
|
2278
|
+
out.splice(index, 1);
|
|
2279
|
+
}
|
|
2280
|
+
});
|
|
2281
|
+
|
|
2282
|
+
adds
|
|
2283
|
+
.sort((a, b) => a.index - b.index)
|
|
2284
|
+
.forEach(({ index, value }) => {
|
|
2285
|
+
const target = Math.max(0, Math.min(index, out.length));
|
|
2286
|
+
out.splice(target, 0, value);
|
|
2287
|
+
});
|
|
2288
|
+
|
|
2289
|
+
return out;
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
/**
|
|
2293
|
+
* @private
|
|
2294
|
+
* @param {object} tabData
|
|
2295
|
+
* @return {void}
|
|
2296
|
+
*/
|
|
2297
|
+
function persistDiff(tabData) {
|
|
2298
|
+
const enabled = this.getOption("persistence.enabled", true);
|
|
2299
|
+
if (enabled !== true) {
|
|
2300
|
+
return;
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
const storage = getStorage.call(this);
|
|
2304
|
+
if (!storage) {
|
|
2305
|
+
return;
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
const key = getStorageKey.call(this, tabData.entry);
|
|
2309
|
+
if (!key) {
|
|
2310
|
+
return;
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
const current = tabData.adapter.getValue(tabData.editor);
|
|
2314
|
+
const original = tabData.original;
|
|
2315
|
+
if (current === original) {
|
|
2316
|
+
storage.removeItem(key);
|
|
2317
|
+
return;
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
const diffResult = diff(original.split("\n"), current.split("\n"));
|
|
2321
|
+
const payload = {
|
|
2322
|
+
v: 1,
|
|
2323
|
+
baseHash: hashText(original),
|
|
2324
|
+
diff: diffResult,
|
|
2325
|
+
};
|
|
2326
|
+
|
|
2327
|
+
try {
|
|
2328
|
+
storage.setItem(key, JSON.stringify(payload));
|
|
2329
|
+
} catch (_e) {
|
|
2330
|
+
// ignore storage errors (quota, private mode)
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2334
|
+
/**
|
|
2335
|
+
* @private
|
|
2336
|
+
* @param {object} entry
|
|
2337
|
+
* @return {void}
|
|
2338
|
+
*/
|
|
2339
|
+
function clearPersistedDiff(entry) {
|
|
2340
|
+
const storage = getStorage.call(this);
|
|
2341
|
+
if (!storage) {
|
|
2342
|
+
return;
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
const key = getStorageKey.call(this, entry);
|
|
2346
|
+
if (key) {
|
|
2347
|
+
storage.removeItem(key);
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
/**
|
|
2352
|
+
* @private
|
|
2353
|
+
* @return {string}
|
|
2354
|
+
*/
|
|
2355
|
+
function getTemplate() {
|
|
2356
|
+
// language=HTML
|
|
2357
|
+
return `
|
|
2358
|
+
<div data-monster-role="control" part="control">
|
|
2359
|
+
<div data-monster-role="toolbar" part="toolbar">
|
|
2360
|
+
<div data-monster-role="title" part="title" data-monster-replace="path:labels.title"></div>
|
|
2361
|
+
<div data-monster-role="actions" part="actions" class="monster-button-bar">
|
|
2362
|
+
<monster-message-state-button
|
|
2363
|
+
data-monster-role="save-button"
|
|
2364
|
+
data-monster-replace="path:labels.save"></monster-message-state-button>
|
|
2365
|
+
<monster-message-state-button
|
|
2366
|
+
data-monster-role="save-all-button"
|
|
2367
|
+
data-monster-replace="path:labels.saveAll"></monster-message-state-button>
|
|
2368
|
+
</div>
|
|
2369
|
+
</div>
|
|
2370
|
+
<monster-split-panel data-monster-role="split-panel">
|
|
2371
|
+
<div slot="start" data-monster-role="nav-panel" part="nav-panel">
|
|
2372
|
+
<monster-panel data-monster-role="tree-panel" part="tree-panel">
|
|
2373
|
+
<monster-tree-menu data-monster-role="tree">
|
|
2374
|
+
${getIconSprite()}
|
|
2375
|
+
</monster-tree-menu>
|
|
2376
|
+
</monster-panel>
|
|
2377
|
+
</div>
|
|
2378
|
+
<div slot="end" data-monster-role="editor-panel" part="editor-panel">
|
|
2379
|
+
<div data-monster-role="tabs-container" part="tabs-container">
|
|
2380
|
+
<monster-tabs data-monster-role="tabs"></monster-tabs>
|
|
2381
|
+
</div>
|
|
2382
|
+
<monster-state data-monster-role="empty" part="empty"></monster-state>
|
|
2383
|
+
</div>
|
|
2384
|
+
</monster-split-panel>
|
|
2385
|
+
</div>
|
|
2386
|
+
`;
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
/**
|
|
2390
|
+
* @private
|
|
2391
|
+
* @return {string}
|
|
2392
|
+
*/
|
|
2393
|
+
function getIconSprite() {
|
|
2394
|
+
// language=HTML
|
|
2395
|
+
return `
|
|
2396
|
+
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
|
|
2397
|
+
<symbol id="monster-file-icon-default" viewBox="0 0 16 16">
|
|
2398
|
+
<path d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5z"/>
|
|
2399
|
+
</symbol>
|
|
2400
|
+
<symbol id="monster-file-icon-code" viewBox="0 0 16 16">
|
|
2401
|
+
<path d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5z"/>
|
|
2402
|
+
<path d="M8.646 6.646a.5.5 0 0 1 .708 0l2 2a.5.5 0 0 1 0 .708l-2 2a.5.5 0 0 1-.708-.708L10.293 9 8.646 7.354a.5.5 0 0 1 0-.708m-1.292 0a.5.5 0 0 0-.708 0l-2 2a.5.5 0 0 0 0 .708l2 2a.5.5 0 0 0 .708-.708L5.707 9l1.647-1.646a.5.5 0 0 0 0-.708"/>
|
|
2403
|
+
</symbol>
|
|
2404
|
+
<symbol id="monster-file-icon-image" viewBox="0 0 16 16">
|
|
2405
|
+
<path d="M6.502 7a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3"/>
|
|
2406
|
+
<path d="M14 14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zM4 1a1 1 0 0 0-1 1v10l2.224-2.224a.5.5 0 0 1 .61-.075L8 11l2.157-3.02a.5.5 0 0 1 .76-.063L13 10V4.5h-2A1.5 1.5 0 0 1 9.5 3V1z"/>
|
|
2407
|
+
</symbol>
|
|
2408
|
+
<symbol id="monster-file-icon-sound" viewBox="0 0 16 16">
|
|
2409
|
+
<path d="M11 6.64a1 1 0 0 0-1.243-.97l-1 .25A1 1 0 0 0 8 6.89v4.306A2.6 2.6 0 0 0 7 11c-.5 0-.974.134-1.338.377-.36.24-.662.628-.662 1.123s.301.883.662 1.123c.364.243.839.377 1.338.377s.974-.134 1.338-.377c.36-.24.662-.628.662-1.123V8.89l2-.5z"/>
|
|
2410
|
+
<path d="M14 14V4.5L9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2M9.5 3A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5z"/>
|
|
2411
|
+
</symbol>
|
|
2412
|
+
<symbol id="monster-file-icon-pdf" viewBox="0 0 16 16">
|
|
2413
|
+
<path d="M14 14V4.5L9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2M9.5 3A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5z"/>
|
|
2414
|
+
<path d="M4.603 14.087a.8.8 0 0 1-.438-.42c-.195-.388-.13-.776.08-1.102.198-.307.526-.568.897-.787a7.7 7.7 0 0 1 1.482-.645 20 20 0 0 0 1.062-2.227 7.3 7.3 0 0 1-.43-1.295c-.086-.4-.119-.796-.046-1.136.075-.354.274-.672.65-.823.192-.077.4-.12.602-.077a.7.7 0 0 1 .477.365c.088.164.12.356.127.538.007.188-.012.396-.047.614-.084.51-.27 1.134-.52 1.794a11 11 0 0 0 .98 1.686 5.8 5.8 0 0 1 1.334.05c.364.066.734.195.96.465.12.144.193.32.2.518.007.192-.047.382-.138.563a1.04 1.04 0 0 1-.354.416.86.86 0 0 1-.51.138c-.331-.014-.654-.196-.933-.417a5.7 5.7 0 0 1-.911-.95 11.7 11.7 0 0 0-1.997.406 11.3 11.3 0 0 1-1.02 1.51c-.292.35-.609.656-.927.787a.8.8 0 0 1-.58.029m1.379-1.901q-.25.115-.459.238c-.328.194-.541.383-.647.547-.094.145-.096.25-.04.361q.016.032.026.044l.035-.012c.137-.056.355-.235.635-.572a8 8 0 0 0 .45-.606m1.64-1.33a13 13 0 0 1 1.01-.193 12 12 0 0 1-.51-.858 21 21 0 0 1-.5 1.05zm2.446.45q.226.245.435.41c.24.19.407.253.498.256a.1.1 0 0 0 .07-.015.3.3 0 0 0 .094-.125.44.44 0 0 0 .059-.2.1.1 0 0 0-.026-.063c-.052-.062-.2-.152-.518-.209a4 4 0 0 0-.612-.053zM8.078 7.8a7 7 0 0 0 .2-.828q.046-.282.038-.465a.6.6 0 0 0-.032-.198.5.5 0 0 0-.145.04c-.087.035-.158.106-.196.283-.04.192-.03.469.046.822q.036.167.09.346z"/>
|
|
2415
|
+
</symbol>
|
|
2416
|
+
<symbol id="monster-file-icon-css" viewBox="0 0 16 16">
|
|
2417
|
+
<path d="M14 4.5V14a2 2 0 0 1-2 2h-1v-1h1a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5z"/>
|
|
2418
|
+
<path d="M3.397 14.841a1.13 1.13 0 0 0 .401.823q.195.162.478.252.284.091.665.091.507 0 .859-.158.354-.158.539-.44.187-.284.187-.656 0-.336-.134-.56a1 1 0 0 0-.375-.357 2 2 0 0 0-.566-.21l-.621-.144a1 1 0 0 1-.404-.176.37.37 0 0 1-.144-.299q0-.234.185-.384.188-.152.512-.152.214 0 .37.068a.6.6 0 0 1 .246.181.56.56 0 0 1 .12.258h.75a1.1 1.1 0 0 0-.2-.566 1.2 1.2 0 0 0-.5-.41 1.8 1.8 0 0 0-.78-.152q-.439 0-.776.15-.337.149-.527.421-.19.273-.19.639 0 .302.122.524.124.223.352.367.228.143.539.213l.618.144q.31.073.463.193a.39.39 0 0 1 .152.326.5.5 0 0 1-.085.29.56.56 0 0 1-.255.193q-.167.07-.413.07-.175 0-.32-.04a.8.8 0 0 1-.248-.115.58.58 0 0 1-.255-.384zM.806 13.693q0-.373.102-.633a.87.87 0 0 1 .302-.399.8.8 0 0 1 .475-.137q.225 0 .398.097a.7.7 0 0 1 .272.26.85.85 0 0 1 .12.381h.765v-.072a1.33 1.33 0 0 0-.466-.964 1.4 1.4 0 0 0-.489-.272 1.8 1.8 0 0 0-.606-.097q-.534 0-.911.223-.375.222-.572.632-.195.41-.196.979v.498q0 .568.193.976.197.407.572.626.375.217.914.217.439 0 .785-.164t.55-.454a1.27 1.27 0 0 0 .226-.674v-.076h-.764a.8.8 0 0 1-.118.363.7.7 0 0 1-.272.25.9.9 0 0 1-.401.087.85.85 0 0 1-.478-.132.83.83 0 0 1-.299-.392 1.7 1.7 0 0 1-.102-.627zM6.78 15.29a1.2 1.2 0 0 1-.111-.449h.764a.58.58 0 0 0 .255.384q.106.073.25.114.142.041.319.041.245 0 .413-.07a.56.56 0 0 0 .255-.193.5.5 0 0 0 .085-.29.39.39 0 0 0-.153-.326q-.152-.12-.463-.193l-.618-.143a1.7 1.7 0 0 1-.539-.214 1 1 0 0 1-.351-.367 1.1 1.1 0 0 1-.123-.524q0-.366.19-.639.19-.272.527-.422t.777-.149q.456 0 .779.152.326.153.5.41.18.255.2.566h-.75a.56.56 0 0 0-.12-.258.6.6 0 0 0-.246-.181.9.9 0 0 0-.37-.068q-.324 0-.512.152a.47.47 0 0 0-.184.384q0 .18.143.3a1 1 0 0 0 .404.175l.621.143q.326.075.566.211t.375.358.135.56q0 .37-.188.656a1.2 1.2 0 0 1-.539.439q-.351.158-.858.158-.381 0-.665-.09a1.4 1.4 0 0 1-.478-.252 1.1 1.1 0 0 1-.29-.375"/>
|
|
2419
|
+
</symbol>
|
|
2420
|
+
<symbol id="monster-file-icon-csv" viewBox="0 0 16 16">
|
|
2421
|
+
<path d="M14 4.5V14a2 2 0 0 1-2 2h-1v-1h1a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5z"/>
|
|
2422
|
+
<path d="M3.517 14.841a1.13 1.13 0 0 0 .401.823q.195.162.478.252.284.091.665.091.507 0 .859-.158.354-.158.539-.44.187-.284.187-.656 0-.336-.134-.56a1 1 0 0 0-.375-.357 2 2 0 0 0-.566-.21l-.621-.144a1 1 0 0 1-.404-.176.37.37 0 0 1-.144-.299q0-.234.185-.384.188-.152.512-.152.214 0 .37.068a.6.6 0 0 1 .246.181.56.56 0 0 1 .12.258h.75a1.1 1.1 0 0 0-.2-.566 1.2 1.2 0 0 0-.5-.41 1.8 1.8 0 0 0-.78-.152q-.439 0-.776.15-.337.149-.527.421-.19.273-.19.639 0 .302.122.524.124.223.352.367.228.143.539.213l.618.144q.31.073.463.193a.39.39 0 0 1 .152.326.5.5 0 0 1-.085.29.56.56 0 0 1-.255.193q-.167.07-.413.07-.175 0-.32-.04a.8.8 0 0 1-.248-.115.58.58 0 0 1-.255-.384zM.806 13.693q0-.373.102-.633a.87.87 0 0 1 .302-.399.8.8 0 0 1 .475-.137q.225 0 .398.097a.7.7 0 0 1 .272.26.85.85 0 0 1 .12.381h.765v-.072a1.33 1.33 0 0 0-.466-.964 1.4 1.4 0 0 0-.489-.272 1.8 1.8 0 0 0-.606-.097q-.534 0-.911.223-.375.222-.572.632-.195.41-.196.979v.498q0 .568.193.976.197.407.572.626.375.217.914.217.439 0 .785-.164t.55-.454a1.27 1.27 0 0 0 .226-.674v-.076h-.764a.8.8 0 0 1-.118.363.7.7 0 0 1-.272.25.9.9 0 0 1-.401.087.85.85 0 0 1-.478-.132.83.83 0 0 1-.299-.392 1.7 1.7 0 0 1-.102-.627zm8.239 2.238h-.953l-1.338-3.999h.917l.896 3.138h.038l.888-3.138h.879z"/>
|
|
2423
|
+
</symbol>
|
|
2424
|
+
<symbol id="monster-file-icon-doc" viewBox="0 0 16 16">
|
|
2425
|
+
<path d="M14 4.5V14a2 2 0 0 1-2 2v-1a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5z"/>
|
|
2426
|
+
<path d="M6.161 13.666v.522q0 .384-.117.641a.86.86 0 0 1-.322.387.9.9 0 0 1-.469.126.9.9 0 0 1-.471-.126.87.87 0 0 1-.32-.386 1.55 1.55 0 0 1-.117-.642v-.522q0-.386.117-.641a.87.87 0 0 1 .32-.387.87.87 0 0 1 .471-.129q.264 0 .469.13a.86.86 0 0 1 .322.386q.117.255.117.641m.803.519v-.513q0-.565-.205-.972a1.46 1.46 0 0 0-.589-.63q-.381-.22-.917-.22-.533 0-.92.22a1.44 1.44 0 0 0-.589.627q-.204.406-.205.975v.513q0 .563.205.973.205.406.59.627.386.216.92.216.535 0 .916-.216.383-.22.59-.627.204-.41.204-.973M0 11.926v4h1.459q.603 0 .999-.238a1.45 1.45 0 0 0 .595-.689q.196-.45.196-1.084 0-.63-.196-1.075a1.43 1.43 0 0 0-.59-.68q-.395-.234-1.004-.234zm.791.645h.563q.371 0 .609.152a.9.9 0 0 1 .354.454q.118.302.118.753a2.3 2.3 0 0 1-.068.592 1.1 1.1 0 0 1-.196.422.8.8 0 0 1-.334.252 1.3 1.3 0 0 1-.483.082H.79V12.57Zm7.422.483a1.7 1.7 0 0 0-.103.633v.495q0 .369.103.627a.83.83 0 0 0 .298.393.85.85 0 0 0 .478.131.9.9 0 0 0 .401-.088.7.7 0 0 0 .273-.248.8.8 0 0 0 .117-.364h.765v.076a1.27 1.27 0 0 1-.226.674q-.205.29-.55.454a1.8 1.8 0 0 1-.786.164q-.54 0-.914-.216a1.4 1.4 0 0 1-.571-.627q-.194-.408-.194-.976v-.498q0-.568.197-.978.195-.411.571-.633.378-.223.911-.223.328 0 .607.097.28.093.489.272a1.33 1.33 0 0 1 .466.964v.073H9.78a.85.85 0 0 0-.12-.38.7.7 0 0 0-.273-.261.8.8 0 0 0-.398-.097.8.8 0 0 0-.475.138.87.87 0 0 0-.301.398"/>
|
|
2427
|
+
</symbol>
|
|
2428
|
+
<symbol id="monster-file-icon-jpg" viewBox="0 0 16 16">
|
|
2429
|
+
<path d="M14 4.5V14a2 2 0 0 1-2 2h-1v-1h1a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5z"/>
|
|
2430
|
+
<path d="M9.66 12.632q.114.23.14.492h-.776a.8.8 0 0 0-.097-.249.7.7 0 0 0-.17-.19.7.7 0 0 0-.237-.126 1 1 0 0 0-.299-.044q-.428 0-.665.302-.234.301-.234.85v.498q0 .351.097.615a.9.9 0 0 0 .304.413.87.87 0 0 0 .519.146 1 1 0 0 0 .457-.096.67.67 0 0 0 .272-.264q.09-.164.091-.363v-.255H8.24v-.59h1.576v.798q0 .29-.097.55a1.3 1.3 0 0 1-.293.458 1.4 1.4 0 0 1-.495.313q-.296.111-.697.111a2 2 0 0 1-.753-.132 1.45 1.45 0 0 1-.533-.377 1.6 1.6 0 0 1-.32-.58 2.5 2.5 0 0 1-.105-.745v-.506q0-.543.2-.95.201-.406.582-.633.384-.228.926-.228.357 0 .636.1.28.1.48.275t.314.407ZM0 14.786q0 .246.082.465.083.22.243.39.165.17.407.267.246.093.569.093.63 0 .984-.345.357-.346.358-1.005v-2.725h-.791v2.745q0 .303-.138.466t-.422.164a.5.5 0 0 1-.454-.246.6.6 0 0 1-.073-.27H0Zm4.92-2.86H3.322v4h.791v-1.343h.803q.43 0 .732-.172.305-.177.463-.475.162-.302.161-.677 0-.374-.158-.677a1.2 1.2 0 0 0-.46-.477q-.3-.18-.732-.179Zm.546 1.333a.8.8 0 0 1-.085.381.57.57 0 0 1-.238.24.8.8 0 0 1-.375.082H4.11v-1.406h.66q.327 0 .512.182.185.181.185.521Z"/>
|
|
2431
|
+
</symbol>
|
|
2432
|
+
<symbol id="monster-file-icon-html" viewBox="0 0 16 16">
|
|
2433
|
+
<path d="M14 4.5V11h-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5z"/>
|
|
2434
|
+
<path d="M4.264 11.85v3.999h-.791v-1.714H1.79v1.714H1V11.85h.791v1.626h1.682V11.85h.79Zm2.251.662v3.337h-.794v-3.337H4.588v-.662h3.064v.662zm2.176 3.337v-2.66h.038l.952 2.159h.516l.946-2.16h.038v2.661h.715V11.85h-.8l-1.14 2.596H9.93L8.79 11.85h-.805v3.999zm4.71-.674h1.696v.674H12.61V11.85h.79v3.325Z"/>
|
|
2435
|
+
</symbol>
|
|
2436
|
+
<symbol id="monster-file-icon-gif" viewBox="0 0 16 16">
|
|
2437
|
+
<path d="M14 4.5V14a2 2 0 0 1-2 2H9v-1h3a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5z"/>
|
|
2438
|
+
<path d="M3.278 13.124a1.4 1.4 0 0 0-.14-.492 1.3 1.3 0 0 0-.314-.407 1.5 1.5 0 0 0-.48-.275 1.9 1.9 0 0 0-.636-.1q-.542 0-.926.229a1.5 1.5 0 0 0-.583.632 2.1 2.1 0 0 0-.199.95v.506q0 .408.105.745.105.336.32.58.213.243.533.377.323.132.753.132.402 0 .697-.111a1.29 1.29 0 0 0 .788-.77q.097-.261.097-.551v-.797H1.717v.589h.823v.255q0 .199-.09.363a.67.67 0 0 1-.273.264 1 1 0 0 1-.457.096.87.87 0 0 1-.519-.146.9.9 0 0 1-.305-.413 1.8 1.8 0 0 1-.096-.615v-.499q0-.547.234-.85.237-.3.665-.301a1 1 0 0 1 .3.044q.136.044.236.126a.7.7 0 0 1 .17.19.8.8 0 0 1 .097.25zm1.353 2.801v-3.999H3.84v4h.79Zm1.493-1.59v1.59h-.791v-3.999H7.88v.653H6.124v1.117h1.605v.638z"/>
|
|
2439
|
+
</symbol>
|
|
2440
|
+
<symbol id="monster-file-icon-mp4" viewBox="0 0 16 16">
|
|
2441
|
+
<path d="M14 4.5V14a2 2 0 0 1-2 2v-1a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5z"/>
|
|
2442
|
+
<path d="M.706 15.849v-2.66h.038l.952 2.16h.516l.946-2.16h.038v2.66h.715V11.85h-.8l-1.14 2.596h-.026L.805 11.85H0v3.999zm5.237-3.999q-.393.65-.79 1.3t-.748 1.31v.648h1.937v.741h.74v-.741h.49v-.639h-.49V11.85H5.944Zm-.82 2.62v-.021q.27-.51.571-1.017.304-.507.607-.984h.04v2.021H5.124Zm2.893-2.62h1.6q.434 0 .732.179.302.175.46.477t.158.677-.16.677q-.159.298-.464.474a1.45 1.45 0 0 1-.732.173h-.803v1.342h-.79zm2.06 1.714a.8.8 0 0 0 .085-.381q0-.34-.185-.521-.184-.183-.513-.182h-.659v1.406h.66a.8.8 0 0 0 .374-.082.57.57 0 0 0 .238-.24"/>
|
|
2443
|
+
</symbol>
|
|
2444
|
+
<symbol id="monster-file-icon-mov" viewBox="0 0 16 16">
|
|
2445
|
+
<path d="M14 4.5V14a2 2 0 0 1-2 2v-1a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5z"/>
|
|
2446
|
+
<path d="M7.086 13.666v.522q0 .384-.117.641a.86.86 0 0 1-.323.387.9.9 0 0 1-.468.126.9.9 0 0 1-.472-.126.87.87 0 0 1-.32-.386 1.55 1.55 0 0 1-.117-.642v-.522q0-.386.118-.641a.87.87 0 0 1 .319-.387.87.87 0 0 1 .472-.129q.263 0 .468.13a.86.86 0 0 1 .323.386q.117.255.117.641m.802.519v-.513q0-.565-.205-.972a1.46 1.46 0 0 0-.588-.63q-.381-.22-.917-.22-.534 0-.92.22a1.44 1.44 0 0 0-.59.627q-.204.406-.204.975v.513q0 .563.205.973.205.406.589.627.386.216.92.216.536 0 .917-.216.383-.22.588-.627.205-.41.205-.973m-7.182 1.74v-2.66h.038l.952 2.16h.516l.946-2.16h.038v2.66h.715v-3.999h-.8l-1.14 2.596h-.026l-1.14-2.596H0v4zm9.54 0h-.952l-1.34-3.999h.918l.896 3.138h.038l.888-3.138h.879z"/>
|
|
2447
|
+
</symbol>
|
|
2448
|
+
<symbol id="monster-file-icon-mp3" viewBox="0 0 16 16">
|
|
2449
|
+
<path d="M14 4.5V14a2 2 0 0 1-2 2v-1a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5z"/>
|
|
2450
|
+
<path d="M9.089 14.17h-.443v-.609h.422a.7.7 0 0 0 .322-.073.56.56 0 0 0 .22-.2.5.5 0 0 0 .076-.284.49.49 0 0 0-.176-.392.65.65 0 0 0-.442-.15.7.7 0 0 0-.252.041.6.6 0 0 0-.193.112.5.5 0 0 0-.179.349H7.71q.009-.235.102-.437.094-.202.27-.352.176-.152.428-.237.255-.085.583-.088.418-.003.723.132.304.135.472.372a.9.9 0 0 1 .173.539.83.83 0 0 1-.12.478.96.96 0 0 1-.619.439v.041a1 1 0 0 1 .718.434.9.9 0 0 1 .144.521q.003.285-.117.507a1.1 1.1 0 0 1-.329.378q-.21.152-.486.234-.273.08-.583.08-.451 0-.77-.153a1.2 1.2 0 0 1-.487-.41 1.1 1.1 0 0 1-.178-.563h.726a.46.46 0 0 0 .106.258.7.7 0 0 0 .249.179 1 1 0 0 0 .357.067.9.9 0 0 0 .384-.076.6.6 0 0 0 .252-.217.56.56 0 0 0 .088-.319.56.56 0 0 0-.334-.522.8.8 0 0 0-.372-.079ZM.706 15.925v-2.66h.038l.952 2.16h.516l.946-2.16h.038v2.66h.715v-3.999h-.8l-1.14 2.596h-.026l-1.14-2.596H0v4zm5.458-3.999h-1.6v4h.792v-1.342h.803q.43 0 .732-.173.304-.177.463-.475a1.4 1.4 0 0 0 .161-.677q0-.374-.158-.677a1.2 1.2 0 0 0-.46-.477 1.4 1.4 0 0 0-.733-.179m.545 1.333a.8.8 0 0 1-.085.381.57.57 0 0 1-.237.24.8.8 0 0 1-.375.082h-.66v-1.406h.66q.328 0 .513.182.184.181.184.521"/>
|
|
2451
|
+
</symbol>
|
|
2452
|
+
<symbol id="monster-file-icon-txt" viewBox="0 0 16 16">
|
|
2453
|
+
<path d="M14 4.5V14a2 2 0 0 1-2 2h-2v-1h2a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5z"/>
|
|
2454
|
+
<path d="M1.928 15.849v-3.337h1.136v-.662H0v.662h1.134v3.337zm4.689-3.999h-.894L4.9 13.289h-.035l-.832-1.439h-.932l1.228 1.983-1.24 2.016h.862l.853-1.415h.035l.85 1.415h.907l-1.253-1.992zm1.93.662v3.337h-.794v-3.337H6.619v-.662h3.064v.662H8.546Z"/>
|
|
2455
|
+
</symbol>
|
|
2456
|
+
<symbol id="monster-file-icon-png" viewBox="0 0 16 16">
|
|
2457
|
+
<path d="M14 4.5V14a2 2 0 0 1-2 2v-1a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5z"/>
|
|
2458
|
+
<path d="M10.24 12.632q.114.23.14.492h-.776a.8.8 0 0 0-.097-.249.7.7 0 0 0-.17-.19.7.7 0 0 0-.237-.126 1 1 0 0 0-.299-.044q-.427 0-.665.302-.234.301-.234.85v.498q0 .351.097.615a.9.9 0 0 0 .304.413.87.87 0 0 0 .519.146 1 1 0 0 0 .457-.096.67.67 0 0 0 .272-.264q.09-.164.091-.363v-.255H8.82v-.59h1.576v.798q0 .29-.097.55a1.3 1.3 0 0 1-.293.458 1.4 1.4 0 0 1-.495.313q-.296.111-.697.111a2 2 0 0 1-.753-.132 1.45 1.45 0 0 1-.533-.377 1.6 1.6 0 0 1-.32-.58 2.5 2.5 0 0 1-.105-.745v-.506q0-.543.2-.95.201-.406.582-.633.384-.228.926-.228.357 0 .636.1.281.1.48.275.2.176.314.407Zm-8.64-.706H0v4h.791v-1.343h.803q.43 0 .732-.172.305-.177.463-.475a1.4 1.4 0 0 0 .161-.677q0-.374-.158-.677a1.2 1.2 0 0 0-.46-.477q-.3-.18-.732-.179m.545 1.333a.8.8 0 0 1-.085.381.57.57 0 0 1-.238.24.8.8 0 0 1-.375.082H.788v-1.406h.66q.327 0 .512.182.185.181.185.521m1.964 2.666V13.25h.032l1.761 2.675h.656v-3.999h-.75v2.66h-.032l-1.752-2.66h-.662v4z"/>
|
|
2459
|
+
</symbol>
|
|
2460
|
+
</svg>
|
|
2461
|
+
`;
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
registerCustomElement(FileManager);
|