@shapediver/viewer.shared.services 1.15.5 → 2.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 (79) hide show
  1. package/dist/converter/Converter.d.ts.map +1 -1
  2. package/dist/converter/Converter.js +2 -2
  3. package/dist/converter/Converter.js.map +1 -1
  4. package/dist/dom-event-engine/DomEventEngine.d.ts +1 -2
  5. package/dist/dom-event-engine/DomEventEngine.d.ts.map +1 -1
  6. package/dist/dom-event-engine/DomEventEngine.js +1 -2
  7. package/dist/dom-event-engine/DomEventEngine.js.map +1 -1
  8. package/dist/event-engine/EventEngine.d.ts +3 -3
  9. package/dist/event-engine/EventEngine.d.ts.map +1 -1
  10. package/dist/event-engine/EventEngine.js +2 -2
  11. package/dist/event-engine/EventEngine.js.map +1 -1
  12. package/dist/event-engine/EventTypes.d.ts +20 -24
  13. package/dist/event-engine/EventTypes.d.ts.map +1 -1
  14. package/dist/event-engine/EventTypes.js +55 -85
  15. package/dist/event-engine/EventTypes.js.map +1 -1
  16. package/dist/http-client/HttpClient.d.ts +11 -8
  17. package/dist/http-client/HttpClient.d.ts.map +1 -1
  18. package/dist/http-client/HttpClient.js +102 -27
  19. package/dist/http-client/HttpClient.js.map +1 -1
  20. package/dist/index.d.ts +4 -4
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +10 -3
  23. package/dist/index.js.map +1 -1
  24. package/dist/input-validator/InputValidator.d.ts +3 -3
  25. package/dist/input-validator/InputValidator.d.ts.map +1 -1
  26. package/dist/input-validator/InputValidator.js +6 -2
  27. package/dist/input-validator/InputValidator.js.map +1 -1
  28. package/dist/logger/Logger.d.ts +16 -16
  29. package/dist/logger/Logger.d.ts.map +1 -1
  30. package/dist/logger/Logger.js +77 -76
  31. package/dist/logger/Logger.js.map +1 -1
  32. package/dist/logger/ShapeDiverError.d.ts +1 -1
  33. package/dist/logger/ShapeDiverError.d.ts.map +1 -1
  34. package/dist/logger/ShapeDiverError.js +1 -1
  35. package/dist/logger/ShapeDiverError.js.map +1 -1
  36. package/dist/logger/ShapeDiverViewerErrors.d.ts +24 -24
  37. package/dist/logger/ShapeDiverViewerErrors.d.ts.map +1 -1
  38. package/dist/logger/ShapeDiverViewerErrors.js +1 -1
  39. package/dist/logger/ShapeDiverViewerErrors.js.map +1 -1
  40. package/dist/performance-evaluator/PerformanceEvaluator.js +1 -1
  41. package/dist/performance-evaluator/PerformanceEvaluator.js.map +1 -1
  42. package/dist/settings-engine/SettingsEngine.d.ts +1 -1
  43. package/dist/settings-engine/SettingsEngine.d.ts.map +1 -1
  44. package/dist/settings-engine/SettingsEngine.js +16 -25
  45. package/dist/settings-engine/SettingsEngine.js.map +1 -1
  46. package/dist/state-engine/StateEngine.d.ts +7 -16
  47. package/dist/state-engine/StateEngine.d.ts.map +1 -1
  48. package/dist/state-engine/StateEngine.js +8 -21
  49. package/dist/state-engine/StateEngine.js.map +1 -1
  50. package/dist/system-info/SystemInfo.js +1 -1
  51. package/dist/system-info/SystemInfo.js.map +1 -1
  52. package/dist/type-check/TypeChecker.js +1 -1
  53. package/dist/type-check/TypeChecker.js.map +1 -1
  54. package/dist/uuid-generator/UuidGenerator.js +5 -5
  55. package/dist/uuid-generator/UuidGenerator.js.map +1 -1
  56. package/package.json +12 -9
  57. package/src/converter/Converter.ts +299 -0
  58. package/src/dom-event-engine/DomEventEngine.ts +334 -0
  59. package/src/dom-event-engine/IDomEventListener.ts +14 -0
  60. package/src/event-engine/EventEngine.ts +116 -0
  61. package/src/event-engine/EventTypes.ts +66 -0
  62. package/src/event-engine/interfaces/ICallback.ts +5 -0
  63. package/src/event-engine/interfaces/IEvent.ts +1 -0
  64. package/src/event-engine/interfaces/IListener.ts +8 -0
  65. package/src/http-client/HttpClient.ts +177 -0
  66. package/src/http-client/HttpResponse.ts +4 -0
  67. package/src/index.ts +82 -0
  68. package/src/input-validator/InputValidator.ts +100 -0
  69. package/src/logger/Logger.ts +297 -0
  70. package/src/logger/ShapeDiverError.ts +48 -0
  71. package/src/logger/ShapeDiverViewerErrors.ts +115 -0
  72. package/src/performance-evaluator/PerformanceEvaluator.ts +102 -0
  73. package/src/settings-engine/SettingsEngine.ts +176 -0
  74. package/src/state-engine/StateEngine.ts +73 -0
  75. package/src/state-engine/StatePromise.ts +54 -0
  76. package/src/system-info/SystemInfo.ts +117 -0
  77. package/src/type-check/TypeChecker.ts +13 -0
  78. package/src/uuid-generator/UuidGenerator.ts +41 -0
  79. package/tsconfig.json +17 -0
@@ -0,0 +1,299 @@
1
+ import { vec3, vec4 } from 'gl-matrix'
2
+ import { TinyColor } from '@ctrl/tinycolor'
3
+ import { container, singleton } from 'tsyringe'
4
+ import { HttpClient } from '../http-client/HttpClient';
5
+ import { HttpResponse } from '../http-client/HttpResponse';
6
+
7
+ @singleton()
8
+ export class Converter {
9
+ private readonly _httpClient: HttpClient = <HttpClient>container.resolve(HttpClient);
10
+
11
+ private tinyColorToString(color: TinyColor): string {
12
+ return color.toHex8String();
13
+ }
14
+
15
+ /**
16
+ * @param color
17
+ * @param defColor
18
+ */
19
+ public toHex8Color(color: any, defColorString: string = '#00fff7'): string {
20
+ const c = this.toColor(color, defColorString);
21
+ const tColor = new TinyColor(c);
22
+ const cH8 = tColor.toHex8String();
23
+ return cH8.replace('#', '0x');
24
+ }
25
+
26
+ public toColorArray(color: string): number[] {
27
+ const tColor = new TinyColor(color);
28
+ const rgb = tColor.toRgb()
29
+ return [rgb.r / 255.0, rgb.g / 255.0, rgb.b / 255.0];
30
+ }
31
+
32
+ public toAlpha(color: string): number {
33
+ const c = this.toColor(color);
34
+ if (c.length <= 8) return 1;
35
+ return parseInt(c.slice(c.length - 2, c.length), 16) / 255;
36
+ }
37
+
38
+ public toThreeJsColorInput(color: string): string {
39
+ const c = this.toColor(color);
40
+ return c.slice(0, c.length - 2);
41
+ }
42
+
43
+
44
+ public async processSVG(blob: Blob): Promise<HTMLImageElement> {
45
+ let data = <string>await new Promise((resolve, _) => {
46
+ const reader = new FileReader();
47
+ reader.onloadend = () => resolve(<string>reader.result);
48
+ reader.readAsDataURL(blob);
49
+ });
50
+ data = data.replace('data:image/svg+xml;base64,', '')
51
+ data = atob(data);
52
+
53
+ let svgC = document.createElement('DIV');
54
+ svgC.id = 'svgc';
55
+ svgC.innerHTML = <string>data;
56
+
57
+ // now we can access the svg element as a DOM object
58
+ let svgE = svgC.getElementsByTagName('svg');
59
+ let childImageURIs: string[] = [];
60
+ let styleURIs: string[] = [];
61
+
62
+ // collect image urls
63
+ for (let i = 0; i < svgE.length; ++i) {
64
+ for (let j = 0; j < 2; ++j) {
65
+ let childImages = <HTMLCollectionOf<SVGImageElement>>svgE[i].getElementsByTagName(['image', 'img'][j]);
66
+ for (let k = 0; k < childImages.length; ++k) {
67
+ if (childImages[k].href.baseVal.substring(0, 5) != 'data:') {
68
+ childImageURIs.push(childImages[k].href.baseVal);
69
+ }
70
+ }
71
+ }
72
+ // collect potential font definitions
73
+ // we assume styles are imported using the following syntax:
74
+ // @import url(CSS_URL);
75
+ let styleElements = <HTMLCollectionOf<HTMLStyleElement>>svgE[i].getElementsByTagName('style');
76
+ for (let j = 0; j < styleElements.length; ++j) {
77
+ let regex = /@import\x20url\(\s*(.*?)\s*\);/g;
78
+ let m;
79
+ while ((m = regex.exec(styleElements[j].innerHTML)) !== null) {
80
+ styleURIs.push(m[1]);
81
+ }
82
+ // make unique
83
+ styleURIs = styleURIs.filter(
84
+ function (value, index, self) {
85
+ return self.indexOf(value) === index;
86
+ }
87
+ );
88
+ }
89
+ }
90
+
91
+ // creating a promise for each image which needs to be converted to a data URI
92
+ let replacementPromises = [];
93
+ let createImagePromise = async (uri: string) => {
94
+ if (uri.length > 0) {
95
+ const response = await this._httpClient.loadTexture(uri);
96
+ let uInt8Array = new Uint8Array(response.data), i = uInt8Array.length;
97
+ let biStr = []; //new Array(i);
98
+ while (i--)
99
+ biStr[i] = String.fromCharCode(uInt8Array[i]);
100
+
101
+ let base64Data = window.btoa(biStr.join(''));
102
+ let imgDataUrl = 'data:' + response.headers['content-type'] + ';base64,' + base64Data;
103
+
104
+ // replace url in SVG string
105
+ // CAUTION theoretically this could cause unwanted replacements
106
+ data = data.replace(uri, imgDataUrl);
107
+ }
108
+ };
109
+
110
+ for (let i = 0; i < childImageURIs.length; ++i)
111
+ replacementPromises.push(createImagePromise(childImageURIs[i]));
112
+
113
+ // now we create promises for the google fonts to be imported
114
+ let createStylePromise = async (styleUrl: string) => {
115
+ const response = await this._httpClient.get(
116
+ styleUrl,
117
+ { responseType: 'text' }
118
+ );
119
+ let cssString = response.data;
120
+ // we assume that fonts are imported using the following syntax:
121
+ // url(FONT_URI);
122
+ let fontURLs = [];
123
+ let regex = /url\(\s*(.*?)\s*\)/g;
124
+ let m;
125
+ while ((m = regex.exec(cssString)) !== null) {
126
+ fontURLs.push(m[1]);
127
+ }
128
+
129
+ let fontPromises = [];
130
+ let createFontPromise = async (fUrl: string) => {
131
+ const response = await this._httpClient.get(
132
+ fUrl,
133
+ { responseType: 'arraybuffer' }
134
+ );
135
+ let uInt8Array = new Uint8Array(response.data), i = uInt8Array.length;
136
+ let biStr = []; //new Array(i);
137
+ while (i--)
138
+ biStr[i] = String.fromCharCode(uInt8Array[i]);
139
+
140
+ let base64Data = window.btoa(biStr.join(''));
141
+ let fontDataUrl = 'data:' + response.headers['content-type'] + ';base64,' + base64Data;
142
+ if (fUrl.length > 0)
143
+ cssString = cssString.replace(fUrl, fontDataUrl);
144
+ };
145
+
146
+ for (let j = 0; j < fontURLs.length; ++j)
147
+ fontPromises.push(createFontPromise(fontURLs[j]));
148
+
149
+ await Promise.all(fontPromises);
150
+ data = data.replace('@import url(' + styleUrl + ');', cssString);
151
+ };
152
+
153
+ for (let i = 0; i < styleURIs.length; ++i)
154
+ replacementPromises.push(createStylePromise(styleURIs[i]));
155
+
156
+ await Promise.all(replacementPromises);
157
+
158
+ let du = 'data:image/svg+xml,' + encodeURIComponent(data);
159
+ let img = new Image(); // same as document.createElement('img')
160
+ img.crossOrigin = 'Anonymous';
161
+ const promise = new Promise<void>(resolve => {
162
+ img.onload = () => resolve();
163
+ })
164
+ img.src = du;
165
+ await promise;
166
+ return img;
167
+ }
168
+
169
+ /**
170
+ * This color converter is mostly left 'as-is' from viewer v2.
171
+ * I didn't want to break something that works.
172
+ *
173
+ * @param color
174
+ * @param defColor
175
+ */
176
+ public toColor(color: any, defColorString: string = '#00fff7'): string {
177
+ if (!color || color === 'default') return defColorString;
178
+
179
+ if (color.constructor === Float32Array)
180
+ color = Array.from(color);
181
+
182
+ const tColor = new TinyColor(color);
183
+
184
+ if (color instanceof TinyColor)
185
+ return this.tinyColorToString(tColor);
186
+
187
+ // check if we got a number
188
+ if (typeof color === 'number') {
189
+ let cs = color.toString(16);
190
+ let cl = cs.length;
191
+ if (cl < 3) cs = cs.padStart(3, '0');
192
+ else if (cl < 6) cs = cs.padStart(6, '0');
193
+ else if (cl < 8) cs = cs.padEnd(8, '0');
194
+ let tc = new TinyColor(cs);
195
+ return tc.isValid ? this.tinyColorToString(tc) : defColorString;
196
+ }
197
+
198
+ // check if the input is a THREE.Color
199
+ if (color.isColor && typeof color.getHexString == 'function') {
200
+ let tc = new TinyColor(color.getHexString());
201
+ return tc.isValid ? this.tinyColorToString(tc) : defColorString;
202
+ }
203
+
204
+ // check for array of numbers
205
+ if (Array.isArray(color) && (color.length == 3 || color.length == 4)) {
206
+ let isRGBArray = true;
207
+ for (let i = 0; i < 3; ++i) {
208
+ color[i] = parseFloat(color[i]);
209
+ if (isNaN(color[i])) {
210
+ isRGBArray = false;
211
+ }
212
+ }
213
+ if (!isRGBArray)
214
+ return defColorString;
215
+
216
+ let tc = new TinyColor({
217
+ r: Math.max(0, Math.min(color[0], 255)),
218
+ g: Math.max(0, Math.min(color[1], 255)),
219
+ b: Math.max(0, Math.min(color[2], 255))
220
+ });
221
+ if (color.length == 4) {
222
+ let a = parseFloat(color[3]);
223
+ if (!isNaN(a)) {
224
+ tc.setAlpha(Math.max(0, Math.min(a, 255)) / 255);
225
+ }
226
+ }
227
+ return tc.isValid ? this.tinyColorToString(tc) : defColorString;
228
+ }
229
+
230
+ // if we got something other than a string, check if
231
+ // tinycolor can work with it
232
+ if (typeof color !== 'string') {
233
+ let tc = new TinyColor(color);
234
+ return tc.isValid ? this.tinyColorToString(tc) : defColorString;
235
+ }
236
+
237
+ // tinycolor doesn't like 0x
238
+ let tmpColor = color.replace('0x', '#');
239
+
240
+ // if we got no alpha value, add full opacity
241
+ if (tmpColor.match(/^#[a-f0-9]{6}$/i) !== null) {
242
+ let tc = new TinyColor(tmpColor + 'ff');
243
+ return tc.isValid ? this.tinyColorToString(tc) : defColorString;
244
+ }
245
+
246
+ // standard case
247
+ if (tmpColor.match(/^#[a-f0-9]{8}$/i) !== null) {
248
+ let tc = new TinyColor(tmpColor);
249
+ return tc.isValid ? this.tinyColorToString(tc) : defColorString;
250
+ }
251
+
252
+ // correct number which have the alpha value defined as a single hex digit
253
+ if (tmpColor.match(/^#[a-f0-9]{7}$/i) !== null) {
254
+ let tc = new TinyColor(tmpColor.slice(0, 7) + '0' + tmpColor.slice(-1));
255
+ return tc.isValid ? this.tinyColorToString(tc) : defColorString;
256
+ }
257
+
258
+ // check if tinycolor understands the string
259
+ let tc = new TinyColor(tmpColor);
260
+ return tc.isValid ? this.tinyColorToString(tc) : defColorString;
261
+ }
262
+
263
+ public toVec3(point: any): vec3 {
264
+ if (Array.isArray(point) && point.length >= 3 && typeof point[0] === 'number' && typeof point[1] === 'number' && typeof point[2] === 'number')
265
+ return vec3.fromValues(point[0], point[1], point[2]);
266
+
267
+ if (((point.x || point.x === 0) && typeof point.x === 'number') && ((point.y || point.y === 0) && typeof point.y === 'number') && ((point.z || point.z === 0) && typeof point.z === 'number'))
268
+ return vec3.fromValues(point.x, point.y, point.z);
269
+
270
+ if (((point.X || point.X === 0) && typeof point.X === 'number') && ((point.Y || point.Y === 0) && typeof point.Y === 'number') && ((point.Z || point.Z === 0) && typeof point.Z === 'number'))
271
+ return vec3.fromValues(point.X, point.Y, point.Z);
272
+
273
+ return vec3.create();
274
+ }
275
+
276
+ public async responseToImage(response: HttpResponse<ArrayBuffer>): Promise<HTMLImageElement> {
277
+ const arrayBufferView = new Uint8Array( response.data );
278
+ const blob = new Blob([ arrayBufferView ], { type: response.headers['content-type'] } );
279
+ if (response.headers['content-type'] === 'image/svg+xml') {
280
+ const img = await this.processSVG(blob);
281
+ return img;
282
+ } else {
283
+ const img = new Image();
284
+ const promise = new Promise<void>(resolve => {
285
+ img.onload = () => resolve();
286
+ })
287
+ img.crossOrigin = "anonymous";
288
+
289
+ let data = <string>await new Promise((resolve, _) => {
290
+ const reader = new FileReader();
291
+ reader.onloadend = () => resolve(<string>reader.result);
292
+ reader.readAsDataURL(blob);
293
+ });
294
+ img.src = data;
295
+ await promise;
296
+ return img;
297
+ }
298
+ }
299
+ }
@@ -0,0 +1,334 @@
1
+ import { container } from 'tsyringe'
2
+ import { UuidGenerator } from '../uuid-generator/UuidGenerator';
3
+
4
+ import { IDomEventListener } from './IDomEventListener'
5
+
6
+ export class DomEventEngine {
7
+ // #region Properties (5)
8
+
9
+ private readonly _domEventListeners: {
10
+ [key: string]: IDomEventListener
11
+ } = {};
12
+ private readonly _uuidGenerator: UuidGenerator = <UuidGenerator>container.resolve(UuidGenerator);
13
+
14
+ private _allowListeners = {
15
+ mousewheel: true,
16
+ mousedown: true,
17
+ mousemove: true,
18
+ mouseup: true,
19
+ mouseout: true,
20
+ touchstart: true,
21
+ touchmove: true,
22
+ touchend: true,
23
+ touchcancel: true,
24
+ keydown: true,
25
+ contextmenu: true,
26
+ };
27
+ private _canvas: HTMLCanvasElement;
28
+ private _currentMousePosition: { x: number, y: number } = { x: 0, y: 0 };
29
+ private _onMouseWheel: (event: Event) => void;
30
+ private _onMouseDown: (event: MouseEvent) => void;
31
+ private _onMouseMove: (event: MouseEvent) => void;
32
+ private _onKeyDownMousePositionHelper: (event: MouseEvent) => void;
33
+ private _onMouseUp: (event: MouseEvent) => void;
34
+ private _onMouseOut: (event: MouseEvent) => void;
35
+ private _onTouchStart: (event: TouchEvent) => void;
36
+ private _onTouchMove: (event: TouchEvent) => void;
37
+ private _onTouchUp: (event: TouchEvent) => void;
38
+ private _onTouchCancel: (event: TouchEvent) => void;
39
+ private _onKeyDown: (event: KeyboardEvent) => void;
40
+ private _onContextMenu: (event: MouseEvent) => void;
41
+
42
+ // #endregion Properties (5)
43
+
44
+ // #region Constructors (1)
45
+
46
+ constructor(canvas: HTMLCanvasElement) {
47
+ this._canvas = canvas;
48
+ this._onMouseWheel = this.onMouseWheel.bind(this);
49
+ this._onMouseDown = this.onMouseDown.bind(this);
50
+ this._onMouseMove = this.onMouseMove.bind(this)
51
+ this._onKeyDownMousePositionHelper = this.onKeyDownMousePositionHelper.bind(this)
52
+ this._onMouseUp = this.onMouseUp.bind(this)
53
+ this._onMouseOut = this.onMouseOut.bind(this)
54
+ this._onTouchStart = this.onTouchStart.bind(this)
55
+ this._onTouchMove = this.onTouchMove.bind(this)
56
+ this._onTouchUp = this.onTouchUp.bind(this)
57
+ this._onTouchCancel = this.onTouchCancel.bind(this)
58
+ this._onKeyDown = this.onKeyDown.bind(this)
59
+ this._onContextMenu = this.onContextMenu.bind(this)
60
+
61
+ this.addEventListeners();
62
+ }
63
+
64
+ // #endregion Constructors (1)
65
+
66
+ // #region Public Methods (5)
67
+
68
+ public addDomEventListener(listener: IDomEventListener): string {
69
+ const id = this._uuidGenerator.create();
70
+ this._domEventListeners[id] = listener;
71
+ return id;
72
+ }
73
+
74
+ /**
75
+ * Allow / disallow events.
76
+ * This can be used to disable events for a specific viewer.
77
+ *
78
+ * Example use case: If you don't want to allow mouse wheel events for a specific viewer so that users can scroll past the viewer.
79
+ *
80
+ * Be aware that this might cause some issues with the the camera controls if the mouse / touch events are disabled only partially.
81
+ *
82
+ * @param allowedListeners
83
+ */
84
+ public allowEventListeners(allowedListeners: {
85
+ mousewheel?: boolean,
86
+ mousedown?: boolean,
87
+ mousemove?: boolean,
88
+ mouseup?: boolean,
89
+ mouseout?: boolean,
90
+ touchstart?: boolean,
91
+ touchmove?: boolean,
92
+ touchend?: boolean,
93
+ touchcancel?: boolean,
94
+ keydown?: boolean,
95
+ contextmenu?: boolean,
96
+ }): void {
97
+ if (allowedListeners.mousewheel !== undefined && this._allowListeners.mousewheel !== allowedListeners.mousewheel) {
98
+ if (allowedListeners.mousewheel) {
99
+ this._canvas.addEventListener("mousewheel", this._onMouseWheel);
100
+ this._canvas.addEventListener("MozMousePixelScroll", this._onMouseWheel); // firefox
101
+ } else {
102
+ this._canvas.removeEventListener("mousewheel", this._onMouseWheel);
103
+ this._canvas.removeEventListener("MozMousePixelScroll", this._onMouseWheel); // firefox
104
+ }
105
+ this._allowListeners.mousewheel = allowedListeners.mousewheel;
106
+ }
107
+
108
+ if (allowedListeners.mousedown !== undefined && this._allowListeners.mousedown !== allowedListeners.mousedown) {
109
+ if (allowedListeners.mousedown) {
110
+ this._canvas.addEventListener("mousedown", this._onMouseDown);
111
+ } else {
112
+ this._canvas.removeEventListener("mousedown", this._onMouseDown);
113
+ }
114
+ this._allowListeners.mousedown = allowedListeners.mousedown;
115
+ }
116
+
117
+ if (allowedListeners.mousemove !== undefined && this._allowListeners.mousemove !== allowedListeners.mousemove) {
118
+ if (allowedListeners.mousemove) {
119
+ this._canvas.addEventListener("mousemove", this._onMouseMove);
120
+ window.addEventListener("mousemove", this._onKeyDownMousePositionHelper);
121
+ } else {
122
+ this._canvas.removeEventListener("mousemove", this._onMouseMove);
123
+ window.removeEventListener("mousemove", this._onKeyDownMousePositionHelper);
124
+ }
125
+ this._allowListeners.mousemove = allowedListeners.mousemove;
126
+ }
127
+
128
+ if (allowedListeners.mouseup !== undefined && this._allowListeners.mouseup !== allowedListeners.mouseup) {
129
+ if (allowedListeners.mouseup) {
130
+ this._canvas.addEventListener("mouseup", this._onMouseUp);
131
+ } else {
132
+ this._canvas.removeEventListener("mouseup", this._onMouseUp);
133
+ }
134
+ this._allowListeners.mouseup = allowedListeners.mouseup;
135
+ }
136
+
137
+ if (allowedListeners.mouseout !== undefined && this._allowListeners.mouseout !== allowedListeners.mouseout) {
138
+ if (allowedListeners.mouseout) {
139
+ this._canvas.addEventListener("mouseout", this._onMouseOut);
140
+ } else {
141
+ this._canvas.removeEventListener("mouseout", this._onMouseOut);
142
+ }
143
+ this._allowListeners.mouseout = allowedListeners.mouseout;
144
+ }
145
+
146
+ if (allowedListeners.touchstart !== undefined && this._allowListeners.touchstart !== allowedListeners.touchstart) {
147
+ if (allowedListeners.touchstart) {
148
+ window.addEventListener("touchstart", this._onTouchStart);
149
+ } else {
150
+ window.removeEventListener("touchstart", this._onTouchStart);
151
+ }
152
+ this._allowListeners.touchstart = allowedListeners.touchstart;
153
+ }
154
+
155
+ if (allowedListeners.touchmove !== undefined && this._allowListeners.touchmove !== allowedListeners.touchmove) {
156
+ if (allowedListeners.touchmove) {
157
+ window.addEventListener("touchmove", this._onTouchMove);
158
+ } else {
159
+ window.removeEventListener("touchmove", this._onTouchMove);
160
+ }
161
+ this._allowListeners.touchmove = allowedListeners.touchmove;
162
+ }
163
+
164
+ if (allowedListeners.touchend !== undefined && this._allowListeners.touchend !== allowedListeners.touchend) {
165
+ if (allowedListeners.touchend) {
166
+ window.addEventListener("touchend", this._onTouchUp);
167
+ } else {
168
+ window.removeEventListener("touchend", this._onTouchUp);
169
+ }
170
+ this._allowListeners.touchend = allowedListeners.touchend;
171
+ }
172
+
173
+ if (allowedListeners.touchcancel !== undefined && this._allowListeners.touchcancel !== allowedListeners.touchcancel) {
174
+ if (allowedListeners.touchcancel) {
175
+ window.addEventListener("touchcancel", this._onTouchCancel);
176
+ } else {
177
+ window.removeEventListener("touchcancel", this._onTouchCancel);
178
+ }
179
+ this._allowListeners.touchcancel = allowedListeners.touchcancel;
180
+ }
181
+
182
+ if (allowedListeners.keydown !== undefined && this._allowListeners.keydown !== allowedListeners.keydown) {
183
+ if (allowedListeners.keydown) {
184
+ window.addEventListener("keydown", this._onKeyDown);
185
+ } else {
186
+ window.removeEventListener("keydown", this._onKeyDown);
187
+ }
188
+ this._allowListeners.keydown = allowedListeners.keydown;
189
+ }
190
+
191
+ if (allowedListeners.contextmenu !== undefined && this._allowListeners.contextmenu !== allowedListeners.contextmenu) {
192
+ if (allowedListeners.contextmenu) {
193
+ this._canvas.addEventListener("contextmenu", this._onContextMenu);
194
+ } else {
195
+ this._canvas.removeEventListener("contextmenu", this._onContextMenu);
196
+ }
197
+ this._allowListeners.contextmenu = allowedListeners.contextmenu;
198
+ }
199
+ }
200
+
201
+ public dispose() {
202
+ this.removeEventListeners();
203
+ }
204
+
205
+ public removeAllDomEventListener(): void {
206
+ for(let id in this._domEventListeners)
207
+ delete this._domEventListeners[id];
208
+ }
209
+
210
+ public removeDomEventListener(id: string): boolean {
211
+ if(this._domEventListeners[id]) {
212
+ delete this._domEventListeners[id];
213
+ return true;
214
+ }
215
+ return false;
216
+ }
217
+
218
+ // #endregion Public Methods (5)
219
+
220
+ // #region Private Methods (12)
221
+
222
+ private addEventListeners() {
223
+ this._canvas.addEventListener("mousewheel", this._onMouseWheel);
224
+ this._canvas.addEventListener("MozMousePixelScroll", this._onMouseWheel); // firefox
225
+
226
+ this._canvas.addEventListener("mousedown", this._onMouseDown);
227
+ this._canvas.addEventListener("mousemove", this._onMouseMove);
228
+ this._canvas.addEventListener("mouseup", this._onMouseUp);
229
+ this._canvas.addEventListener("mouseout", this._onMouseOut);
230
+
231
+ window.addEventListener("touchstart", this._onTouchStart);
232
+ window.addEventListener("touchmove", this._onTouchMove);
233
+ window.addEventListener("touchend", this._onTouchUp);
234
+ window.addEventListener("touchcancel", this._onTouchCancel);
235
+
236
+ window.addEventListener("keydown", this._onKeyDown);
237
+ window.addEventListener("mousemove", this._onKeyDownMousePositionHelper);
238
+
239
+ // just prevent right click menu
240
+ this._canvas.addEventListener("contextmenu", this._onContextMenu);
241
+ }
242
+
243
+ private onContextMenu(event: MouseEvent): void {
244
+ event.preventDefault();
245
+ }
246
+
247
+ private onKeyDown(event: KeyboardEvent): void {
248
+ if (this._canvas === document.elementFromPoint(this._currentMousePosition.x, this._currentMousePosition.y))
249
+ Object.values(this._domEventListeners).forEach(e => e.onKeyDown(event));
250
+ }
251
+
252
+ private onKeyDownMousePositionHelper(event: MouseEvent): void {
253
+ this._currentMousePosition = { x: event.pageX, y: event.pageY };
254
+ }
255
+
256
+ private onMouseDown(event: MouseEvent): void {
257
+ event.preventDefault();
258
+ Object.values(this._domEventListeners).forEach(e => e.onMouseDown(event));
259
+ }
260
+
261
+ private onMouseMove(event: MouseEvent): void {
262
+ event.preventDefault();
263
+ Object.values(this._domEventListeners).forEach(e => e.onMouseMove(event));
264
+ }
265
+
266
+ private onMouseUp(event: MouseEvent): void {
267
+ event.preventDefault();
268
+ Object.values(this._domEventListeners).forEach(e => e.onMouseUp(event));
269
+ Object.values(this._domEventListeners).forEach(e => e.onMouseEnd(event));
270
+ }
271
+
272
+ private onMouseOut(event: MouseEvent): void {
273
+ event.preventDefault();
274
+ Object.values(this._domEventListeners).forEach(e => e.onMouseOut(event));
275
+ Object.values(this._domEventListeners).forEach(e => e.onMouseEnd(event));
276
+ }
277
+
278
+ private onMouseWheel(event: Event): void {
279
+ event.preventDefault();
280
+ event.stopPropagation();
281
+ Object.values(this._domEventListeners).forEach(e => e.onMouseWheel(<WheelEvent>event));
282
+ }
283
+
284
+ private onTouchUp(event: TouchEvent): void {
285
+ if (event.composedPath().includes(this._canvas)) {
286
+ event.preventDefault();
287
+ Object.values(this._domEventListeners).forEach(e => e.onTouchUp(event));
288
+ Object.values(this._domEventListeners).forEach(e => e.onTouchEnd(event));
289
+ }
290
+ }
291
+
292
+ private onTouchCancel(event: TouchEvent): void {
293
+ if (event.composedPath().includes(this._canvas)) {
294
+ event.preventDefault();
295
+ Object.values(this._domEventListeners).forEach(e => e.onTouchCancel(event));
296
+ Object.values(this._domEventListeners).forEach(e => e.onTouchEnd(event));
297
+ }
298
+ }
299
+
300
+ private onTouchMove(event: TouchEvent): void {
301
+ if (event.composedPath().includes(this._canvas)) {
302
+ event.preventDefault();
303
+ Object.values(this._domEventListeners).forEach(e => e.onTouchMove(event))
304
+ }
305
+ }
306
+
307
+ private onTouchStart(event: TouchEvent): void {
308
+ if (event.composedPath().includes(this._canvas)) {
309
+ event.preventDefault();
310
+ Object.values(this._domEventListeners).forEach(e => e.onTouchStart(event));
311
+ }
312
+ }
313
+
314
+ private removeEventListeners() {
315
+ this._canvas.removeEventListener("mousewheel", this._onMouseWheel);
316
+ this._canvas.removeEventListener("MozMousePixelScroll", this._onMouseWheel); // firefox
317
+
318
+ this._canvas.removeEventListener("mousedown", this._onMouseDown);
319
+ this._canvas.removeEventListener("mousemove", this._onMouseMove);
320
+ this._canvas.removeEventListener("mouseup", this._onMouseUp);
321
+ this._canvas.removeEventListener("mouseout", this._onMouseOut);
322
+
323
+ window.removeEventListener("touchstart", this._onTouchStart);
324
+ window.removeEventListener("touchmove", this._onTouchMove);
325
+ window.removeEventListener("touchend", this._onTouchUp);
326
+ window.removeEventListener("touchcancel", this._onTouchCancel);
327
+
328
+ window.removeEventListener("keydown", this._onKeyDown);
329
+ window.removeEventListener("mousemove", this._onKeyDownMousePositionHelper);
330
+ this._canvas.removeEventListener("contextmenu", this._onContextMenu);
331
+ }
332
+
333
+ // #endregion Private Methods (12)
334
+ }
@@ -0,0 +1,14 @@
1
+ export interface IDomEventListener {
2
+ onKeyDown(event: KeyboardEvent): void;
3
+ onMouseDown(event: MouseEvent): void;
4
+ onMouseMove(event: MouseEvent): void;
5
+ onMouseEnd(event: MouseEvent): void;
6
+ onMouseUp(event: MouseEvent): void;
7
+ onMouseOut(event: MouseEvent): void;
8
+ onMouseWheel(event: WheelEvent): void;
9
+ onTouchEnd(event: TouchEvent): void;
10
+ onTouchUp(event: TouchEvent): void;
11
+ onTouchCancel(event: TouchEvent): void;
12
+ onTouchMove(event: TouchEvent): void;
13
+ onTouchStart(event: TouchEvent): void;
14
+ }