@jinntec/fore 3.3.1 → 4.0.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.
Files changed (73) hide show
  1. package/README.md +98 -70
  2. package/dist/fore-dev.js +5593 -6832
  3. package/dist/fore.js +5602 -4887
  4. package/index.js +5 -10
  5. package/package.json +7 -7
  6. package/resources/fore.css +33 -0
  7. package/src/DependencyNotifyingDomFacade.js +90 -21
  8. package/src/DependentXPathQueries.js +15 -2
  9. package/src/ForeElementMixin.js +110 -16
  10. package/src/UndoManager.js +267 -0
  11. package/src/actions/abstract-action.js +71 -30
  12. package/src/actions/fx-action.js +5 -0
  13. package/src/actions/fx-append.js +3 -3
  14. package/src/actions/fx-commit-history.js +26 -0
  15. package/src/actions/fx-hide.js +1 -1
  16. package/src/actions/fx-insert.js +25 -22
  17. package/src/actions/fx-load.js +5 -5
  18. package/src/actions/fx-redo.js +58 -0
  19. package/src/actions/fx-refresh.js +2 -2
  20. package/src/actions/fx-reload.js +1 -1
  21. package/src/actions/fx-replace.js +1 -1
  22. package/src/actions/fx-send.js +27 -5
  23. package/src/actions/fx-setattribute.js +11 -7
  24. package/src/actions/fx-undo.js +58 -0
  25. package/src/createNodes.js +314 -0
  26. package/src/fore.js +53 -18
  27. package/src/functions/fx-functionlib.js +10 -10
  28. package/src/fx-bind.js +30 -18
  29. package/src/fx-fore.js +222 -200
  30. package/src/fx-instance.js +18 -1
  31. package/src/fx-model.js +236 -69
  32. package/src/fx-submission.js +37 -29
  33. package/src/fx-var.js +49 -13
  34. package/src/getInScopeContext.js +1 -1
  35. package/src/json/JSONDomFacade.js +1 -1
  36. package/src/json/JSONLens.js +2 -2
  37. package/src/ui/UIElement.js +18 -8
  38. package/src/ui/abstract-control.js +45 -3
  39. package/src/ui/fx-alert.js +4 -0
  40. package/src/ui/fx-case.js +1 -1
  41. package/src/ui/fx-container.js +3 -0
  42. package/src/ui/fx-control-menu.js +79 -11
  43. package/src/ui/fx-control.js +130 -41
  44. package/src/ui/fx-dialog.js +5 -0
  45. package/src/ui/fx-items.js +6 -6
  46. package/src/ui/fx-output.js +37 -1
  47. package/src/ui/fx-repeat.js +1065 -103
  48. package/src/ui/fx-repeatitem.js +4 -1
  49. package/src/ui/fx-switch.js +116 -3
  50. package/src/ui/fx-trigger.js +9 -4
  51. package/src/ui/fx-upload.js +10 -4
  52. package/src/ui/repeat-base.js +20 -12
  53. package/src/withDraggability.js +10 -1
  54. package/src/xpath-evaluation.js +30 -18
  55. package/src/xpath-path.js +122 -0
  56. package/src/xpath-util.js +11 -126
  57. package/src/actions/StringTpl.js +0 -17
  58. package/src/extract-predicate-deps.js +0 -57
  59. package/src/extractPredicateDependencies.js +0 -36
  60. package/src/json/lensFromNode.js +0 -5
  61. package/src/json-util.js +0 -27
  62. package/src/tools/adi.js +0 -1111
  63. package/src/tools/deprecation.md +0 -1
  64. package/src/tools/fx-action-log.js +0 -745
  65. package/src/tools/fx-devtools.js +0 -444
  66. package/src/tools/fx-dom-inspector.js +0 -610
  67. package/src/tools/fx-json-instance.js +0 -444
  68. package/src/tools/fx-log-item.js +0 -128
  69. package/src/tools/fx-log-settings.js +0 -533
  70. package/src/tools/fx-minimap.js +0 -203
  71. package/src/tools/helpers.js +0 -132
  72. package/src/ui/TemplateExpression.js +0 -12
  73. package/src/ui/fx-dom-inspector.js +0 -1255
package/src/tools/adi.js DELETED
@@ -1,1111 +0,0 @@
1
- import {
2
- addClass,
3
- removeClass,
4
- isEmptyTextNode,
5
- containsOnlyText,
6
- newElement,
7
- drawOptions,
8
- pauseEvent,
9
- drawAttrRow,
10
- } from './helpers.js';
11
-
12
- import { Fore } from '../fore.js';
13
-
14
- function isAttributeShown(name, sourceNode) {
15
- if (name === 'style') return false;
16
- return true;
17
- // return name === 'id' || name === 'ref' || name === 'event';
18
- }
19
-
20
- class ADI {
21
- constructor(rootElement, instance) {
22
- this.sourceNodeByInspectorNodeLookup = new Map();
23
-
24
- this.uiView = null;
25
- this.menuView = null;
26
- this.domView = null;
27
- this.attrView = null;
28
- this.optsView = null;
29
- /**
30
- * The current active element. Note this is the element in the DOM view
31
- */
32
- this.activeElement = null;
33
- this.vertResizing = false;
34
- this.horizResizing = false;
35
- this.pathScrolling = null;
36
- this.elemLookup = false;
37
- this.styleBackup = '';
38
- this.xPos = 0;
39
- this.delegatedEvents = [];
40
-
41
- this.options = {
42
- align: 'right', // NOTE: left is not supported in this version
43
- split: 50,
44
- minSplit: 30,
45
- visible: true,
46
- saving: false,
47
- transparent: true,
48
- omitEmptyText: true,
49
- makeVisible: true,
50
- foldText: true,
51
- nodeTypes: [Node.ELEMENT_NODE, Node.TEXT_NODE, Node.COMMENT_NODE, Node.DOCUMENT_NODE],
52
- };
53
-
54
- if (instance === '#document') {
55
- this.instanceId = '#document';
56
- this.document = window.document;
57
- this.isInstanceViewer = false;
58
- } else {
59
- this.instanceId = instance.id;
60
- if (!instance || instance.localName !== 'fx-instance') {
61
- console.error('No instance found!');
62
- }
63
- this.document = instance.getInstanceData();
64
- this.isInstanceViewer = true;
65
- this.options.foldText = false;
66
- }
67
- this.drawUI(rootElement);
68
- this.registerEvents();
69
-
70
- // We're updating here, but we're doing that again later, when the UI is read (the 'ready' event fires)
71
- this.drawDOM(this.document, this.domView.querySelector('.adi-tree-view'), true);
72
- document.addEventListener('execute-action', e => this.processExecuteAction);
73
- }
74
-
75
- // Returns selected element or null
76
- getSelected() {
77
- if (!this.activeElement) {
78
- return null;
79
- }
80
-
81
- let elem = document;
82
-
83
- elem = this.sourceNodeByInspectorNodeLookup.get(this.activeElement);
84
- /*
85
- document.dispatchEvent(
86
- new CustomEvent('path-touched', {
87
- composed: true,
88
- bubbles: true,
89
- detail: {path: elem.modelItem.path},
90
- }),
91
- );
92
- */
93
-
94
- return elem;
95
- }
96
-
97
- // Loads user defined options stored in HTML5 storage (if available)
98
- loadOptions() {
99
- let userOptions = {};
100
-
101
- userOptions = JSON.parse(window.localStorage.getItem('ADI.options')) || {};
102
-
103
- // merge with defaults
104
- for (const opt of Object.keys(userOptions)) {
105
- this.options[opt] = userOptions[opt];
106
- }
107
- }
108
-
109
- // Saves user defined options into the HTML5 storage (if available)
110
- saveOptions() {
111
- if (this.options.saving) {
112
- window.localStorage.setItem('ADI.options', JSON.stringify(this.options));
113
- }
114
- }
115
-
116
- // Checks if a node has some child nodes and if at least on of them is of a supported type
117
- hasRequiredNodes(node) {
118
- if (typeof node !== 'object') {
119
- throw new Error(
120
- `hasRequiredNodes: Expected argument node of type object, ${typeof node} given.`,
121
- );
122
- }
123
-
124
- if (node.hasChildNodes()) {
125
- for (let i = 0, len = node.childNodes.length; i < len; i += 1) {
126
- const child = node.childNodes[i];
127
- if (this.options.nodeTypes.includes(child.nodeType)) {
128
- return true;
129
- }
130
- }
131
- }
132
-
133
- return false;
134
- }
135
-
136
- // Creates a starting markup for a new DOM tree view node
137
- newTreeNode(sourceNode) {
138
- if (typeof sourceNode !== 'object') {
139
- throw new Error(
140
- `newTreeNode: Expected argument node of type object, ${typeof sourceNode} given.`,
141
- );
142
- }
143
-
144
- const withChildren = this.hasRequiredNodes(sourceNode);
145
- let omit = false;
146
- let adiNode = sourceNode.nodeName.startsWith('FX-')
147
- ? `adi-node ${sourceNode.nodeName.toLowerCase()}`
148
- : '';
149
- if (sourceNode.nodeName.startsWith('FX-')) {
150
- adiNode = `adi-node ${sourceNode.nodeName.toLowerCase()}`;
151
- adiNode += Fore.isActionElement(sourceNode.nodeName) ? ' action' : '';
152
- }
153
- const elem = newElement('li', {
154
- class: adiNode,
155
- });
156
-
157
- // do not show ADI DOM nodes in the DOM view
158
- if (sourceNode === this.uiView) {
159
- return null;
160
- }
161
-
162
- // generate UI for elements with children
163
- if (withChildren) {
164
- elem.appendChild(newElement('span', { class: 'adi-trigger' }));
165
- }
166
-
167
- // we can omit empty text nodes if allowed in options
168
- if (this.options.omitEmptyText && sourceNode.nodeType === Node.TEXT_NODE) {
169
- omit = isEmptyTextNode(sourceNode);
170
- }
171
-
172
- if (!omit) {
173
- const tagStart = newElement('span');
174
-
175
- this.sourceNodeByInspectorNodeLookup.set(tagStart, sourceNode);
176
- this.sourceNodeByInspectorNodeLookup.set(sourceNode, tagStart);
177
-
178
- let tagEnd = null;
179
-
180
- if (containsOnlyText(sourceNode)) {
181
- if (sourceNode.nodeType === Node.COMMENT_NODE) {
182
- addClass(tagStart, 'adi-comment-node');
183
- if (typeof tagStart.innerText === 'string') {
184
- tagStart.innerText = `<!-- ${sourceNode.textContent} -->`;
185
- }
186
- } else {
187
- addClass(tagStart, 'adi-text-node');
188
- tagStart.textContent = sourceNode.textContent;
189
- }
190
- } else {
191
- addClass(tagStart, 'adi-normal-node');
192
- if (sourceNode.nodeType !== Node.DOCUMENT_NODE) {
193
- // tagStart.textContent = '<' + node.nodeName.toLowerCase() + '>';
194
-
195
- /*
196
- let attrString = `<${sourceNode.nodeName.toLowerCase()} `;
197
- if(sourceNode.attributes){
198
- Array.from(sourceNode.attributes).forEach(attr => {
199
- attrString += `${attr.nodeName}="${attr.nodeValue}" `;
200
- });
201
- console.log('ATTRSTRING',attrString);
202
- }
203
- if (sourceNode.nodeName === 'FX-BIND') {
204
- tagStart.textContent = `<${sourceNode.nodeName.toLowerCase()} ref="${sourceNode.getAttribute('ref')}">`;
205
- } else if (sourceNode.nodeName === 'FX-INSERT') {
206
- tagStart.textContent = `<${sourceNode.nodeName.toLowerCase()} ref="${sourceNode.getAttribute('ref')}">`;
207
- } else if (sourceNode.nodeName === 'FX-INSTANCE') {
208
- tagStart.textContent = `<${sourceNode.nodeName.toLowerCase()} id="${sourceNode.id}">`;
209
- } else if (sourceNode.nodeName === 'FX-CONTROL') {
210
- tagStart.textContent = `<${sourceNode.nodeName.toLowerCase()} ref="${sourceNode.getAttribute('ref')}">`;
211
- } else if (sourceNode.nodeName === 'FX-SEND') {
212
- tagStart.textContent = `<${sourceNode.nodeName.toLowerCase()} submission="${sourceNode.getAttribute('submission')}">`;
213
- } else if (sourceNode.nodeName === 'FX-SETVALUE') {
214
- tagStart.textContent = `<${sourceNode.nodeName.toLowerCase()} ref="${sourceNode.getAttribute('ref')}">`;
215
- } else if (sourceNode.nodeName === 'FX-SUBMISSION') {
216
- tagStart.textContent = `<${sourceNode.nodeName.toLowerCase()} id="${sourceNode.getAttribute('id')}">`;
217
- } else {
218
- */
219
- const attrString = Array.from(sourceNode.attributes)
220
- .filter(attr =>
221
- this.isInstanceViewer ? true : isAttributeShown(attr.name, sourceNode),
222
- )
223
- .map(attr => `${attr.name}="${attr.value}"`)
224
- .join(' ');
225
- tagStart.textContent = `<${sourceNode.nodeName.toLowerCase()}${
226
- attrString ? ` ${attrString}` : ''
227
- }>`;
228
- // }
229
-
230
- if (withChildren) {
231
- tagEnd = newElement('span');
232
- addClass(tagEnd, 'adi-end-node');
233
- tagEnd.textContent = `</${sourceNode.nodeName.toLowerCase()}>`;
234
- }
235
- } else {
236
- tagStart.textContent = sourceNode.nodeName.toLowerCase();
237
- }
238
- }
239
-
240
- elem.appendChild(tagStart);
241
- /*
242
- const icon = document.createElement('span');
243
- icon.textContent = 'x';
244
- icon.classList.add(('icon'))
245
- const icon2 = document.createElement('span');
246
- icon2.textContent = '<-';
247
- icon2.classList.add(('icon'))
248
- elem.appendChild(icon);
249
- elem.appendChild(icon2);
250
- */
251
- if (sourceNode.nodeName.startsWith('FX-')) {
252
- tagStart.classList.add('fore-node');
253
- tagStart.classList.add(sourceNode.nodeName.toLowerCase());
254
- }
255
-
256
- if (tagEnd) {
257
- elem.appendChild(tagEnd);
258
-
259
- if (sourceNode.nodeName.startsWith('FX-')) {
260
- tagEnd.classList.add('fore-node');
261
- }
262
- }
263
-
264
- return elem;
265
- }
266
- return null;
267
- }
268
-
269
- // Renders the DOM Tree view
270
- drawDOM(root, elem, isRoot) {
271
- if (typeof root !== 'object') {
272
- throw new Error(`drawDOM: Expected argument root of type object, ${typeof root} given.`);
273
- }
274
-
275
- let newNode = null;
276
- let isOpen = true;
277
-
278
- const adiNode = elem.nodeName.startsWith('FX-')
279
- ? `adi-node ${node.nodeName.toLowerCase()}`
280
- : '';
281
-
282
- if (isRoot && this.options.nodeTypes.indexOf(root.nodeType) !== -1) {
283
- elem.innerHTML = '';
284
- newNode = this.newTreeNode(root);
285
-
286
- if (this.hasRequiredNodes(root)) {
287
- newNode.appendChild(newElement('ul', { 'data-open': true, class: adiNode }));
288
-
289
- addClass(newNode.querySelector('.adi-trigger'), 'opened');
290
- }
291
-
292
- elem.appendChild(newNode);
293
- elem = elem.querySelector('ul');
294
- }
295
-
296
- // recursive DOM traversal
297
- for (let i = 0, len = root.childNodes.length; i < len; i += 1) {
298
- const node = root.childNodes[i];
299
- const withChildren = this.hasRequiredNodes(node);
300
-
301
- if (this.options.nodeTypes.indexOf(node.nodeType) !== -1) {
302
- newNode = this.newTreeNode(node);
303
-
304
- if (newNode) {
305
- if (withChildren) {
306
- if (this.options.foldText) {
307
- isOpen = !containsOnlyText(node, true);
308
- } else {
309
- isOpen = true;
310
- }
311
-
312
- if (node.nodeName === 'HEAD') isOpen = false;
313
- if (node.nodeName === 'SELECT') isOpen = false;
314
- if (node.nodeName === 'FX-INSTANCE') isOpen = false;
315
- /*
316
- if(this.options.closedElements.includes(node.nodeName.toLowerCase())){
317
- isOpen = false;
318
- }
319
- */
320
-
321
- if (node.nodeType === Node.DOCUMENT_NODE) {
322
- newNode.appendChild(newElement('ul', { 'data-open': isOpen, class: adiNode }));
323
- } else {
324
- newNode.insertBefore(
325
- newElement('ul', { 'data-open': isOpen, class: adiNode }),
326
- newNode.lastChild,
327
- );
328
- }
329
-
330
- addClass(newNode.querySelector('.adi-trigger'), isOpen ? 'opened' : 'closed');
331
- }
332
-
333
- elem.appendChild(newNode);
334
-
335
- if (this.getSelected() === node) {
336
- const span = newNode.querySelector('span.adi-normal-node');
337
- span?.classList?.add('adi-active-node');
338
- this.activeElement = span;
339
- newNode.scrollIntoView({ block: 'nearest', behavior: 'instant' });
340
- }
341
-
342
- if (withChildren) {
343
- this.drawDOM(node, newNode.querySelector('ul'), false);
344
- }
345
- }
346
- }
347
- }
348
- }
349
-
350
- // Show/hide the options view
351
- toggleOptions() {
352
- if (this.optsView.className.indexOf('adi-hidden') !== -1) {
353
- removeClass(this.optsView, 'adi-hidden');
354
- } else {
355
- addClass(this.optsView, 'adi-hidden');
356
- this.attrView.querySelector('.adi-content').innerHTML = '';
357
- this.refreshUI();
358
- this.drawDOM(document, this.domView.querySelector('.adi-tree-view'), true);
359
- if (this.options.saving) {
360
- this.saveOptions();
361
- } else {
362
- window.localStorage.removeItem('ADI.options');
363
- }
364
- }
365
- }
366
-
367
- // Renders the UI
368
- drawUI(rootElement) {
369
- this.uiView = newElement('div', {
370
- id: 'adi-wrapper',
371
- class: this.options.transparent ? 'transparent' : '',
372
- });
373
- this.domView = newElement('div', { id: 'adi-dom-view' });
374
- const domViewContent = newElement('div', { class: 'adi-content', id: 'detailsView' });
375
- // this.attrView.appendChild(newElement('fx-fore', {src: './lab/inspector-view.html'}));
376
-
377
- // const horizSplit = newElement('div', {id: 'adi-horiz-split'});
378
- const domTree = newElement('ul', { class: 'adi-tree-view' });
379
- const domPathWrap = newElement('div', { class: 'adi-path-wrap' });
380
- const domPathScrollLeft = newElement('span', { class: 'adi-path-left' });
381
- const domPathScrollRight = newElement('span', { class: 'adi-path-right' });
382
- this.menuView = newElement('div', { id: 'adi-panel' });
383
- const naviButtons = newElement('div', { class: 'adi-menu-wrap' });
384
- const naviConfig = newElement('a', { class: 'adi-menu-config', title: 'Settings' });
385
- const naviLookup = newElement('a', { class: 'adi-menu-lookup', title: 'Lookup tool' });
386
-
387
- // this.horizSplit = horizSplit;
388
-
389
- this.optsView = drawOptions();
390
-
391
- // put UI together
392
- domViewContent.appendChild(domTree);
393
- this.domView.appendChild(this.menuView);
394
- this.domView.appendChild(domViewContent);
395
-
396
- domPathWrap.appendChild(domPathScrollLeft);
397
- domPathWrap.appendChild(domPathScrollRight);
398
- naviButtons.appendChild(naviLookup);
399
- naviButtons.appendChild(naviConfig);
400
- this.menuView.appendChild(domPathWrap);
401
- this.menuView.appendChild(naviButtons);
402
- // this.uiView.appendChild(this.menuView);
403
- this.uiView.appendChild(this.optsView);
404
- this.uiView.appendChild(this.domView);
405
- if (!this.isInstanceViewer) {
406
- this.attrView = newElement('div', { id: 'adi-attr-view' });
407
- const attrViewContent = newElement('div', { class: 'adi-content' });
408
- this.attrView.appendChild(attrViewContent);
409
- this.uiView.appendChild(this.attrView);
410
- }
411
-
412
- // this.uiView.appendChild(horizSplit);
413
- // wrapper.appendChild(naviWrap);
414
-
415
- // cache UI object and append to the DOM
416
-
417
- rootElement.appendChild(this.uiView);
418
- // document.querySelector('#inspector').appendChild(wrapper);
419
-
420
- this.refreshUI(true);
421
- }
422
-
423
- // Refreshes the global UI
424
- refreshUI(refreshOpts) {
425
- if (this.uiView === null) {
426
- return;
427
- }
428
-
429
- // load options if requested (e.g. before the first UI refresh)
430
- if (refreshOpts) {
431
- this.loadOptions();
432
- }
433
-
434
- // Options view refresh
435
- if (refreshOpts) {
436
- this.optsView.querySelector('[data-opt="transparent"]').checked = this.options.transparent;
437
- this.optsView.querySelector('[data-opt="saving"]').checked = this.options.saving;
438
- this.optsView.querySelector('[data-opt="omitEmptyText"]').checked =
439
- this.options.omitEmptyText;
440
- this.optsView.querySelector('[data-opt="makeVisible"]').checked = this.options.makeVisible;
441
- this.optsView.querySelector('[data-opt="foldText"]').checked = this.options.foldText;
442
- this.optsView.querySelector('[data-opt="nodeTypes-3"]').checked =
443
- this.options.nodeTypes.indexOf(3) !== -1;
444
- this.optsView.querySelector('[data-opt="nodeTypes-8"]').checked =
445
- this.options.nodeTypes.indexOf(8) !== -1;
446
- // this.optsView.querySelector('[data-opt="nodeTypes-1"]').checked = this.options.nodeTypes.indexOf(1) !== -1;
447
- // this.optsView.querySelector('[data-opt="nodeTypes-9"]').checked = this.options.nodeTypes.indexOf(9) !== -1;
448
- }
449
-
450
- // UI appearance refresh
451
- this.uiView.className = this.options.transparent ? 'transparent' : '';
452
- // this.uiView.style.display = this.options.visible ? 'grid' : 'none';
453
- // this.domView.style.height = `${this.options.split}%`;
454
- // this.attrView.style.height = `${100 - this.options.split}%`;
455
- this.domView.querySelector('.adi-content').style.height = `${this.domView.clientHeight}px`;
456
- if (!this.isInstanceViewer) {
457
- this.attrView.querySelector('.adi-content').style.height = `${
458
- this.attrView.clientHeight - this.menuView.clientHeight
459
- }px`;
460
- }
461
-
462
- addClass(this.uiView, this.options.align);
463
- }
464
-
465
- // UI visibility toggle handler
466
- toggleVisibilityUI() {
467
- if (this.uiView === null) {
468
- return;
469
- }
470
-
471
- this.uiView.style.display = this.options.visible ? 'none' : 'block';
472
- this.options.visible = !this.options.visible;
473
- this.saveOptions();
474
- }
475
-
476
- // Renders the attribute view
477
- drawAttrs(elem) {
478
- if (this.isInstanceViewer) {
479
- return;
480
- }
481
- const content = this.attrView.querySelector('.adi-content');
482
- // prepare attributes
483
- content.innerHTML = '';
484
-
485
- const header = document.createElement('header');
486
- header.innerText = 'Attributes';
487
- content.appendChild(header);
488
-
489
- // todo: hook element-def.json in here
490
- /*
491
- if (elem.nodeName.startsWith('FX-')) {
492
- console.log('got a fore element');
493
- const {properties} = elem.constructor;
494
- Object.keys(properties).forEach(propertyName => {
495
-
496
- const property = properties[propertyName];
497
- if (!property || property.hidden) {
498
- return;
499
- }
500
- const row = content.appendChild(newElement('span', {class: 'adi-attr'}));
501
-
502
- switch (property.type) {
503
- case 'referencedNode': {
504
- row.innerHTML = `<label>${propertyName}: <button>${elem[propertyName]?.nodeName}</button></label>`;
505
- const button = row.querySelector('button');
506
- button.addEventListener(
507
- 'click', () => this.handleLookup({detail: {target: elem[propertyName]}})
508
- );
509
- break;
510
- }
511
- case Number: {
512
- row.innerHTML = `<label>${propertyName}: <input type="number" data-attr="${propertyName}" value="${elem[propertyName]}"></label>`;
513
- break;
514
- }
515
- case String: {
516
- if (property.valueSpace) {
517
- row.innerHTML = `<label>${propertyName}: <select data-attr="${propertyName}" value="${elem[propertyName]}">${property.valueSpace.map(value => `<option>${value}</option>`)}</label>`;
518
- break;
519
-
520
- }
521
- row.innerHTML = `<label>${propertyName}: <input type="text" data-attr="${propertyName}" value="${elem[propertyName]}"></label>`;
522
- break;
523
- }
524
- case Boolean: {
525
- if (property.valueSpace) {
526
- row.innerHTML = `<label>${propertyName}: <input type="checkbox" data-attr="${propertyName}" value="${elem[propertyName]}"></input></label>`;
527
-
528
- }
529
- break;
530
- }
531
- case Object: {
532
- try {
533
- row.innerHTML = `<label>${propertyName}: <code>${JSON.stringify(elem[propertyName])}</code></label>`;
534
- } catch (err) {
535
- row.innerHTML = `<label>${propertyName}: <code>Unserializable</code></label>`;
536
- }
537
- break;
538
- }
539
- case Map: {
540
- try {
541
- row.innerHTML = `<label>${propertyName}: <code>${JSON.stringify(elem[propertyName])}</code></label>`;
542
- } catch (err) {
543
- row.innerHTML = `<label>${propertyName}: <code>Unserializable</code></label>`;
544
- }
545
- break;
546
- }
547
- default: {
548
- row.innerHTML = `<label>${propertyName}: Unknown type ${property.type}</label>`;
549
- }
550
- }
551
- });
552
- } else {
553
- */
554
- [...elem.attributes].forEach(attr => {
555
- if (attr.name !== 'style') {
556
- content.appendChild(drawAttrRow(attr.name, attr.value));
557
- }
558
- });
559
- // }
560
- }
561
-
562
- // Handles attribute changes
563
- changeAttribute(e) {
564
- const target = e ? e.target : window.event.srcElement;
565
- const attr = target.getAttribute('data-attr');
566
- const val = target.value;
567
- const elem = this.getSelected();
568
-
569
- // remove attribute if the new value is empty
570
- if (val === '') {
571
- elem.removeAttribute(attr);
572
- } else {
573
- elem.setAttribute(attr, val);
574
- }
575
- }
576
-
577
- // Handles option changes
578
- changeOption(e) {
579
- const target = e ? e.target : window.event.srcElement;
580
- const data = target.getAttribute('data-opt');
581
- const val = target.checked;
582
-
583
- if (data.indexOf('nodeTypes') !== -1) {
584
- const type = parseInt(data.match(/\d+/)[0], 10);
585
-
586
- if (val) {
587
- this.options.nodeTypes.push(type);
588
- } else {
589
- this.options.nodeTypes.splice(this.options.nodeTypes.indexOf(type), 1);
590
- }
591
- } else {
592
- this.options[data] = val;
593
- }
594
- }
595
-
596
- // Key events processing
597
- processKey(e) {
598
- e = e || window.event;
599
- const code = e.keyCode || e.which;
600
-
601
- switch (code) {
602
- case 272: // ctrl + alt + d
603
- this.toggleVisibilityUI();
604
- break;
605
- default:
606
- break;
607
- }
608
- }
609
-
610
- // Vertical splitter resize handler
611
- verticalResize(e) {
612
- if (!this.vertResizing) {
613
- return;
614
- }
615
-
616
- e = e || window.event;
617
- document.documentElement.style.cursor = 'e-resize';
618
- const nWidth = this.options.width + this.xPos - e.clientX;
619
-
620
- if (nWidth >= this.options.minWidth) {
621
- this.options.width = nWidth;
622
- this.xPos = e.clientX;
623
- this.refreshUI();
624
- this.saveOptions();
625
- }
626
- }
627
-
628
- // Horizontal splitter resize handler
629
- horizontalResize(e) {
630
- if (!this.horizResizing) {
631
- return;
632
- }
633
-
634
- e = e || window.event;
635
- document.documentElement.style.cursor = 'n-resize';
636
- const nSplit = Math.floor((e.clientY / this.uiView.clientHeight) * 100);
637
-
638
- if (nSplit >= this.options.minSplit && nSplit <= 100 - this.options.minSplit) {
639
- this.options.split = nSplit;
640
- this.refreshUI();
641
- this.saveOptions();
642
- }
643
- }
644
-
645
- processExecuteAction(e) {
646
- this.refreshUI();
647
- }
648
-
649
- // Handles active element selection
650
- handleActive(e) {
651
- let target = e ? e.detail?.target || e.target : window.event.srcElement;
652
- const active = this.domView.querySelector('.adi-active-node');
653
-
654
- if (active) {
655
- removeClass(active, 'adi-active-node');
656
- }
657
-
658
- // clicked on normal-node or end-node?
659
- if (!target || target.nodeType === Node.DOCUMENT_NODE) return;
660
- if (target && target.classList && target.classList.contains('adi-end-node')) {
661
- target = target.parentNode.querySelector('.adi-normal-node');
662
- }
663
-
664
- this.activeElement = target;
665
- addClass(target, 'adi-active-node');
666
-
667
- /*
668
- e.target.dispatchEvent(
669
- new CustomEvent('handle-active', {
670
- composed: true,
671
- bubbles: true,
672
- detail: {active: this.activeElement, selected: this.getSelected()},
673
- }),
674
- );
675
- */
676
-
677
- // make it visible (scroll)
678
- if (this.options.makeVisible) {
679
- const wrap = this.domView.querySelector('.adi-content');
680
- wrap.scrollIntoView({ block: 'center', behavior: 'instant' });
681
- }
682
- const selected = this.getSelected();
683
- this.drawAttrs(selected);
684
-
685
- if (selected && typeof selected.getModelItem === 'function' && selected.getModelItem()?.node) {
686
- let selectedElement = selected.modelItem.node;
687
- if (selectedElement?.nodeType === Node.ATTRIBUTE_NODE) {
688
- selectedElement = selectedElement.ownerElement;
689
- }
690
- window.document.dispatchEvent(
691
- new CustomEvent('log-active-element', { detail: { target: selectedElement } }),
692
- );
693
- }
694
- // window.document.dispatchEvent(new CustomEvent('log-active-element', {detail: {target: selected}}));
695
- }
696
-
697
- // Highlights an element on page
698
- highlightElement(event) {
699
- // console.log('highlight',e);
700
- let sourceNode = event ? event.target : window.event.srcElement;
701
-
702
- if (sourceNode.classList.contains('adi-end-node')) {
703
- sourceNode = sourceNode.parentNode.querySelector('.adi-normal-node');
704
- }
705
-
706
- const inspectorNode = this.sourceNodeByInspectorNodeLookup.get(sourceNode);
707
-
708
- if (!inspectorNode || inspectorNode.ownerDocument !== window.document) {
709
- // Not in HTML: ignore
710
- return;
711
- }
712
-
713
- if (inspectorNode) {
714
- if (event.type === 'mouseover') {
715
- this.styleBackup = inspectorNode.getAttribute('style') || '';
716
- inspectorNode.setAttribute('style', `outline: 2px solid blue; ${this.styleBackup}`);
717
- } else if (this.styleBackup === '') {
718
- inspectorNode.removeAttribute('style');
719
- } else {
720
- inspectorNode.setAttribute('style', this.styleBackup);
721
- }
722
- }
723
- }
724
-
725
- // Handles element lookup on page
726
- handleLookup(e) {
727
- const target = e ? e.detail?.target || e.target : window.event.srcElement;
728
-
729
- if (!this.document.contains(target)) {
730
- // Targetted at somewhere else!!!
731
- return;
732
- }
733
- if (target.nodeType === Node.DOCUMENT_NODE) {
734
- // Targetted at the document node. Nothing to highlight
735
- return;
736
- }
737
-
738
- if (target.className.indexOf('adi-menu-lookup') !== -1) {
739
- // enable/disable interactive lookup
740
- if (this.elemLookup) {
741
- removeClass(target, 'adi-active');
742
- this.elemLookup = false;
743
- this.removeEvent(document.body, 'mouseover', this.handleLookup, true);
744
- this.removeEvent(document.body, 'mouseout', this.handleLookup, true);
745
- this.removeEvent(document.body, 'click', this.handleLookup, true);
746
- return;
747
- }
748
- addClass(target, 'adi-active');
749
- this.elemLookup = true;
750
- this.addEventDelegate(
751
- document.body,
752
- 'mouseover',
753
- this.handleLookup,
754
- false,
755
- '*',
756
- true,
757
- 'adi-wrapper',
758
- );
759
- this.addEventDelegate(
760
- document.body,
761
- 'mouseout',
762
- this.handleLookup,
763
- false,
764
- '*',
765
- true,
766
- 'adi-wrapper',
767
- );
768
- this.addEventDelegate(
769
- document.body,
770
- 'click',
771
- this.handleLookup,
772
- false,
773
- '*',
774
- true,
775
- 'adi-wrapper',
776
- );
777
- return;
778
- }
779
- // handle lookup events
780
- if (e.type === 'mouseover') {
781
- this.styleBackup = target.getAttribute('style') || '';
782
- target.setAttribute('style', `outline: 1px dashed red; ${this.styleBackup}`);
783
- return;
784
- }
785
- if (e.type === 'mouseout') {
786
- target.setAttribute('style', this.styleBackup);
787
- return;
788
- }
789
- this.elemLookup = false;
790
- removeClass(this.menuView.querySelector('.adi-menu-lookup'), 'adi-active');
791
- target.setAttribute('style', this.styleBackup);
792
- this.removeEvent(document.body, 'mouseover', this.handleLookup, true);
793
- this.removeEvent(document.body, 'mouseout', this.handleLookup, true);
794
- this.removeEvent(document.body, 'click', this.handleLookup, true);
795
- pauseEvent(e);
796
-
797
- // find corresponding node in the DOM view
798
- const active = this.sourceNodeByInspectorNodeLookup.get(target);
799
-
800
- // activate it
801
- if (!active) return;
802
- if (active) {
803
- active.click();
804
- }
805
-
806
- // open the whole path in DOM view
807
- if (!active.parentNode) return;
808
- let node = active.parentNode;
809
- let tmp;
810
-
811
- if (node.querySelector('ul')) {
812
- node.querySelector('ul').setAttribute('data-open', 'true');
813
- }
814
- while (node !== this.domView.querySelector('.adi-content')) {
815
- if (node.className.indexOf('adi-node') !== -1) {
816
- tmp = node.querySelector('.adi-trigger');
817
- if (tmp) {
818
- removeClass(tmp, 'closed');
819
- addClass(tmp, 'opened');
820
- }
821
-
822
- node = node.parentNode; // ul node
823
- node.setAttribute('data-open', 'true');
824
- }
825
-
826
- node = node.parentNode;
827
- }
828
-
829
- // make it visible (scroll)
830
- if (this.options.makeVisible) {
831
- active.scrollIntoView({ behavior: 'instant', block: 'nearest', inline: 'nearest' });
832
- }
833
- target.scrollIntoView({ behavior: 'instant', block: 'nearest', inline: 'nearest' });
834
- }
835
-
836
- // Simple cross-browser event handler that enables simple event delegation.
837
- // Note that the selector must be a string and no nesting is supported.
838
- // Selector is expected to be in one of formats listed below and works for all children
839
- // in the particular element.
840
- // Store parameter enables storing the reference to custom event handler.
841
- // Exclude parameter will exclude the particular element and all of its children, this works
842
- // only for id selectors.
843
- // Selector formats: tag name ("div"), class name (".my-class"), id ("#my-id") and any ("*").
844
-
845
- addEventDelegate(elem, evt, fn, capture, selector, store, exclude) {
846
- // custom event handler is registered
847
- const handler = e => {
848
- // check if target corresponds to the selector
849
- const target = e ? e.target : window.event.srcElement;
850
- const sel = selector.substr(1);
851
- let delegate = false;
852
-
853
- if (exclude) {
854
- let node = target;
855
-
856
- while (node !== document) {
857
- if (node.id === exclude) {
858
- return;
859
- }
860
-
861
- node = node.parentNode;
862
- }
863
- }
864
-
865
- // should the event be delegated?
866
- if (selector.indexOf('#') === 0) {
867
- // ID
868
- delegate = target.id === sel;
869
- } else if (selector.indexOf('.') === 0) {
870
- // class
871
- delegate = target.className.indexOf(sel) !== -1;
872
- } else if (selector === '*') {
873
- // any
874
- delegate = true;
875
- } else {
876
- // tag name
877
- delegate = target.nodeName.toLowerCase() === selector;
878
- }
879
-
880
- // delegate the event handling
881
- if (delegate) {
882
- fn(e);
883
- }
884
- };
885
- // save the reference
886
- if (store) {
887
- this.delegatedEvents.push({
888
- handle: handler,
889
- elem,
890
- fn,
891
- evt,
892
- });
893
- }
894
-
895
- elem.addEventListener(evt, handler, capture);
896
- }
897
-
898
- // Simple cross-browser event removing
899
- removeEvent(elem, evt, fn, wasDelegated) {
900
- if (typeof elem !== 'object') {
901
- throw new Error(`addEvent: Expected argument elem of type object, ${typeof elem} given.`);
902
- }
903
-
904
- // try to find stored delegated event
905
- let stored = null;
906
- if (wasDelegated) {
907
- for (let i = 0, len = this.delegatedEvents.length; i < len; i += 1) {
908
- stored = this.delegatedEvents[i];
909
- if (stored.elem === elem && stored.evt === evt && stored.fn === fn) {
910
- fn = stored.handle;
911
- this.delegatedEvents.splice(i, 1);
912
- break;
913
- }
914
- }
915
- }
916
-
917
- // elem.detachEvent(`on${evt}`, fn);
918
- }
919
-
920
- // Event registration
921
- registerEvents() {
922
- // events for splitters
923
- /*
924
- this.horizSplit.addEventListener(
925
- 'mousedown',
926
- e => {
927
- e = e || window.event;
928
- pauseEvent(e);
929
- this.horizResizing = true;
930
- },
931
- false,
932
- );
933
- */
934
-
935
- const redrawUi = () => {
936
- if (this.instanceId !== '#document') {
937
- const instance = window.document.querySelector(`#${this.instanceId}`);
938
- this.document = instance.getInstanceData();
939
- }
940
- this.drawDOM(this.document, this.domView.querySelector('.adi-tree-view'), true);
941
- };
942
-
943
- // Update UI when something with instances changed
944
- document.addEventListener('instance-loaded', redrawUi);
945
- // Update UI when some value changes
946
- document.addEventListener('value-changed', redrawUi);
947
- // Update UI when we're done loading and all repeats are done
948
- document.addEventListener('ready', redrawUi);
949
-
950
- document.addEventListener(
951
- 'mouseup',
952
- () => {
953
- document.documentElement.style.cursor = 'default';
954
- this.vertResizing = false;
955
- this.horizResizing = false;
956
- },
957
- false,
958
- );
959
-
960
- document.addEventListener('mousemove', event => this.verticalResize(event), false);
961
- document.addEventListener('mousemove', event => this.horizontalResize(event), false);
962
- // window resize
963
- window.addEventListener('resize', event => this.refreshUI(event), false);
964
-
965
- // keypress events
966
- document.addEventListener('keypress', event => this.processKey(event), false);
967
-
968
- // fore action events
969
- document.addEventListener('log-active-element', event => this.handleLookup(event), false);
970
-
971
- // Dom view folding handler
972
- const handleFolding = e => {
973
- const target = e ? e.target : window.event.srcElement;
974
- const ul = target.parentNode.querySelector('ul');
975
-
976
- if (ul.getAttribute('data-open') === 'true') {
977
- removeClass(target, 'opened');
978
- addClass(target, 'closed');
979
- ul.setAttribute('data-open', 'false');
980
- } else {
981
- removeClass(target, 'closed');
982
- addClass(target, 'opened');
983
- ul.setAttribute('data-open', 'true');
984
- }
985
- };
986
-
987
- // dom tree view folding
988
- this.addEventDelegate(this.domView, 'click', handleFolding, false, '.adi-trigger');
989
-
990
- // active element
991
- this.addEventDelegate(
992
- this.domView,
993
- 'click',
994
- event => this.handleActive(event),
995
- false,
996
- '.adi-normal-node',
997
- );
998
- this.addEventDelegate(
999
- this.domView,
1000
- 'click',
1001
- event => this.handleActive(event),
1002
- false,
1003
- '.adi-end-node',
1004
- );
1005
-
1006
- // matching tag highlighting
1007
- this.addEventDelegate(
1008
- this.domView,
1009
- 'mouseover',
1010
- e => {
1011
- const target = e ? e.target : window.event.srcElement;
1012
- addClass(target.parentNode.querySelector('.adi-normal-node'), 'hover');
1013
- },
1014
- false,
1015
- '.adi-end-node',
1016
- );
1017
- this.addEventDelegate(
1018
- this.domView,
1019
- 'mouseout',
1020
- e => {
1021
- const target = e ? e.target : window.event.srcElement;
1022
- removeClass(target.parentNode.querySelector('.adi-normal-node'), 'hover');
1023
- },
1024
- false,
1025
- '.adi-end-node',
1026
- );
1027
-
1028
- // page element highlighting
1029
- this.addEventDelegate(
1030
- this.domView,
1031
- 'mouseover',
1032
- event => this.highlightElement(event),
1033
- false,
1034
- '.adi-end-node',
1035
- );
1036
- this.addEventDelegate(
1037
- this.domView,
1038
- 'mouseover',
1039
- event => this.highlightElement(event),
1040
- false,
1041
- '.adi-normal-node',
1042
- );
1043
- this.addEventDelegate(
1044
- this.domView,
1045
- 'mouseout',
1046
- event => this.highlightElement(event),
1047
- false,
1048
- '.adi-end-node',
1049
- );
1050
- this.addEventDelegate(
1051
- this.domView,
1052
- 'mouseout',
1053
- event => this.highlightElement(event),
1054
- false,
1055
- '.adi-normal-node',
1056
- );
1057
-
1058
- // element lookup
1059
- this.menuView
1060
- .querySelector('.adi-menu-lookup')
1061
- .addEventListener('click', event => this.handleLookup(event), false);
1062
-
1063
- document.addEventListener('handle-active', e => {
1064
- if (e.detail.selected === this.getSelected()) {
1065
- // We caused this. ignore
1066
- return;
1067
- }
1068
- const { selected } = e.detail;
1069
- const target = this.sourceNodeByInspectorNodeLookup.get(selected);
1070
- // make it visible (scroll)
1071
- if (this.options.makeVisible) {
1072
- const wrap = this.domView.querySelector('.adi-content');
1073
- if (target.offsetTop >= wrap.clientHeight || target.offsetTop <= wrap.scrollTop) {
1074
- wrap.scrollTop = target.offsetTop - Math.floor(wrap.clientHeight / 2);
1075
- }
1076
- }
1077
-
1078
- this.drawAttrs(this.getSelected());
1079
- });
1080
-
1081
- document.addEventListener('execute-action', e => this.processExecuteAction(event), {
1082
- capture: true,
1083
- });
1084
-
1085
- // options events
1086
- this.addEventDelegate(
1087
- this.optsView,
1088
- 'change',
1089
- event => this.changeOption(event),
1090
- false,
1091
- 'input',
1092
- );
1093
- this.addEventDelegate(
1094
- this.optsView,
1095
- 'click',
1096
- event => this.toggleOptions(event),
1097
- false,
1098
- '.adi-opt-close',
1099
- );
1100
- this.menuView
1101
- .querySelector('.adi-menu-config')
1102
- .addEventListener('click', event => this.toggleOptions(event), false);
1103
-
1104
- // attributes events
1105
- if (!this.isInstanceViewer) {
1106
- this.addEventDelegate(this.attrView, 'change', this.changeAttribute, false, 'input');
1107
- }
1108
- }
1109
- }
1110
-
1111
- export default ADI;