@node-projects/web-component-designer 0.1.108 → 0.1.110
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/assets/images/display/block.svg +1 -0
- package/assets/images/display/inline.svg +1 -0
- package/dist/elements/controls/ImageButtonListSelector.d.ts +9 -6
- package/dist/elements/controls/ImageButtonListSelector.js +25 -6
- package/dist/elements/item/DesignItem.d.ts +2 -0
- package/dist/elements/item/DesignItem.js +55 -8
- package/dist/elements/item/IDesignItem.d.ts +2 -0
- package/dist/elements/services/DefaultServiceBootstrap.js +2 -0
- package/dist/elements/services/dragDropService/DragDropService.js +3 -1
- package/dist/elements/services/placementService/DefaultPlacementService.d.ts +1 -1
- package/dist/elements/services/placementService/DefaultPlacementService.js +3 -1
- package/dist/elements/services/placementService/FlexBoxPlacementService.d.ts +1 -1
- package/dist/elements/services/placementService/FlexBoxPlacementService.js +5 -2
- package/dist/elements/services/placementService/GridPlacementService.d.ts +1 -1
- package/dist/elements/services/placementService/GridPlacementService.js +5 -2
- package/dist/elements/services/placementService/IPlacementService.d.ts +1 -1
- package/dist/elements/services/propertiesService/PropertyGroupsService.js +2 -2
- package/dist/elements/services/propertiesService/propertyEditors/ImageButtonListPropertyEditor.js +1 -1
- package/dist/elements/services/stylesheetService/AbstractStylesheetService.d.ts +3 -0
- package/dist/elements/services/stylesheetService/AbstractStylesheetService.js +23 -0
- package/dist/elements/services/stylesheetService/IStylesheetService.d.ts +4 -2
- package/dist/elements/widgets/designerView/designerCanvas.js +8 -2
- package/dist/elements/widgets/designerView/extensions/AbstractExtension.d.ts +1 -1
- package/dist/elements/widgets/designerView/extensions/AbstractExtension.js +23 -2
- package/dist/elements/widgets/designerView/extensions/BasicDisplayToolbarExtension.d.ts +20 -0
- package/dist/elements/widgets/designerView/extensions/BasicDisplayToolbarExtension.js +68 -0
- package/dist/elements/widgets/designerView/extensions/BasicStackedToolbarExtension.d.ts +21 -0
- package/dist/elements/widgets/designerView/extensions/BasicStackedToolbarExtension.js +81 -0
- package/dist/elements/widgets/designerView/extensions/BlockToolbarExtension.d.ts +12 -0
- package/dist/elements/widgets/designerView/extensions/BlockToolbarExtension.js +40 -0
- package/dist/elements/widgets/designerView/extensions/ExtensionManager.js +1 -0
- package/dist/elements/widgets/designerView/extensions/block/BlockToolbarExtension.d.ts +4 -7
- package/dist/elements/widgets/designerView/extensions/block/BlockToolbarExtension.js +7 -26
- package/dist/elements/widgets/designerView/extensions/block/BlockToolbarExtensionProvider.js +4 -2
- package/dist/elements/widgets/designerView/extensions/flex/FlexToolbarExtension.d.ts +5 -6
- package/dist/elements/widgets/designerView/extensions/flex/FlexToolbarExtension.js +63 -21
- package/dist/elements/widgets/designerView/extensions/flex/FlexToolbarExtensionProvider.js +4 -2
- package/dist/elements/widgets/designerView/extensions/grid/GridChildToolbarExtension.d.ts +3 -6
- package/dist/elements/widgets/designerView/extensions/grid/GridChildToolbarExtension.js +45 -56
- package/dist/elements/widgets/designerView/extensions/grid/GridChildToolbarExtensionProvider.d.ts +0 -1
- package/dist/elements/widgets/designerView/extensions/grid/GridChildToolbarExtensionProvider.js +5 -7
- package/dist/elements/widgets/designerView/extensions/grid/GridToolbarExtension.d.ts +3 -6
- package/dist/elements/widgets/designerView/extensions/grid/GridToolbarExtension.js +50 -29
- package/dist/elements/widgets/designerView/extensions/grid/GridToolbarExtensionProvider.js +4 -2
- package/dist/elements/widgets/designerView/overlayLayerView.js +16 -1
- package/dist/elements/widgets/designerView/tools/PointerTool.d.ts +1 -0
- package/dist/elements/widgets/designerView/tools/PointerTool.js +6 -5
- package/dist/elements/widgets/propertyGrid/PropertyGridWithHeader.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +2 -2
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { html } from "@node-projects/base-custom-webcomponent";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import { BasicStackedToolbarExtension } from "../BasicStackedToolbarExtension.js";
|
|
3
|
+
import { assetsPath } from "../../../../../Constants.js";
|
|
4
|
+
export class GridToolbarExtension extends BasicStackedToolbarExtension {
|
|
4
5
|
static template = html `
|
|
5
6
|
<div style="height: 100%; width: 100%;">
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<option>flex</option>
|
|
9
|
-
<option selected>grid</option>
|
|
10
|
-
</select>
|
|
11
|
-
<select id="gridType" style="pointer-events: all; height: 24px; width: 60px; padding: 0;">
|
|
7
|
+
${BasicStackedToolbarExtension.basicTemplate}
|
|
8
|
+
<select title="display" id="gridType" style="pointer-events: all; height: 24px; width: 60px; padding: 0; margin-right: 10px">
|
|
12
9
|
<option>1x1</option>
|
|
13
10
|
<option>1x16</option>
|
|
14
11
|
<option>2x8</option>
|
|
@@ -17,26 +14,51 @@ export class GridToolbarExtension extends AbstractExtension {
|
|
|
17
14
|
<option>16x1</option>
|
|
18
15
|
<option>custom</option>
|
|
19
16
|
</select>
|
|
17
|
+
<node-projects-image-button-list-selector property="align-content" no-value-in-header id="align-content">
|
|
18
|
+
<img data-value="start" src="${assetsPath}images/chromeDevtools/align-content-flex-start-icon.svg">
|
|
19
|
+
<img data-value="center" src="${assetsPath}images/chromeDevtools/align-content-center-icon.svg">
|
|
20
|
+
<img data-value="end" src="${assetsPath}images/chromeDevtools/align-content-flex-end-icon.svg">
|
|
21
|
+
<img data-value="space-around" src="${assetsPath}images/chromeDevtools/align-content-space-around-icon.svg">
|
|
22
|
+
<img data-value="space-evenly" src="${assetsPath}images/chromeDevtools/align-content-space-evenly-icon.svg">
|
|
23
|
+
<img data-value="space-between" src="${assetsPath}images/chromeDevtools/align-content-space-between-icon.svg">
|
|
24
|
+
<img data-value="stretch" src="${assetsPath}images/chromeDevtools/align-content-stretch-icon.svg">
|
|
25
|
+
</node-projects-image-button-list-selector>
|
|
26
|
+
<node-projects-image-button-list-selector property="justify-content" no-value-in-header id="justify-content">
|
|
27
|
+
<img data-value="start" src="${assetsPath}images/chromeDevtools/justify-content-start-icon.svg">
|
|
28
|
+
<img data-value="center" src="${assetsPath}images/chromeDevtools/justify-content-center-icon.svg">
|
|
29
|
+
<img data-value="end" src="${assetsPath}images/chromeDevtools/justify-content-end-icon.svg">
|
|
30
|
+
<img data-value="space-around" src="${assetsPath}images/chromeDevtools/justify-content-space-around-icon.svg">
|
|
31
|
+
<img data-value="space-evenly" src="${assetsPath}images/chromeDevtools/justify-content-space-evenly-icon.svg">
|
|
32
|
+
<img data-value="space-between" src="${assetsPath}images/chromeDevtools/justify-content-space-between-icon.svg">
|
|
33
|
+
</node-projects-image-button-list-selector>
|
|
34
|
+
<node-projects-image-button-list-selector property="align-items" no-value-in-header id="align-items">
|
|
35
|
+
<img data-value="start" src="${assetsPath}images/chromeDevtools/align-items-start-icon.svg">
|
|
36
|
+
<img data-value="center" src="${assetsPath}images/chromeDevtools/align-items-center-icon.svg">
|
|
37
|
+
<img data-value="end" src="${assetsPath}images/chromeDevtools/align-items-end-icon.svg">
|
|
38
|
+
<img data-value="stretch" src="${assetsPath}images/chromeDevtools/align-items-stretch-icon.svg">
|
|
39
|
+
<img data-value="space-evenly" src="${assetsPath}images/chromeDevtools/align-items-baseline-icon.svg">
|
|
40
|
+
</node-projects-image-button-list-selector>
|
|
41
|
+
<node-projects-image-button-list-selector property="justify-items" no-value-in-header id="justify-items">
|
|
42
|
+
<img data-value="start" src="${assetsPath}images/chromeDevtools/justify-items-start-icon.svg">
|
|
43
|
+
<img data-value="center" src="${assetsPath}images/chromeDevtools/justify-items-center-icon.svg">
|
|
44
|
+
<img data-value="end" src="${assetsPath}images/chromeDevtools/justify-items-end-icon.svg">
|
|
45
|
+
<img data-value="stretch" src="${assetsPath}images/chromeDevtools/justify-items-stretch-icon.svg">
|
|
46
|
+
</node-projects-image-button-list-selector>
|
|
20
47
|
</div>
|
|
21
48
|
`;
|
|
22
|
-
_toolbar;
|
|
23
49
|
constructor(extensionManager, designerView, extendedItem) {
|
|
24
50
|
super(extensionManager, designerView, extendedItem);
|
|
51
|
+
this._size.width = 560;
|
|
25
52
|
}
|
|
26
53
|
extend(cache, event) {
|
|
54
|
+
super.extend(cache, event);
|
|
27
55
|
const style = getComputedStyle(this.extendedItem.element);
|
|
28
|
-
this._toolbar = this.createToolbar(GridToolbarExtension.template, 200, 30);
|
|
29
|
-
const displayTypeEl = this._toolbar.getById('displayType');
|
|
30
|
-
displayTypeEl.onchange = () => {
|
|
31
|
-
this.extendedItem.updateStyleInSheetOrLocal('display', displayTypeEl.value);
|
|
32
|
-
this.extensionManager.reapplyAllAppliedExtentions([this.extendedItem]);
|
|
33
|
-
};
|
|
34
56
|
const gridTypeEl = this._toolbar.getById('gridType');
|
|
35
57
|
let op = document.createElement('option');
|
|
36
58
|
op.innerText = style.gridTemplateColumns.split(' ').length + 'x' + style.gridTemplateRows.split(' ').length;
|
|
37
59
|
gridTypeEl.insertAdjacentElement('afterbegin', op);
|
|
38
60
|
gridTypeEl.selectedIndex = 0;
|
|
39
|
-
gridTypeEl.onchange = () => {
|
|
61
|
+
gridTypeEl.onchange = async () => {
|
|
40
62
|
if (gridTypeEl.value == 'custom') {
|
|
41
63
|
const columns = prompt("Number of columns?", '4');
|
|
42
64
|
if (!columns)
|
|
@@ -44,24 +66,23 @@ export class GridToolbarExtension extends AbstractExtension {
|
|
|
44
66
|
const rows = prompt("Number of rows?", '4');
|
|
45
67
|
if (!rows)
|
|
46
68
|
return;
|
|
47
|
-
this.extendedItem.
|
|
48
|
-
this.extendedItem.
|
|
69
|
+
const cg = this.extendedItem.openGroup('change grid type');
|
|
70
|
+
await this.extendedItem.updateStyleInSheetOrLocalAsync('grid-template-columns', '1fr '.repeat(parseInt(columns)).trim());
|
|
71
|
+
await this.extendedItem.updateStyleInSheetOrLocalAsync('grid-template-rows', '1fr '.repeat(parseInt(rows)).trim());
|
|
72
|
+
cg.commit();
|
|
49
73
|
}
|
|
50
74
|
else {
|
|
51
75
|
const parts = gridTypeEl.value.split('x');
|
|
52
|
-
this.extendedItem.
|
|
53
|
-
this.extendedItem.
|
|
76
|
+
const cg = this.extendedItem.openGroup('change grid type');
|
|
77
|
+
await this.extendedItem.updateStyleInSheetOrLocalAsync('grid-template-columns', '1fr '.repeat(parseInt(parts[0])).trim());
|
|
78
|
+
await this.extendedItem.updateStyleInSheetOrLocalAsync('grid-template-rows', '1fr '.repeat(parseInt(parts[1])).trim());
|
|
79
|
+
cg.commit();
|
|
54
80
|
}
|
|
55
81
|
};
|
|
82
|
+
this._addStyleButton('align-content');
|
|
83
|
+
this._addStyleButton('justify-content');
|
|
84
|
+
this._addStyleButton('align-items');
|
|
85
|
+
this._addStyleButton('justify-items');
|
|
56
86
|
this.refresh(cache, event);
|
|
57
87
|
}
|
|
58
|
-
refresh(cache, event) {
|
|
59
|
-
if (event) {
|
|
60
|
-
const pos = this.designerCanvas.getNormalizedEventCoordinates(event);
|
|
61
|
-
this._toolbar.updatePosition({ x: (pos.x - (16 / this.designerCanvas.zoomFactor)), y: (pos.y - (44 / this.designerCanvas.zoomFactor)) });
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
dispose() {
|
|
65
|
-
this._removeAllOverlays();
|
|
66
|
-
}
|
|
67
88
|
}
|
|
@@ -2,8 +2,10 @@ import { GridToolbarExtension } from './GridToolbarExtension.js';
|
|
|
2
2
|
import { NodeType } from '../../../../item/NodeType.js';
|
|
3
3
|
export class GridToolbarExtensionProvider {
|
|
4
4
|
shouldExtend(extensionManager, designerView, designItem) {
|
|
5
|
-
if (designItem.nodeType === NodeType.Element
|
|
6
|
-
|
|
5
|
+
if (designItem.nodeType === NodeType.Element) {
|
|
6
|
+
const d = getComputedStyle(designItem.element).display;
|
|
7
|
+
return d === 'grid' || d === 'inline-grid';
|
|
8
|
+
}
|
|
7
9
|
return false;
|
|
8
10
|
}
|
|
9
11
|
getExtension(extensionManager, designerView, designItem) {
|
|
@@ -23,7 +23,22 @@ export class OverlayLayerView extends BaseCustomWebComponentConstructorAppend {
|
|
|
23
23
|
.svg-path-line { stroke: #3899ec; stroke-dasharray: 2; }
|
|
24
24
|
.svg-draw-new-element { stroke: black; fill: transparent; stroke-width: 1; }
|
|
25
25
|
.svg-toolbar-container { overflow: visible }
|
|
26
|
-
.svg-toolbar-container div { padding: 5px; display: flex; gap: 2px; background: white; border-radius: 4px; box-shadow: 0 2px 10px 0 rgba(19,23,32,.2); align-items: center; }
|
|
26
|
+
.svg-toolbar-container > div { padding: 5px; display: flex; gap: 2px; background: white; border-radius: 4px; box-shadow: 0 2px 10px 0 rgba(19,23,32,.2); align-items: center; }
|
|
27
|
+
|
|
28
|
+
node-projects-image-button-list-selector img {
|
|
29
|
+
height: 16px;
|
|
30
|
+
border: 1px solid black;
|
|
31
|
+
border-radius: 4px;
|
|
32
|
+
box-sizing: border-box;
|
|
33
|
+
pointer-events: auto;
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
}
|
|
36
|
+
node-projects-image-button-list-selector img:hover {
|
|
37
|
+
background: lightgray;
|
|
38
|
+
}
|
|
39
|
+
node-projects-image-button-list-selector img:active {
|
|
40
|
+
translate: 1px 1px
|
|
41
|
+
}
|
|
27
42
|
`;
|
|
28
43
|
static is = 'node-projects-overlay-layer-view';
|
|
29
44
|
_serviceContainer;
|
|
@@ -6,6 +6,7 @@ import { ITool } from './ITool.js';
|
|
|
6
6
|
import { ServiceContainer } from "../../../services/ServiceContainer.js";
|
|
7
7
|
export declare class PointerTool implements ITool {
|
|
8
8
|
cursor: string;
|
|
9
|
+
private _minMoveOffset;
|
|
9
10
|
private _movedSinceStartedAction;
|
|
10
11
|
private _initialPoint;
|
|
11
12
|
private _actionType?;
|
|
@@ -5,6 +5,7 @@ import { ExtensionType } from '../extensions/ExtensionType.js';
|
|
|
5
5
|
import { NamedTools } from './NamedTools.js';
|
|
6
6
|
export class PointerTool {
|
|
7
7
|
cursor = 'default';
|
|
8
|
+
_minMoveOffset = 5;
|
|
8
9
|
_movedSinceStartedAction = false;
|
|
9
10
|
_initialPoint;
|
|
10
11
|
_actionType;
|
|
@@ -120,7 +121,7 @@ export class PointerTool {
|
|
|
120
121
|
}
|
|
121
122
|
}
|
|
122
123
|
if (event.type === EventNames.PointerMove) {
|
|
123
|
-
this._movedSinceStartedAction = this._movedSinceStartedAction || currentPoint.x
|
|
124
|
+
this._movedSinceStartedAction = this._movedSinceStartedAction || Math.abs(currentPoint.x - this._initialPoint.x) > this._minMoveOffset || Math.abs(currentPoint.y - this._initialPoint.y) > this._minMoveOffset;
|
|
124
125
|
if (this._actionType == PointerActionType.DrawSelection)
|
|
125
126
|
this._actionType = PointerActionType.DrawingSelection;
|
|
126
127
|
}
|
|
@@ -236,7 +237,7 @@ export class PointerTool {
|
|
|
236
237
|
}
|
|
237
238
|
if (this._movedSinceStartedAction) {
|
|
238
239
|
const containerStyle = getComputedStyle(this._actionStartedDesignItem.parent.element);
|
|
239
|
-
const currentContainerService = designerCanvas.serviceContainer.getLastServiceWhere('containerService', x => x.serviceForContainer(this._actionStartedDesignItem.parent, containerStyle));
|
|
240
|
+
const currentContainerService = designerCanvas.serviceContainer.getLastServiceWhere('containerService', x => x.serviceForContainer(this._actionStartedDesignItem.parent, containerStyle, this._actionStartedDesignItem));
|
|
240
241
|
if (currentContainerService) {
|
|
241
242
|
const dragItem = this._actionStartedDesignItem.parent;
|
|
242
243
|
if (this._dragParentExtensionItem != dragItem) {
|
|
@@ -323,7 +324,7 @@ export class PointerTool {
|
|
|
323
324
|
}
|
|
324
325
|
if (this._movedSinceStartedAction) {
|
|
325
326
|
const containerStyle = getComputedStyle(this._actionStartedDesignItem.parent.element);
|
|
326
|
-
let containerService = designerCanvas.serviceContainer.getLastServiceWhere('containerService', x => x.serviceForContainer(this._actionStartedDesignItem.parent, containerStyle));
|
|
327
|
+
let containerService = designerCanvas.serviceContainer.getLastServiceWhere('containerService', x => x.serviceForContainer(this._actionStartedDesignItem.parent, containerStyle, this._actionStartedDesignItem));
|
|
327
328
|
const cp = { x: currentPoint.x - this._moveItemsOffset.x, y: currentPoint.y - this._moveItemsOffset.y };
|
|
328
329
|
if (containerService) {
|
|
329
330
|
if (!this._changeGroup)
|
|
@@ -399,7 +400,7 @@ export class PointerTool {
|
|
|
399
400
|
else if (e == designerCanvas.rootDesignItem.element) {
|
|
400
401
|
newContainerElementDesignItem = designerCanvas.rootDesignItem;
|
|
401
402
|
const containerStyle = getComputedStyle(newContainerElementDesignItem.element);
|
|
402
|
-
newContainerService = designerCanvas.serviceContainer.getLastServiceWhere('containerService', x => x.serviceForContainer(newContainerElementDesignItem, containerStyle));
|
|
403
|
+
newContainerService = designerCanvas.serviceContainer.getLastServiceWhere('containerService', x => x.serviceForContainer(newContainerElementDesignItem, containerStyle, designItem));
|
|
403
404
|
break;
|
|
404
405
|
}
|
|
405
406
|
else if (false) {
|
|
@@ -408,7 +409,7 @@ export class PointerTool {
|
|
|
408
409
|
else {
|
|
409
410
|
newContainerElementDesignItem = DesignItem.GetOrCreateDesignItem(e, e, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);
|
|
410
411
|
const containerStyle = getComputedStyle(newContainerElementDesignItem.element);
|
|
411
|
-
newContainerService = designerCanvas.serviceContainer.getLastServiceWhere('containerService', x => x.serviceForContainer(newContainerElementDesignItem, containerStyle));
|
|
412
|
+
newContainerService = designerCanvas.serviceContainer.getLastServiceWhere('containerService', x => x.serviceForContainer(newContainerElementDesignItem, containerStyle, designItem));
|
|
412
413
|
if (newContainerService) {
|
|
413
414
|
if (newContainerService.canEnter(newContainerElementDesignItem, designItems)) {
|
|
414
415
|
break;
|
|
@@ -140,7 +140,7 @@ export class PropertyGridWithHeader extends BaseCustomWebComponentLazyAppend {
|
|
|
140
140
|
this._selectionChangedHandler = this._instanceServiceContainer.selectionService.onSelectionChanged.on(async (e) => {
|
|
141
141
|
this._pg.instanceServiceContainer = value;
|
|
142
142
|
await sleep(20); // delay assignment a little bit, so onblur above could still set the value.
|
|
143
|
-
if (this._instanceServiceContainer.selectionService
|
|
143
|
+
if (this._instanceServiceContainer.selectionService?.primarySelection?.isRootItem) {
|
|
144
144
|
this._configButton.style.display = 'none';
|
|
145
145
|
this._id.value = '';
|
|
146
146
|
this._content.value = '';
|
package/dist/index.d.ts
CHANGED
|
@@ -196,6 +196,7 @@ export * from "./elements/widgets/designerView/tools/ZoomTool.js";
|
|
|
196
196
|
export type { IDesignerExtension } from "./elements/widgets/designerView/extensions/IDesignerExtension.js";
|
|
197
197
|
export type { IDesignerExtensionProvider } from "./elements/widgets/designerView/extensions/IDesignerExtensionProvider.js";
|
|
198
198
|
export type { IExtensionManager } from "./elements/widgets/designerView/extensions/IExtensionManger.js";
|
|
199
|
+
export * from "./elements/widgets/designerView/extensions/BasicStackedToolbarExtension.js";
|
|
199
200
|
export * from "./elements/widgets/designerView/extensions/OverlayLayer.js";
|
|
200
201
|
export * from "./elements/widgets/designerView/extensions/ExtensionType.js";
|
|
201
202
|
export * from "./elements/widgets/designerView/extensions/AbstractExtension.js";
|
package/dist/index.js
CHANGED
|
@@ -137,6 +137,7 @@ export * from "./elements/widgets/designerView/tools/PointerTool.js";
|
|
|
137
137
|
export * from "./elements/widgets/designerView/tools/RectangleSelectorTool.js";
|
|
138
138
|
export * from "./elements/widgets/designerView/tools/TextTool.js";
|
|
139
139
|
export * from "./elements/widgets/designerView/tools/ZoomTool.js";
|
|
140
|
+
export * from "./elements/widgets/designerView/extensions/BasicStackedToolbarExtension.js";
|
|
140
141
|
export * from "./elements/widgets/designerView/extensions/OverlayLayer.js";
|
|
141
142
|
export * from "./elements/widgets/designerView/extensions/ExtensionType.js";
|
|
142
143
|
export * from "./elements/widgets/designerView/extensions/AbstractExtension.js";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "A
|
|
2
|
+
"description": "A WYSIWYG designer webcomponent for html components",
|
|
3
3
|
"name": "@node-projects/web-component-designer",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.110",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"author": "jochen.kuehner@gmx.de",
|