@node-projects/web-component-designer 0.0.88 → 0.0.89

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 (24) hide show
  1. package/dist/elements/controls/SimpleSplitView.js +1 -1
  2. package/dist/elements/documentContainer.js +2 -0
  3. package/dist/elements/services/undoService/UndoService.js +2 -2
  4. package/dist/elements/widgets/designerView/IDesignerCanvas.d.ts +3 -1
  5. package/dist/elements/widgets/designerView/designerCanvas.d.ts +4 -2
  6. package/dist/elements/widgets/designerView/designerCanvas.js +28 -3
  7. package/dist/elements/widgets/designerView/designerView.js +2 -2
  8. package/dist/elements/widgets/designerView/extensions/ElementDragTitleExtension.js +2 -2
  9. package/dist/elements/widgets/designerView/extensions/PathExtension.js +1 -0
  10. package/dist/elements/widgets/designerView/extensions/ResizeExtension.js +2 -1
  11. package/dist/elements/widgets/designerView/extensions/TransformOriginExtension.js +1 -0
  12. package/dist/elements/widgets/designerView/tools/DrawEllipsisTool.js +1 -0
  13. package/dist/elements/widgets/designerView/tools/DrawLineTool.js +1 -0
  14. package/dist/elements/widgets/designerView/tools/DrawPathTool.js +1 -0
  15. package/dist/elements/widgets/designerView/tools/DrawRectTool.js +1 -0
  16. package/dist/elements/widgets/designerView/tools/MagicWandSelectorTool.js +1 -0
  17. package/dist/elements/widgets/designerView/tools/PanTool.js +1 -0
  18. package/dist/elements/widgets/designerView/tools/PointerTool.js +4 -2
  19. package/dist/elements/widgets/designerView/tools/RectangleSelectorTool.js +1 -0
  20. package/dist/elements/widgets/designerView/tools/ZoomTool.d.ts +1 -0
  21. package/dist/elements/widgets/designerView/tools/ZoomTool.js +38 -3
  22. package/dist/elements/widgets/paletteView/paletteTreeView.js +5 -0
  23. package/dist/elements/widgets/treeView/treeViewExtended.js +10 -13
  24. package/package.json +1 -1
@@ -8,7 +8,7 @@ export class SimpleSplitView extends BaseCustomWebComponentConstructorAppend {
8
8
  position: relative;
9
9
  height: 100%;
10
10
  width: 100%;
11
- grid-template-rows: calc(var(--split) * 1%) 5px calc((100 - var(--split)) * 1%);
11
+ grid-template-rows: calc(var(--split) * 1%) 5px calc(((100 - var(--split)) * 1%) - 5px);
12
12
  display: grid;
13
13
  align-items: center;
14
14
  }
@@ -62,9 +62,11 @@ export class DocumentContainer extends BaseCustomWebComponentLazyAppend {
62
62
  this.designerView.designerCanvas.onContentChanged.on(() => this.designerContentChanged());
63
63
  this.codeView = new serviceContainer.config.codeViewWidget();
64
64
  this.codeView.slot = 'bottom';
65
+ this.codeView.style.position = 'relative';
65
66
  this._codeDiv = document.createElement("div");
66
67
  this._tabControl.appendChild(this._codeDiv);
67
68
  this._codeDiv.title = 'Code';
69
+ this._codeDiv.style.position = 'relative';
68
70
  this._codeDiv.appendChild(this.codeView);
69
71
  this.codeView.onTextChanged.on(text => {
70
72
  if (!this._disableChangeNotificationDesigner) {
@@ -19,7 +19,7 @@ export class UndoService {
19
19
  let itm = this._transactionStack.pop();
20
20
  if (itm !== transactionItem) {
21
21
  this.clear();
22
- throw "UndoService - Commited Transation was not the last";
22
+ throw "UndoService - Commited Transaction was not the last";
23
23
  }
24
24
  if (itm.undoStack.length)
25
25
  this._undoStack.push(itm);
@@ -29,7 +29,7 @@ export class UndoService {
29
29
  let itm = this._transactionStack.pop();
30
30
  if (itm !== transactionItem) {
31
31
  this.clear();
32
- throw "UndoService - Aborted Transation was not the last";
32
+ throw "UndoService - Aborted Transaction was not the last";
33
33
  }
34
34
  itm.undo();
35
35
  }
@@ -32,10 +32,12 @@ export interface IDesignerCanvas extends IPlacementView, IUiCommandHandler {
32
32
  getNormalizedEventCoordinates(event: MouseEvent): IPoint;
33
33
  getViewportCoordinates(event: MouseEvent): IPoint;
34
34
  getNormalizedElementCoordinates(element: Element): IRect;
35
+ removeCurrentPointerEventHandler(): any;
35
36
  getDesignSurfaceDimensions(): ISize;
36
37
  getNormalizedOffsetInElement(event: MouseEvent, element: Element): IPoint;
37
38
  getElementAtPoint(point: IPoint, ignoreElementCallback?: (element: HTMLElement) => boolean): any;
38
39
  elementFromPoint(x: number, y: number): Element;
39
40
  getItemsBelowMouse(event: MouseEvent): Element[];
40
- zoomTowardsPointer(point: IPoint, scalechange: number): void;
41
+ zoomTowardsPoint(point: IPoint, scalechange: number): void;
42
+ zoomOntoRectangle(startPoint: IPoint, endPoint: IPoint): void;
41
43
  }
@@ -87,16 +87,18 @@ export declare class DesignerCanvas extends BaseCustomWebComponentLazyAppend imp
87
87
  private onKeyUp;
88
88
  private onKeyDown;
89
89
  getNormalizedEventCoordinates(event: MouseEvent): IPoint;
90
+ convertEventToViewPortCoordinates(point: IPoint): IPoint;
90
91
  getViewportCoordinates(event: MouseEvent): IPoint;
91
92
  getNormalizedElementCoordinates(element: Element): IRect;
92
93
  getNormalizedOffsetInElement(event: MouseEvent, element: Element): IPoint;
93
94
  getElementAtPoint(point: IPoint, ignoreElementCallback?: (element: HTMLElement) => boolean): HTMLElement;
94
95
  _rect: SVGRectElement;
95
96
  private _pointerEventHandler;
97
+ removeCurrentPointerEventHandler(): void;
96
98
  private _fillCalculationrects;
97
99
  addOverlay(element: SVGGraphicsElement, overlayLayer?: OverlayLayer): void;
98
100
  removeOverlay(element: SVGGraphicsElement): void;
99
101
  getItemsBelowMouse(event: MouseEvent): Element[];
100
- zoomOntoRectangle(startpoint: IPoint, endPoint: IPoint, scalechange: number): void;
101
- zoomTowardsPointer(point: IPoint, newZoom: number): void;
102
+ zoomOntoRectangle(startPoint: IPoint, endPoint: IPoint): void;
103
+ zoomTowardsPoint(canvasPoint: IPoint, newZoom: number): void;
102
104
  }
@@ -616,6 +616,14 @@ export class DesignerCanvas extends BaseCustomWebComponentLazyAppend {
616
616
  y: offsetOfOuterY - offsetOfCanvasY / this.zoomFactor
617
617
  };
618
618
  }
619
+ convertEventToViewPortCoordinates(point) {
620
+ const offsetOfCanvasX = this.containerBoundingRect.x - this.outerRect.x;
621
+ const offsetOfCanvasY = this.containerBoundingRect.y - this.outerRect.y;
622
+ return {
623
+ x: (point.x + offsetOfCanvasX / this.zoomFactor) * this.zoomFactor,
624
+ y: (point.y + offsetOfCanvasY / this.zoomFactor) * this.zoomFactor
625
+ };
626
+ }
619
627
  getViewportCoordinates(event) {
620
628
  return {
621
629
  x: (event.clientX - this.outerRect.x),
@@ -734,12 +742,14 @@ export class DesignerCanvas extends BaseCustomWebComponentLazyAppend {
734
742
  this._fillCalculationrects();
735
743
  let tool = this.serviceContainer.globalContext.tool ?? this.serviceContainer.designerTools.get(NamedTools.Pointer);
736
744
  this._canvas.style.cursor = tool.cursor;
737
- tool.pointerEventHandler(this, event, currentElement);
738
745
  if (event.type == EventNames.PointerDown) {
739
746
  this._lastPointerDownHandler = (evt) => tool.pointerEventHandler(this, evt, currentElement);
740
747
  }
741
748
  tool.pointerEventHandler(this, event, currentElement);
742
749
  }
750
+ removeCurrentPointerEventHandler() {
751
+ this._lastPointerDownHandler = null;
752
+ }
743
753
  _fillCalculationrects() {
744
754
  this.containerBoundingRect = this._canvasContainer.getBoundingClientRect();
745
755
  this.outerRect = this._outercanvas2.getBoundingClientRect();
@@ -784,10 +794,25 @@ export class DesignerCanvas extends BaseCustomWebComponentLazyAppend {
784
794
  }
785
795
  return lstEl;
786
796
  }
787
- zoomOntoRectangle(startpoint, endPoint, scalechange) {
797
+ zoomOntoRectangle(startPoint, endPoint) {
798
+ let rect = {
799
+ x: startPoint.x < endPoint.x ? startPoint.x : endPoint.x,
800
+ y: startPoint.y < endPoint.y ? startPoint.y : endPoint.y,
801
+ width: Math.abs(startPoint.x - endPoint.x),
802
+ height: Math.abs(startPoint.y - endPoint.y),
803
+ };
804
+ let zFactorWidth = this.outerRect.width / rect.width;
805
+ let zFactorHeight = this.outerRect.height / rect.height;
806
+ let zoomFactor = zFactorWidth >= zFactorHeight ? zFactorHeight : zFactorWidth;
807
+ let rectCenter = {
808
+ x: (rect.width / 2) + rect.x,
809
+ y: (rect.height / 2) + rect.y
810
+ };
811
+ this.zoomTowardsPoint(rectCenter, zoomFactor);
788
812
  }
789
- zoomTowardsPointer(point, newZoom) {
813
+ zoomTowardsPoint(canvasPoint, newZoom) {
790
814
  const scaleChange = newZoom / this.zoomFactor;
815
+ const point = this.convertEventToViewPortCoordinates(canvasPoint);
791
816
  const newCanvasOffset = {
792
817
  x: -(point.x * (scaleChange - 1) + scaleChange * -this.canvasOffsetUnzoomed.x),
793
818
  y: -(point.y * (scaleChange - 1) + scaleChange * -this.canvasOffsetUnzoomed.y)
@@ -247,8 +247,8 @@ export class DesignerView extends BaseCustomWebComponentConstructorAppend {
247
247
  zf += event.deltaY * -0.001; //deltamode = 0
248
248
  if (zf < 0.02)
249
249
  zf = 0.02;
250
- const vp = this.designerCanvas.getViewportCoordinates(event);
251
- this.designerCanvas.zoomTowardsPointer(vp, zf);
250
+ const vp = this.designerCanvas.getNormalizedEventCoordinates(event);
251
+ this.designerCanvas.zoomTowardsPoint(vp, zf);
252
252
  }
253
253
  else {
254
254
  this._sHor.value += event.deltaX / 1000;
@@ -1,3 +1,4 @@
1
+ import { NamedTools } from "../tools/NamedTools.js";
1
2
  import { AbstractExtension } from './AbstractExtension';
2
3
  export class ElementDragTitleExtension extends AbstractExtension {
3
4
  _rect;
@@ -29,8 +30,7 @@ export class ElementDragTitleExtension extends AbstractExtension {
29
30
  _pointerEvent(event) {
30
31
  event.preventDefault();
31
32
  event.stopPropagation();
32
- //@ts-ignore
33
- this.designerCanvas._pointerEventHandlerBound(event, this.extendedItem.element);
33
+ this.designerCanvas.serviceContainer.designerTools.get(NamedTools.Pointer).pointerEventHandler(this.designerCanvas, event, this.extendedItem.element);
34
34
  }
35
35
  dispose() {
36
36
  this._removeAllOverlays();
@@ -113,6 +113,7 @@ export class PathExtension extends AbstractExtension {
113
113
  break;
114
114
  case EventNames.PointerUp:
115
115
  event.target.releasePointerCapture(event.pointerId);
116
+ this.designerCanvas.removeCurrentPointerEventHandler();
116
117
  this._startPos = null;
117
118
  this._circlePos = null;
118
119
  this._lastPos = null;
@@ -60,7 +60,7 @@ export class ResizeExtension extends AbstractExtension {
60
60
  }
61
61
  _pointerActionTypeResize(circle, event, actionMode = 'se-resize') {
62
62
  event.stopPropagation();
63
- const currentPoint = this.designerCanvas.getNormalizedEventCoordinates(event); //, this.extendedItem.element, event.type === 'pointerdown' ? null : this._initialPoint);
63
+ const currentPoint = this.designerCanvas.getNormalizedEventCoordinates(event);
64
64
  switch (event.type) {
65
65
  case EventNames.PointerDown:
66
66
  const cx = parseFloat(circle.getAttribute('cx'));
@@ -135,6 +135,7 @@ export class ResizeExtension extends AbstractExtension {
135
135
  break;
136
136
  case EventNames.PointerUp:
137
137
  event.target.releasePointerCapture(event.pointerId);
138
+ this.designerCanvas.removeCurrentPointerEventHandler();
138
139
  let cg = this.extendedItem.openGroup("Resize Elements", this.designerCanvas.instanceServiceContainer.selectionService.selectedElements);
139
140
  this.extendedItem.setStyle('width', this.extendedItem.element.style.width);
140
141
  this.extendedItem.setStyle('height', this.extendedItem.element.style.height);
@@ -43,6 +43,7 @@ export class TransformOriginExtension extends AbstractExtension {
43
43
  break;
44
44
  case EventNames.PointerUp:
45
45
  event.target.releasePointerCapture(event.pointerId);
46
+ this.designerCanvas.removeCurrentPointerEventHandler();
46
47
  if (this._startPos) {
47
48
  const dx = normalized.x - this._startPos.x;
48
49
  const dy = normalized.y - this._startPos.y;
@@ -67,6 +67,7 @@ export class DrawEllipsisTool {
67
67
  break;
68
68
  case EventNames.PointerUp:
69
69
  event.target.releasePointerCapture(event.pointerId);
70
+ designerCanvas.removeCurrentPointerEventHandler();
70
71
  const rect = this._path.getBoundingClientRect();
71
72
  designerCanvas.overlayLayer.removeOverlay(this._path);
72
73
  const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
@@ -50,6 +50,7 @@ export class DrawLineTool {
50
50
  break;
51
51
  case EventNames.PointerUp:
52
52
  event.target.releasePointerCapture(event.pointerId);
53
+ designerCanvas.removeCurrentPointerEventHandler();
53
54
  const rect = this._path.getBoundingClientRect();
54
55
  designerCanvas.overlayLayer.removeOverlay(this._path);
55
56
  const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
@@ -70,6 +70,7 @@ export class DrawPathTool {
70
70
  break;
71
71
  case EventNames.PointerUp:
72
72
  event.target.releasePointerCapture(event.pointerId);
73
+ designerCanvas.removeCurrentPointerEventHandler();
73
74
  if (this._eventStarted && !this._pointerMoved) {
74
75
  this._p2pMode = true;
75
76
  }
@@ -82,6 +82,7 @@ export class DrawRectTool {
82
82
  break;
83
83
  case EventNames.PointerUp:
84
84
  event.target.releasePointerCapture(event.pointerId);
85
+ designerCanvas.removeCurrentPointerEventHandler();
85
86
  const rect = this._path.getBoundingClientRect();
86
87
  designerCanvas.overlayLayer.removeOverlay(this._path);
87
88
  const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
@@ -24,6 +24,7 @@ export class MagicWandSelectorTool {
24
24
  break;
25
25
  case EventNames.PointerUp:
26
26
  event.target.releasePointerCapture(event.pointerId);
27
+ designerCanvas.removeCurrentPointerEventHandler();
27
28
  const elements = designerCanvas.rootDesignItem.element.querySelectorAll('*');
28
29
  const inSelectionElements = [];
29
30
  let point = designerCanvas.overlayLayer.createPoint();
@@ -13,6 +13,7 @@ export class PanTool {
13
13
  break;
14
14
  case EventNames.PointerUp:
15
15
  event.target.releasePointerCapture(event.pointerId);
16
+ designerCanvas.removeCurrentPointerEventHandler();
16
17
  break;
17
18
  }
18
19
  }
@@ -37,6 +37,7 @@ export class PointerTool {
37
37
  break;
38
38
  case EventNames.PointerUp:
39
39
  event.target.releasePointerCapture(event.pointerId);
40
+ designerCanvas.removeCurrentPointerEventHandler();
40
41
  break;
41
42
  }
42
43
  if (!event.altKey)
@@ -189,7 +190,7 @@ export class PointerTool {
189
190
  break;
190
191
  }
191
192
  }
192
- else if (newContainerElement == this._actionStartedDesignItem.element) {
193
+ else if (newContainerElement == this._actionStartedDesignItem.element || newContainerElement == currentDesignItem.element) {
193
194
  backupPEventsMap.set(newContainerElement, newContainerElement.style.pointerEvents);
194
195
  newContainerElement.style.pointerEvents = 'none';
195
196
  const old = newContainerElement;
@@ -197,6 +198,7 @@ export class PointerTool {
197
198
  if (old === newContainerElement) {
198
199
  newContainerElementDesignItem = null;
199
200
  newContainerService = null;
201
+ newContainerElement = null;
200
202
  break;
201
203
  }
202
204
  }
@@ -297,7 +299,7 @@ export class PointerTool {
297
299
  }
298
300
  case EventNames.PointerUp:
299
301
  {
300
- if (this._actionType == PointerActionType.DragOrSelect) {
302
+ if (!this._movedSinceStartedAction || this._actionType == PointerActionType.DragOrSelect) {
301
303
  if (this._previousEventName == EventNames.PointerDown && !event.shiftKey && !event.ctrlKey)
302
304
  designerCanvas.instanceServiceContainer.selectionService.setSelectedElements([currentDesignItem]);
303
305
  return;
@@ -46,6 +46,7 @@ export class RectangleSelectorTool {
46
46
  break;
47
47
  case EventNames.PointerUp:
48
48
  event.target.releasePointerCapture(event.pointerId);
49
+ designerCanvas.removeCurrentPointerEventHandler();
49
50
  const elements = designerCanvas.rootDesignItem.element.querySelectorAll('*');
50
51
  const inSelectionElements = [];
51
52
  let point = designerCanvas.overlayLayer.createPoint();
@@ -3,6 +3,7 @@ import { IDesignerCanvas } from '../IDesignerCanvas';
3
3
  import { ITool } from './ITool';
4
4
  export declare class ZoomTool implements ITool {
5
5
  cursor: string;
6
+ private _rect;
6
7
  private _startPoint;
7
8
  private _endPoint;
8
9
  private _pointerMovementTolerance;
@@ -1,6 +1,7 @@
1
- import { EventNames } from '../../../../index.js';
1
+ import { EventNames, OverlayLayer } from '../../../../index.js';
2
2
  export class ZoomTool {
3
3
  cursor = 'zoom-in';
4
+ _rect;
4
5
  _startPoint;
5
6
  _endPoint;
6
7
  _pointerMovementTolerance = 5;
@@ -8,10 +9,40 @@ export class ZoomTool {
8
9
  activated(serviceContainer) {
9
10
  }
10
11
  pointerEventHandler(designerCanvas, event, currentElement) {
11
- const eventPoint = designerCanvas.getViewportCoordinates(event);
12
+ const eventPoint = designerCanvas.getNormalizedEventCoordinates(event);
12
13
  switch (event.type) {
13
14
  case EventNames.PointerDown:
14
15
  this._startPoint = eventPoint;
16
+ if (!this._rect)
17
+ this._rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
18
+ this._rect.setAttribute('class', 'svg-selector');
19
+ this._rect.setAttribute('x', (this._startPoint.x * designerCanvas.zoomFactor));
20
+ this._rect.setAttribute('y', (this._startPoint.y * designerCanvas.zoomFactor));
21
+ this._rect.setAttribute('width', 0);
22
+ this._rect.setAttribute('height', 0);
23
+ designerCanvas.overlayLayer.addOverlay(this._rect, OverlayLayer.Foregorund);
24
+ break;
25
+ case EventNames.PointerMove:
26
+ if (this._startPoint) {
27
+ let width = eventPoint.x - this._startPoint.x;
28
+ let height = eventPoint.y - this._startPoint.y;
29
+ if (width >= 0) {
30
+ this._rect.setAttribute('x', this._startPoint.x);
31
+ this._rect.setAttribute('width', width);
32
+ }
33
+ else {
34
+ this._rect.setAttribute('x', eventPoint.x);
35
+ this._rect.setAttribute('width', (-1 * width));
36
+ }
37
+ if (height >= 0) {
38
+ this._rect.setAttribute('y', this._startPoint.y);
39
+ this._rect.setAttribute('height', height);
40
+ }
41
+ else {
42
+ this._rect.setAttribute('y', eventPoint.y);
43
+ this._rect.setAttribute('height', (-1 * height));
44
+ }
45
+ }
15
46
  break;
16
47
  case EventNames.PointerUp:
17
48
  this._endPoint = eventPoint;
@@ -22,6 +53,9 @@ export class ZoomTool {
22
53
  this._zoomOnto(isLeftClick, this._startPoint, this._endPoint, designerCanvas);
23
54
  break;
24
55
  }
56
+ designerCanvas.overlayLayer.removeOverlay(this._rect);
57
+ this._rect = null;
58
+ this._startPoint = null;
25
59
  break;
26
60
  }
27
61
  }
@@ -29,9 +63,10 @@ export class ZoomTool {
29
63
  if (this._isPositionEqual(startPoint, endPoint)) {
30
64
  const oldZoom = designerCanvas.zoomFactor;
31
65
  const newZoom = isZoomInto ? oldZoom + this._zoomStepSize : oldZoom - this._zoomStepSize;
32
- designerCanvas.zoomTowardsPointer(endPoint, newZoom);
66
+ designerCanvas.zoomTowardsPoint(endPoint, newZoom);
33
67
  }
34
68
  else {
69
+ designerCanvas.zoomOntoRectangle(startPoint, endPoint);
35
70
  }
36
71
  }
37
72
  _isPositionEqual(startPoint, endPoint) {
@@ -72,6 +72,11 @@ export class PaletteTreeView extends BaseCustomWebComponentConstructorAppend {
72
72
  extensions: ['childcounter', 'dnd5', 'filter'],
73
73
  quicksearch: true,
74
74
  source: [],
75
+ filter: {
76
+ autoExpand: true,
77
+ mode: 'hide',
78
+ highlight: true
79
+ },
75
80
  dnd5: {
76
81
  dropMarkerParent: this.shadowRoot,
77
82
  preventRecursion: true,
@@ -294,7 +294,7 @@ export class TreeViewExtended extends BaseCustomWebComponentConstructorAppend {
294
294
  },
295
295
  filter: {
296
296
  autoApply: true,
297
- autoExpand: false,
297
+ autoExpand: true,
298
298
  counter: true,
299
299
  fuzzy: true,
300
300
  hideExpandedCounter: true,
@@ -365,18 +365,15 @@ export class TreeViewExtended extends BaseCustomWebComponentConstructorAppend {
365
365
  }
366
366
  }
367
367
  _highlight(activeElements) {
368
- if (activeElements != null) {
369
- this._tree.visit((node) => {
370
- //@ts-ignore
371
- if (activeElements.indexOf(node.data.ref) >= 0) {
372
- node.setSelected(true);
373
- node.makeVisible({ scrollIntoView: true });
374
- }
375
- else {
376
- node.setSelected(false);
377
- }
378
- });
379
- }
368
+ this._tree.visit((node) => {
369
+ if (activeElements && activeElements.indexOf(node.data.ref) >= 0) {
370
+ node.setSelected(true);
371
+ node.makeVisible({ scrollIntoView: true });
372
+ }
373
+ else {
374
+ node.setSelected(false);
375
+ }
376
+ });
380
377
  }
381
378
  }
382
379
  customElements.define('node-projects-tree-view-extended', TreeViewExtended);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "description": "A UI designer for Polymer apps",
3
3
  "name": "@node-projects/web-component-designer",
4
- "version": "0.0.88",
4
+ "version": "0.0.89",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "author": "",