js-draw 0.3.0 → 0.3.1

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 (56) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/bundle.js +1 -1
  3. package/dist/src/components/Stroke.js +11 -6
  4. package/dist/src/components/builders/FreehandLineBuilder.js +5 -5
  5. package/dist/src/math/LineSegment2.d.ts +2 -0
  6. package/dist/src/math/LineSegment2.js +6 -0
  7. package/dist/src/math/Path.d.ts +5 -1
  8. package/dist/src/math/Path.js +89 -7
  9. package/dist/src/math/Rect2.js +1 -1
  10. package/dist/src/math/Triangle.d.ts +11 -0
  11. package/dist/src/math/Triangle.js +19 -0
  12. package/dist/src/rendering/Display.js +2 -2
  13. package/dist/src/rendering/renderers/AbstractRenderer.d.ts +2 -1
  14. package/dist/src/rendering/renderers/SVGRenderer.d.ts +10 -11
  15. package/dist/src/rendering/renderers/SVGRenderer.js +27 -68
  16. package/dist/src/toolbar/HTMLToolbar.d.ts +1 -0
  17. package/dist/src/toolbar/HTMLToolbar.js +1 -0
  18. package/dist/src/toolbar/widgets/BaseWidget.d.ts +3 -0
  19. package/dist/src/toolbar/widgets/BaseWidget.js +21 -1
  20. package/dist/src/tools/BaseTool.d.ts +1 -0
  21. package/dist/src/tools/BaseTool.js +6 -0
  22. package/dist/src/tools/Pen.d.ts +2 -1
  23. package/dist/src/tools/Pen.js +16 -0
  24. package/dist/src/tools/ToolController.d.ts +1 -0
  25. package/dist/src/tools/ToolController.js +9 -2
  26. package/dist/src/tools/ToolSwitcherShortcut.d.ts +8 -0
  27. package/dist/src/tools/ToolSwitcherShortcut.js +26 -0
  28. package/dist/src/tools/lib.d.ts +1 -0
  29. package/dist/src/tools/lib.js +1 -0
  30. package/dist/src/tools/localization.d.ts +1 -0
  31. package/dist/src/tools/localization.js +1 -0
  32. package/dist/src/types.d.ts +8 -2
  33. package/dist/src/types.js +1 -0
  34. package/package.json +2 -2
  35. package/src/components/Stroke.test.ts +5 -0
  36. package/src/components/Stroke.ts +13 -7
  37. package/src/components/builders/FreehandLineBuilder.ts +5 -5
  38. package/src/math/LineSegment2.ts +8 -0
  39. package/src/math/Path.test.ts +53 -0
  40. package/src/math/Path.toString.test.ts +4 -2
  41. package/src/math/Path.ts +109 -11
  42. package/src/math/Rect2.ts +1 -1
  43. package/src/math/Triangle.ts +29 -0
  44. package/src/rendering/Display.ts +2 -2
  45. package/src/rendering/renderers/AbstractRenderer.ts +1 -0
  46. package/src/rendering/renderers/SVGRenderer.ts +30 -84
  47. package/src/toolbar/HTMLToolbar.ts +1 -1
  48. package/src/toolbar/types.ts +1 -1
  49. package/src/toolbar/widgets/BaseWidget.ts +27 -1
  50. package/src/tools/BaseTool.ts +8 -0
  51. package/src/tools/Pen.ts +20 -1
  52. package/src/tools/ToolController.ts +10 -3
  53. package/src/tools/ToolSwitcherShortcut.ts +34 -0
  54. package/src/tools/lib.ts +1 -0
  55. package/src/tools/localization.ts +2 -0
  56. package/src/types.ts +13 -1
@@ -76,9 +76,9 @@ export default class Display {
76
76
  return this.dryInkRenderer.canRenderFromWithoutDataLoss(renderer);
77
77
  },
78
78
  blockResolution: cacheBlockResolution,
79
- cacheSize: 600 * 600 * 4 * 120,
79
+ cacheSize: 600 * 600 * 4 * 90,
80
80
  maxScale: 1.4,
81
- minComponentsPerCache: 45,
81
+ minComponentsPerCache: 20,
82
82
  minComponentsToUseCache: 105,
83
83
  });
84
84
 
@@ -11,6 +11,7 @@ export interface RenderablePathSpec {
11
11
  startPoint: Point2;
12
12
  commands: PathCommand[];
13
13
  style: RenderingStyle;
14
+ path?: Path;
14
15
  }
15
16
 
16
17
  export default abstract class AbstractRenderer {
@@ -2,23 +2,19 @@
2
2
  import { LoadSaveDataTable } from '../../components/AbstractComponent';
3
3
  import { TextStyle } from '../../components/Text';
4
4
  import Mat33 from '../../math/Mat33';
5
- import Path, { PathCommand, PathCommandType } from '../../math/Path';
5
+ import Path from '../../math/Path';
6
6
  import Rect2 from '../../math/Rect2';
7
7
  import { toRoundedString } from '../../math/rounding';
8
8
  import { Point2, Vec2 } from '../../math/Vec2';
9
9
  import { svgAttributesDataKey, SVGLoaderUnknownAttribute, SVGLoaderUnknownStyleAttribute, svgStyleAttributesDataKey } from '../../SVGLoader';
10
10
  import Viewport from '../../Viewport';
11
11
  import RenderingStyle from '../RenderingStyle';
12
- import AbstractRenderer from './AbstractRenderer';
12
+ import AbstractRenderer, { RenderablePathSpec } from './AbstractRenderer';
13
13
 
14
14
  const svgNameSpace = 'http://www.w3.org/2000/svg';
15
15
  export default class SVGRenderer extends AbstractRenderer {
16
- private currentPath: PathCommand[]|null;
17
- private pathStart: Point2|null;
18
-
19
- private lastPathStyle: RenderingStyle|null;
20
- private lastPath: PathCommand[]|null;
21
- private lastPathStart: Point2|null;
16
+ private lastPathStyle: RenderingStyle|null = null;
17
+ private lastPathString: string[] = [];
22
18
  private objectElems: SVGElement[]|null = null;
23
19
 
24
20
  private overwrittenAttrs: Record<string, string|null> = {};
@@ -58,46 +54,17 @@ export default class SVGRenderer extends AbstractRenderer {
58
54
  }
59
55
  }
60
56
  this.overwrittenAttrs = {};
61
- }
62
-
63
- protected beginPath(startPoint: Point2) {
64
- this.currentPath = [];
65
- this.pathStart = this.canvasToScreen(startPoint);
66
- this.lastPathStart ??= this.pathStart;
67
- }
68
-
69
- protected endPath(style: RenderingStyle) {
70
- if (this.currentPath == null) {
71
- throw new Error('No path exists to end! Make sure beginPath was called!');
72
- }
73
-
74
- // Try to extend the previous path, if possible
75
- if (style.fill.eq(this.lastPathStyle?.fill) && this.lastPath != null) {
76
- this.lastPath.push({
77
- kind: PathCommandType.MoveTo,
78
- point: this.pathStart!,
79
- }, ...this.currentPath);
80
- this.pathStart = null;
81
- this.currentPath = null;
82
- } else {
83
- this.addPathToSVG();
84
- this.lastPathStart = this.pathStart;
85
- this.lastPathStyle = style;
86
- this.lastPath = this.currentPath;
87
-
88
- this.pathStart = null;
89
- this.currentPath = null;
90
- }
57
+ this.lastPathString = [];
91
58
  }
92
59
 
93
60
  // Push [this.fullPath] to the SVG
94
61
  private addPathToSVG() {
95
- if (!this.lastPathStyle || !this.lastPath) {
62
+ if (!this.lastPathStyle || this.lastPathString.length === 0) {
96
63
  return;
97
64
  }
98
65
 
99
66
  const pathElem = document.createElementNS(svgNameSpace, 'path');
100
- pathElem.setAttribute('d', Path.toString(this.lastPathStart!, this.lastPath));
67
+ pathElem.setAttribute('d', this.lastPathString.join(' '));
101
68
 
102
69
  const style = this.lastPathStyle;
103
70
  pathElem.setAttribute('fill', style.fill.toHexString());
@@ -111,6 +78,19 @@ export default class SVGRenderer extends AbstractRenderer {
111
78
  this.objectElems?.push(pathElem);
112
79
  }
113
80
 
81
+ public drawPath(pathSpec: RenderablePathSpec) {
82
+ const style = pathSpec.style;
83
+ const path = Path.fromRenderable(pathSpec);
84
+
85
+ // Try to extend the previous path, if possible
86
+ if (!style.fill.eq(this.lastPathStyle?.fill) || this.lastPathString.length === 0) {
87
+ this.addPathToSVG();
88
+ this.lastPathStyle = style;
89
+ this.lastPathString = [];
90
+ }
91
+ this.lastPathString.push(path.toString());
92
+ }
93
+
114
94
  public drawText(text: string, transform: Mat33, style: TextStyle): void {
115
95
  transform = this.getCanvasToScreenTransform().rightMul(transform);
116
96
 
@@ -146,8 +126,7 @@ export default class SVGRenderer extends AbstractRenderer {
146
126
  super.startObject(boundingBox);
147
127
 
148
128
  // Only accumulate a path within an object
149
- this.lastPath = null;
150
- this.lastPathStart = null;
129
+ this.lastPathString = [];
151
130
  this.lastPathStyle = null;
152
131
  this.objectElems = [];
153
132
  }
@@ -179,49 +158,16 @@ export default class SVGRenderer extends AbstractRenderer {
179
158
  }
180
159
  }
181
160
 
182
- protected lineTo(point: Point2) {
183
- point = this.canvasToScreen(point);
184
-
185
- this.currentPath!.push({
186
- kind: PathCommandType.LineTo,
187
- point,
188
- });
189
- }
190
-
191
- protected moveTo(point: Point2) {
192
- point = this.canvasToScreen(point);
193
-
194
- this.currentPath!.push({
195
- kind: PathCommandType.MoveTo,
196
- point,
197
- });
198
- }
199
-
161
+ // Not implemented -- use drawPath instead.
162
+ private unimplementedMessage() { throw new Error('Not implemenented!'); }
163
+ protected beginPath(_startPoint: Point2) { this.unimplementedMessage(); }
164
+ protected endPath(_style: RenderingStyle) { this.unimplementedMessage(); }
165
+ protected lineTo(_point: Point2) { this.unimplementedMessage(); }
166
+ protected moveTo(_point: Point2) { this.unimplementedMessage(); }
200
167
  protected traceCubicBezierCurve(
201
- controlPoint1: Point2, controlPoint2: Point2, endPoint: Point2
202
- ) {
203
- controlPoint1 = this.canvasToScreen(controlPoint1);
204
- controlPoint2 = this.canvasToScreen(controlPoint2);
205
- endPoint = this.canvasToScreen(endPoint);
206
-
207
- this.currentPath!.push({
208
- kind: PathCommandType.CubicBezierTo,
209
- controlPoint1,
210
- controlPoint2,
211
- endPoint,
212
- });
213
- }
214
-
215
- protected traceQuadraticBezierCurve(controlPoint: Point2, endPoint: Point2) {
216
- controlPoint = this.canvasToScreen(controlPoint);
217
- endPoint = this.canvasToScreen(endPoint);
218
-
219
- this.currentPath!.push({
220
- kind: PathCommandType.QuadraticBezierTo,
221
- controlPoint,
222
- endPoint,
223
- });
224
- }
168
+ _controlPoint1: Point2, _controlPoint2: Point2, _endPoint: Point2
169
+ ) { this.unimplementedMessage(); }
170
+ protected traceQuadraticBezierCurve(_controlPoint: Point2, _endPoint: Point2) { this.unimplementedMessage(); }
225
171
 
226
172
  public drawPoints(...points: Point2[]) {
227
173
  points.map(point => {
@@ -17,7 +17,6 @@ import HandToolWidget from './widgets/HandToolWidget';
17
17
  import BaseWidget from './widgets/BaseWidget';
18
18
  import { EraserTool, PenTool } from '../tools/lib';
19
19
 
20
-
21
20
  export const toolbarCSSPrefix = 'toolbar-';
22
21
 
23
22
  type UpdateColorisCallback = ()=>void;
@@ -28,6 +27,7 @@ export default class HTMLToolbar {
28
27
  private static colorisStarted: boolean = false;
29
28
  private updateColoris: UpdateColorisCallback|null = null;
30
29
 
30
+ /** @internal */
31
31
  public constructor(
32
32
  private editor: Editor, parent: HTMLElement,
33
33
  private localizationTable: ToolbarLocalization = defaultToolbarLocalization,
@@ -2,4 +2,4 @@
2
2
  export interface ActionButtonIcon {
3
3
  icon: Element;
4
4
  label: string;
5
- }
5
+ }
@@ -1,5 +1,5 @@
1
1
  import Editor from '../../Editor';
2
- import { InputEvtType } from '../../types';
2
+ import { EditorEventType, InputEvtType } from '../../types';
3
3
  import { toolbarCSSPrefix } from '../HTMLToolbar';
4
4
  import { makeDropdownIcon } from '../icons';
5
5
  import { ToolbarLocalization } from '../localization';
@@ -14,6 +14,7 @@ export default abstract class BaseWidget {
14
14
  #hasDropdown: boolean;
15
15
  private disabled: boolean = false;
16
16
  private subWidgets: BaseWidget[] = [];
17
+ private toplevel: boolean = true;
17
18
 
18
19
  public constructor(
19
20
  protected editor: Editor,
@@ -46,6 +47,7 @@ export default abstract class BaseWidget {
46
47
 
47
48
  for (const widget of this.subWidgets) {
48
49
  widget.addTo(dropdown);
50
+ widget.setIsToplevel(false);
49
51
  }
50
52
  return true;
51
53
  }
@@ -103,6 +105,7 @@ export default abstract class BaseWidget {
103
105
  }
104
106
 
105
107
  // Adds this to [parent]. This can only be called once for each ToolbarWidget.
108
+ // @internal
106
109
  public addTo(parent: HTMLElement) {
107
110
  this.label.innerText = this.getTitle();
108
111
 
@@ -119,6 +122,19 @@ export default abstract class BaseWidget {
119
122
  this.dropdownIcon = this.createDropdownIcon();
120
123
  this.button.appendChild(this.dropdownIcon);
121
124
  this.container.appendChild(this.dropdownContainer);
125
+
126
+ this.editor.notifier.on(EditorEventType.ToolbarDropdownShown, (evt) => {
127
+ if (
128
+ evt.kind === EditorEventType.ToolbarDropdownShown
129
+ && evt.parentWidget !== this
130
+
131
+ // Don't hide if a submenu wash shown (it might be a submenu of
132
+ // the current menu).
133
+ && evt.parentWidget.toplevel
134
+ ) {
135
+ this.setDropdownVisible(false);
136
+ }
137
+ });
122
138
  }
123
139
 
124
140
  this.setDropdownVisible(false);
@@ -171,6 +187,11 @@ export default abstract class BaseWidget {
171
187
  this.editor.announceForAccessibility(
172
188
  this.localizationTable.dropdownShown(this.getTitle())
173
189
  );
190
+
191
+ this.editor.notifier.dispatch(EditorEventType.ToolbarDropdownShown, {
192
+ kind: EditorEventType.ToolbarDropdownShown,
193
+ parentWidget: this,
194
+ });
174
195
  } else {
175
196
  this.dropdownContainer.classList.add('hidden');
176
197
  this.container.classList.remove('dropdownVisible');
@@ -195,6 +216,11 @@ export default abstract class BaseWidget {
195
216
  }
196
217
  }
197
218
 
219
+ /** Set whether the widget is contained within another. @internal */
220
+ protected setIsToplevel(toplevel: boolean) {
221
+ this.toplevel = toplevel;
222
+ }
223
+
198
224
  protected isDropdownVisible(): boolean {
199
225
  return !this.dropdownContainer.classList.contains('hidden');
200
226
  }
@@ -56,5 +56,13 @@ export default abstract class BaseTool implements PointerEvtListener {
56
56
 
57
57
  this.group = group;
58
58
  }
59
+
60
+ public getToolGroup(): ToolEnabledGroup|null {
61
+ if (this.group) {
62
+ return this.group;
63
+ }
64
+
65
+ return null;
66
+ }
59
67
  }
60
68
 
package/src/tools/Pen.ts CHANGED
@@ -3,7 +3,7 @@ import Editor from '../Editor';
3
3
  import EditorImage from '../EditorImage';
4
4
  import Pointer, { PointerDevice } from '../Pointer';
5
5
  import { makeFreehandLineBuilder } from '../components/builders/FreehandLineBuilder';
6
- import { EditorEventType, PointerEvt, StrokeDataPoint } from '../types';
6
+ import { EditorEventType, KeyPressEvent, PointerEvt, StrokeDataPoint } from '../types';
7
7
  import BaseTool from './BaseTool';
8
8
  import { ComponentBuilder, ComponentBuilderFactory } from '../components/builders/types';
9
9
 
@@ -159,4 +159,23 @@ export default class Pen extends BaseTool {
159
159
  public getThickness() { return this.style.thickness; }
160
160
  public getColor() { return this.style.color; }
161
161
  public getStrokeFactory() { return this.builderFactory; }
162
+
163
+ public onKeyPress({ key }: KeyPressEvent): boolean {
164
+ key = key.toLowerCase();
165
+
166
+ let newThickness: number|undefined;
167
+ if (key === '-' || key === '_') {
168
+ newThickness = this.getThickness() * 2/3;
169
+ } else if (key === '+' || key === '=') {
170
+ newThickness = this.getThickness() * 3/2;
171
+ }
172
+
173
+ if (newThickness !== undefined) {
174
+ newThickness = Math.min(Math.max(1, newThickness), 128);
175
+ this.setThickness(newThickness);
176
+ return true;
177
+ }
178
+
179
+ return false;
180
+ }
162
181
  }
@@ -11,6 +11,7 @@ import { ToolLocalization } from './localization';
11
11
  import UndoRedoShortcut from './UndoRedoShortcut';
12
12
  import TextTool from './TextTool';
13
13
  import PipetteTool from './PipetteTool';
14
+ import ToolSwitcherShortcut from './ToolSwitcherShortcut';
14
15
 
15
16
  export default class ToolController {
16
17
  private tools: BaseTool[];
@@ -26,9 +27,6 @@ export default class ToolController {
26
27
  const keyboardPanZoomTool = new PanZoom(editor, PanZoomMode.Keyboard, localization.keyboardPanZoom);
27
28
  const primaryPenTool = new Pen(editor, localization.penTool(1), { color: Color4.purple, thickness: 16 });
28
29
  const primaryTools = [
29
- new SelectionTool(editor, localization.selectionTool),
30
- new Eraser(editor, localization.eraserTool),
31
-
32
30
  // Three pens
33
31
  primaryPenTool,
34
32
  new Pen(editor, localization.penTool(2), { color: Color4.clay, thickness: 4 }),
@@ -36,6 +34,8 @@ export default class ToolController {
36
34
  // Highlighter-like pen with width=64
37
35
  new Pen(editor, localization.penTool(3), { color: Color4.ofRGBA(1, 1, 0, 0.5), thickness: 64 }),
38
36
 
37
+ new Eraser(editor, localization.eraserTool),
38
+ new SelectionTool(editor, localization.selectionTool),
39
39
  new TextTool(editor, localization.textTool, localization),
40
40
  ];
41
41
  this.tools = [
@@ -44,6 +44,7 @@ export default class ToolController {
44
44
  ...primaryTools,
45
45
  keyboardPanZoomTool,
46
46
  new UndoRedoShortcut(editor),
47
+ new ToolSwitcherShortcut(editor),
47
48
  ];
48
49
  primaryTools.forEach(tool => tool.setToolGroup(primaryToolGroup));
49
50
  panZoomTool.setEnabled(true);
@@ -81,6 +82,12 @@ export default class ToolController {
81
82
  this.addTool(tool);
82
83
  }
83
84
 
85
+ public getPrimaryTools(): BaseTool[] {
86
+ return this.tools.filter(tool => {
87
+ return tool.getToolGroup() === this.primaryToolGroup;
88
+ });
89
+ }
90
+
84
91
  // Add a tool to the end of this' tool list (the added tool receives events after tools already added to this).
85
92
  // This should be called before creating the app's toolbar.
86
93
  public addTool(tool: BaseTool) {
@@ -0,0 +1,34 @@
1
+ // Handles ctrl+1, ctrl+2, ctrl+3, ..., shortcuts for switching tools.
2
+ // @packageDocumentation
3
+
4
+ import Editor from '../Editor';
5
+ import { KeyPressEvent } from '../types';
6
+ import BaseTool from './BaseTool';
7
+
8
+ // {@inheritDoc ToolSwitcherShortcut!}
9
+ export default class ToolSwitcherShortcut extends BaseTool {
10
+ public constructor(private editor: Editor) {
11
+ super(editor.notifier, editor.localization.changeTool);
12
+ }
13
+
14
+ public onKeyPress({ key }: KeyPressEvent): boolean {
15
+ const toolController = this.editor.toolController;
16
+ const primaryTools = toolController.getPrimaryTools();
17
+
18
+ // Map keys 0-9 to primary tools.
19
+ const keyMatch = /^[0-9]$/.exec(key);
20
+
21
+ let targetTool: BaseTool|undefined;
22
+ if (keyMatch) {
23
+ const targetIdx = parseInt(keyMatch[0], 10) - 1;
24
+ targetTool = primaryTools[targetIdx];
25
+ }
26
+
27
+ if (targetTool) {
28
+ targetTool.setEnabled(true);
29
+ return true;
30
+ }
31
+
32
+ return false;
33
+ }
34
+ }
package/src/tools/lib.ts CHANGED
@@ -8,6 +8,7 @@ export { default as ToolController } from './ToolController';
8
8
  export { default as ToolEnabledGroup } from './ToolEnabledGroup';
9
9
 
10
10
  export { default as UndoRedoShortcut } from './UndoRedoShortcut';
11
+ export { default as ToolSwitcherShortcut } from './ToolSwitcherShortcut';
11
12
  export { default as PanZoomTool, PanZoomMode } from './PanZoom';
12
13
 
13
14
  export { default as PenTool, PenStyle } from './Pen';
@@ -12,6 +12,7 @@ export interface ToolLocalization {
12
12
 
13
13
  textTool: string;
14
14
  enterTextToInsert: string;
15
+ changeTool: string;
15
16
 
16
17
  toolEnabledAnnouncement: (toolName: string) => string;
17
18
  toolDisabledAnnouncement: (toolName: string) => string;
@@ -30,6 +31,7 @@ export const defaultToolLocalization: ToolLocalization = {
30
31
 
31
32
  textTool: 'Text',
32
33
  enterTextToInsert: 'Text to insert',
34
+ changeTool: 'Change tool',
33
35
 
34
36
  toolEnabledAnnouncement: (toolName) => `${toolName} enabled`,
35
37
  toolDisabledAnnouncement: (toolName) => `${toolName} disabled`,
package/src/types.ts CHANGED
@@ -10,6 +10,7 @@ import Rect2 from './math/Rect2';
10
10
  import Pointer from './Pointer';
11
11
  import Color4 from './Color4';
12
12
  import Command from './commands/Command';
13
+ import { BaseWidget } from './lib';
13
14
 
14
15
 
15
16
  export interface PointerEvtListener {
@@ -88,6 +89,10 @@ export type EditorNotifier = EventDispatcher<EditorEventType, EditorEventDataTyp
88
89
 
89
90
 
90
91
 
92
+
93
+
94
+
95
+
91
96
  export enum EditorEventType {
92
97
  ToolEnabled,
93
98
  ToolDisabled,
@@ -103,6 +108,7 @@ export enum EditorEventType {
103
108
 
104
109
  ColorPickerToggled,
105
110
  ColorPickerColorSelected,
111
+ ToolbarDropdownShown,
106
112
  }
107
113
 
108
114
  type EditorToolEventType = EditorEventType.ToolEnabled
@@ -157,10 +163,16 @@ export interface ColorPickerColorSelected {
157
163
  readonly color: Color4;
158
164
  }
159
165
 
166
+ export interface ToolbarDropdownShownEvent {
167
+ readonly kind: EditorEventType.ToolbarDropdownShown;
168
+ readonly parentWidget: BaseWidget;
169
+ }
170
+
160
171
  export type EditorEventDataType = EditorToolEvent | EditorObjectEvent
161
172
  | EditorViewportChangedEvent | DisplayResizedEvent
162
173
  | EditorUndoStackUpdated | CommandDoneEvent | CommandUndoneEvent
163
- | ColorPickerToggled | ColorPickerColorSelected;
174
+ | ColorPickerToggled | ColorPickerColorSelected
175
+ | ToolbarDropdownShownEvent;
164
176
 
165
177
 
166
178
  // Returns a Promise to indicate that the event source should pause until the Promise resolves.