@vpmedia/phaser 1.119.0 → 1.121.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 (156) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +499 -607
  4. package/dist/index.js.map +1 -1
  5. package/dist/phaser/core/animation.d.ts.map +1 -1
  6. package/dist/phaser/core/animation_manager.d.ts.map +1 -1
  7. package/dist/phaser/core/animation_parser.d.ts +1 -1
  8. package/dist/phaser/core/array_set.d.ts.map +1 -1
  9. package/dist/phaser/core/callback.d.ts +12 -0
  10. package/dist/phaser/core/callback.d.ts.map +1 -0
  11. package/dist/phaser/core/device.d.ts +1 -5
  12. package/dist/phaser/core/device.d.ts.map +1 -1
  13. package/dist/phaser/core/dom.d.ts.map +1 -1
  14. package/dist/phaser/core/factory.d.ts +2 -1
  15. package/dist/phaser/core/factory.d.ts.map +1 -1
  16. package/dist/phaser/core/game.d.ts +3 -0
  17. package/dist/phaser/core/game.d.ts.map +1 -1
  18. package/dist/phaser/core/input.d.ts +11 -9
  19. package/dist/phaser/core/input.d.ts.map +1 -1
  20. package/dist/phaser/core/input_event.d.ts +1 -1
  21. package/dist/phaser/core/input_event.d.ts.map +1 -1
  22. package/dist/phaser/core/input_handler.d.ts.map +1 -1
  23. package/dist/phaser/core/input_mouse.d.ts.map +1 -1
  24. package/dist/phaser/core/input_mspointer.d.ts.map +1 -1
  25. package/dist/phaser/core/input_pointer.d.ts +3 -2
  26. package/dist/phaser/core/input_pointer.d.ts.map +1 -1
  27. package/dist/phaser/core/input_touch.d.ts.map +1 -1
  28. package/dist/phaser/core/loader.d.ts +5 -4
  29. package/dist/phaser/core/loader.d.ts.map +1 -1
  30. package/dist/phaser/core/scale_manager.d.ts +5 -4
  31. package/dist/phaser/core/scale_manager.d.ts.map +1 -1
  32. package/dist/phaser/core/scene_manager.d.ts.map +1 -1
  33. package/dist/phaser/core/signal.d.ts +14 -7
  34. package/dist/phaser/core/signal.d.ts.map +1 -1
  35. package/dist/phaser/core/signal_binding.d.ts +10 -10
  36. package/dist/phaser/core/signal_binding.d.ts.map +1 -1
  37. package/dist/phaser/core/sound.d.ts +3 -3
  38. package/dist/phaser/core/sound.d.ts.map +1 -1
  39. package/dist/phaser/core/sound_manager.d.ts +9 -3
  40. package/dist/phaser/core/sound_manager.d.ts.map +1 -1
  41. package/dist/phaser/core/sound_sprite.d.ts +19 -7
  42. package/dist/phaser/core/sound_sprite.d.ts.map +1 -1
  43. package/dist/phaser/core/timer.d.ts +5 -4
  44. package/dist/phaser/core/timer.d.ts.map +1 -1
  45. package/dist/phaser/core/timer_event.d.ts +3 -2
  46. package/dist/phaser/core/timer_event.d.ts.map +1 -1
  47. package/dist/phaser/core/tween.d.ts +7 -6
  48. package/dist/phaser/core/tween.d.ts.map +1 -1
  49. package/dist/phaser/core/tween_data.d.ts +2 -1
  50. package/dist/phaser/core/tween_data.d.ts.map +1 -1
  51. package/dist/phaser/core/tween_easing.d.ts +31 -31
  52. package/dist/phaser/core/tween_manager.d.ts.map +1 -1
  53. package/dist/phaser/display/bitmap_text.d.ts.map +1 -1
  54. package/dist/phaser/display/button.d.ts +2 -1
  55. package/dist/phaser/display/button.d.ts.map +1 -1
  56. package/dist/phaser/display/canvas/pool.d.ts.map +1 -1
  57. package/dist/phaser/display/canvas/renderer.d.ts +1 -1
  58. package/dist/phaser/display/canvas/renderer.d.ts.map +1 -1
  59. package/dist/phaser/display/canvas/tinter.d.ts.map +1 -1
  60. package/dist/phaser/display/canvas/util.d.ts +2 -2
  61. package/dist/phaser/display/canvas/util.d.ts.map +1 -1
  62. package/dist/phaser/display/display_object.d.ts +8 -6
  63. package/dist/phaser/display/display_object.d.ts.map +1 -1
  64. package/dist/phaser/display/graphics.d.ts +7 -3
  65. package/dist/phaser/display/graphics.d.ts.map +1 -1
  66. package/dist/phaser/display/group.d.ts +1 -1
  67. package/dist/phaser/display/group.d.ts.map +1 -1
  68. package/dist/phaser/display/image.d.ts.map +1 -1
  69. package/dist/phaser/display/sprite_util.d.ts.map +1 -1
  70. package/dist/phaser/display/text.d.ts +5 -0
  71. package/dist/phaser/display/text.d.ts.map +1 -1
  72. package/dist/phaser/display/webgl/base_texture.d.ts.map +1 -1
  73. package/dist/phaser/display/webgl/earcut.d.ts.map +1 -1
  74. package/dist/phaser/display/webgl/fast_sprite_batch.d.ts.map +1 -1
  75. package/dist/phaser/display/webgl/filter_manager.d.ts.map +1 -1
  76. package/dist/phaser/display/webgl/graphics.d.ts.map +1 -1
  77. package/dist/phaser/display/webgl/graphics_data.d.ts.map +1 -1
  78. package/dist/phaser/display/webgl/render_texture.d.ts.map +1 -1
  79. package/dist/phaser/display/webgl/renderer.d.ts.map +1 -1
  80. package/dist/phaser/display/webgl/sprite_batch.d.ts.map +1 -1
  81. package/dist/phaser/display/webgl/texture.d.ts.map +1 -1
  82. package/dist/phaser/geom/polygon.d.ts +11 -5
  83. package/dist/phaser/geom/polygon.d.ts.map +1 -1
  84. package/dist/phaser/geom/rectangle.d.ts.map +1 -1
  85. package/dist/phaser/geom/rounded_rectangle.d.ts.map +1 -1
  86. package/dist/phaser/util/math.d.ts.map +1 -1
  87. package/package.json +1 -1
  88. package/src/index.ts +2 -0
  89. package/src/phaser/core/animation.ts +1 -2
  90. package/src/phaser/core/animation_manager.ts +3 -11
  91. package/src/phaser/core/animation_parser.ts +2 -2
  92. package/src/phaser/core/array_set.ts +8 -10
  93. package/src/phaser/core/cache.ts +2 -2
  94. package/src/phaser/core/callback.ts +14 -0
  95. package/src/phaser/core/device.ts +24 -53
  96. package/src/phaser/core/device_util.ts +6 -6
  97. package/src/phaser/core/dom.ts +10 -7
  98. package/src/phaser/core/factory.ts +3 -2
  99. package/src/phaser/core/game.ts +4 -5
  100. package/src/phaser/core/input.ts +16 -12
  101. package/src/phaser/core/input_event.ts +1 -1
  102. package/src/phaser/core/input_handler.ts +21 -22
  103. package/src/phaser/core/input_mouse.ts +12 -17
  104. package/src/phaser/core/input_mspointer.ts +8 -11
  105. package/src/phaser/core/input_pointer.ts +22 -21
  106. package/src/phaser/core/input_touch.ts +16 -19
  107. package/src/phaser/core/loader.ts +75 -86
  108. package/src/phaser/core/loader_parser.ts +6 -6
  109. package/src/phaser/core/scale_manager.ts +34 -42
  110. package/src/phaser/core/scene_manager.ts +2 -1
  111. package/src/phaser/core/signal.ts +21 -13
  112. package/src/phaser/core/signal_binding.ts +20 -21
  113. package/src/phaser/core/sound.ts +9 -11
  114. package/src/phaser/core/sound_manager.ts +53 -47
  115. package/src/phaser/core/sound_sprite.ts +34 -21
  116. package/src/phaser/core/stage.ts +4 -4
  117. package/src/phaser/core/timer.ts +13 -30
  118. package/src/phaser/core/timer_event.ts +3 -2
  119. package/src/phaser/core/tween.ts +9 -8
  120. package/src/phaser/core/tween_data.ts +15 -41
  121. package/src/phaser/core/tween_easing.test.ts +35 -35
  122. package/src/phaser/core/tween_easing.ts +33 -33
  123. package/src/phaser/core/tween_manager.ts +77 -75
  124. package/src/phaser/display/bitmap_text.ts +7 -11
  125. package/src/phaser/display/button.ts +3 -2
  126. package/src/phaser/display/canvas/graphics.ts +7 -7
  127. package/src/phaser/display/canvas/pool.ts +5 -6
  128. package/src/phaser/display/canvas/renderer.ts +7 -4
  129. package/src/phaser/display/canvas/tinter.ts +9 -11
  130. package/src/phaser/display/canvas/util.test.ts +122 -0
  131. package/src/phaser/display/canvas/util.ts +34 -35
  132. package/src/phaser/display/display_object.ts +30 -43
  133. package/src/phaser/display/graphics.ts +30 -45
  134. package/src/phaser/display/group.ts +1 -1
  135. package/src/phaser/display/image.ts +9 -16
  136. package/src/phaser/display/sprite_util.ts +0 -1
  137. package/src/phaser/display/text.ts +27 -43
  138. package/src/phaser/display/webgl/base_texture.ts +3 -3
  139. package/src/phaser/display/webgl/earcut.ts +2 -3
  140. package/src/phaser/display/webgl/fast_sprite_batch.ts +1 -2
  141. package/src/phaser/display/webgl/filter_manager.ts +6 -18
  142. package/src/phaser/display/webgl/filter_texture.ts +3 -3
  143. package/src/phaser/display/webgl/graphics.ts +28 -38
  144. package/src/phaser/display/webgl/graphics_data.ts +0 -1
  145. package/src/phaser/display/webgl/render_texture.ts +4 -13
  146. package/src/phaser/display/webgl/renderer.ts +0 -1
  147. package/src/phaser/display/webgl/sprite_batch.ts +2 -9
  148. package/src/phaser/display/webgl/texture.ts +12 -15
  149. package/src/phaser/geom/point.ts +1 -1
  150. package/src/phaser/geom/polygon.test.ts +59 -0
  151. package/src/phaser/geom/polygon.ts +43 -45
  152. package/src/phaser/geom/rectangle.ts +3 -15
  153. package/src/phaser/geom/rounded_rectangle.ts +2 -4
  154. package/src/phaser/geom/util/point.ts +1 -1
  155. package/src/phaser/geom/util/rectangle.ts +2 -2
  156. package/src/phaser/util/math.ts +1 -5
@@ -11,12 +11,13 @@ import {
11
11
  POINTER_CURSOR,
12
12
  TOUCH_OVERRIDES_MOUSE,
13
13
  } from './const.js';
14
+ import type { AppliedCallback, Callback } from './callback.js';
14
15
 
15
16
  /** A callback deferred until the click that triggered it has finished dispatching. */
16
17
  export type ClickTrampoline = {
17
18
  name: string;
18
19
  targetObject: InputHandler | null;
19
- callback: Function;
20
+ callback: Callback;
20
21
  callbackContext: unknown;
21
22
  callbackArgs: unknown[];
22
23
  };
@@ -281,7 +282,7 @@ export class Pointer {
281
282
  while (i) {
282
283
  i -= 1;
283
284
  const moveCallback = input.moveCallbacks[i]!;
284
- moveCallback.callback.call(moveCallback.context, this, this.x, this.y, fromClick);
285
+ (moveCallback.callback as AppliedCallback).call(moveCallback.context, this, this.x, this.y, fromClick);
285
286
  }
286
287
  // Easy out if we're dragging something and it still exists
287
288
  if (this.targetObject !== null && this.targetObject.isDragged) {
@@ -331,26 +332,26 @@ export class Pointer {
331
332
  // (A node that was previously checked did not request a pixel-perfect check.)
332
333
  currentNode = this.game.input.interactiveItems.first;
333
334
  while (currentNode) {
334
- if (!currentNode.checked && currentNode.validForInput(highestInputPriorityID, highestRenderOrderID, true)) {
335
- if (
336
- (fromClick && currentNode.checkPointerDown(this, false)) ||
337
- (!fromClick && currentNode.checkPointerOver(this, false))
338
- ) {
339
- highestRenderOrderID = currentNode.sprite.renderOrderID;
340
- highestInputPriorityID = currentNode.priorityID;
341
- candidateTarget = currentNode;
342
- this.interactiveCandidates.push(currentNode);
343
- }
335
+ if (
336
+ !currentNode.checked &&
337
+ currentNode.validForInput(highestInputPriorityID, highestRenderOrderID, true) &&
338
+ ((fromClick && currentNode.checkPointerDown(this, false)) ||
339
+ (!fromClick && currentNode.checkPointerOver(this, false)))
340
+ ) {
341
+ highestRenderOrderID = currentNode.sprite.renderOrderID;
342
+ highestInputPriorityID = currentNode.priorityID;
343
+ candidateTarget = currentNode;
344
+ this.interactiveCandidates.push(currentNode);
344
345
  }
345
346
  currentNode = this.game.input.interactiveItems.next;
346
347
  }
347
348
  if (this.game.input.customCandidateHandler) {
348
- candidateTarget = this.game.input.customCandidateHandler.call(
349
+ candidateTarget = (this.game.input.customCandidateHandler as AppliedCallback).call(
349
350
  this.game.input.customCandidateHandlerContext,
350
351
  this,
351
352
  this.interactiveCandidates,
352
353
  candidateTarget
353
- );
354
+ ) as InputHandler | null;
354
355
  }
355
356
  this.swapTarget(candidateTarget, false);
356
357
  return this.targetObject !== null;
@@ -456,8 +457,8 @@ export class Pointer {
456
457
  * @returns {boolean} TBD.
457
458
  */
458
459
  public justPressed(duration: number): boolean {
459
- duration = duration || this.game.input.justPressedRate;
460
- return this.isDown && this.timeDown + duration > this.game.time.time;
460
+ const window = duration || this.game.input.justPressedRate;
461
+ return this.isDown && this.timeDown + window > this.game.time.time;
461
462
  }
462
463
 
463
464
  /**
@@ -466,8 +467,8 @@ export class Pointer {
466
467
  * @returns {boolean} TBD.
467
468
  */
468
469
  public justReleased(duration: number): boolean {
469
- duration = duration || this.game.input.justReleasedRate;
470
- return this.isUp && this.timeUp + duration > this.game.time.time;
470
+ const window = duration || this.game.input.justReleasedRate;
471
+ return this.isUp && this.timeUp + window > this.game.time.time;
471
472
  }
472
473
 
473
474
  /**
@@ -477,11 +478,11 @@ export class Pointer {
477
478
  * @param {object} callbackContext - TBD.
478
479
  * @param {...any} callbackArgs - TBD.
479
480
  */
480
- public addClickTrampoline(name: string, callback: Function, callbackContext: unknown, callbackArgs: unknown[]): void {
481
+ public addClickTrampoline(name: string, callback: Callback, callbackContext: unknown, callbackArgs: unknown[]): void {
481
482
  if (!this.isDown) {
482
483
  return;
483
484
  }
484
- this._clickTrampolines = this._clickTrampolines ?? [];
485
+ this._clickTrampolines ??= [];
485
486
  const trampolines = this._clickTrampolines;
486
487
  for (let i = 0; i < trampolines.length; i += 1) {
487
488
  if (trampolines[i]!.name === name) {
@@ -508,7 +509,7 @@ export class Pointer {
508
509
  }
509
510
  for (const trampoline of trampolines) {
510
511
  if (trampoline.targetObject === this._trampolineTargetObject) {
511
- trampoline.callback.apply(trampoline.callbackContext, trampoline.callbackArgs);
512
+ (trampoline.callback as AppliedCallback).apply(trampoline.callbackContext, trampoline.callbackArgs);
512
513
  }
513
514
  }
514
515
  this._clickTrampolines = null;
@@ -51,24 +51,23 @@ export class Touch {
51
51
  if (!this.game.device.touch || this._onTouchStart !== null) {
52
52
  return;
53
53
  }
54
- const scope = this;
55
54
  this._onTouchStart = (event): void => {
56
- scope.onTouchStart(event);
55
+ this.onTouchStart(event);
57
56
  };
58
57
  this._onTouchMove = (event): void => {
59
- scope.onTouchMove(event);
58
+ this.onTouchMove(event);
60
59
  };
61
60
  this._onTouchEnd = (event): void => {
62
- scope.onTouchEnd(event);
61
+ this.onTouchEnd(event);
63
62
  };
64
63
  this._onTouchEnter = (event): void => {
65
- scope.onTouchEnter(event);
64
+ this.onTouchEnter(event);
66
65
  };
67
66
  this._onTouchLeave = (event): void => {
68
- scope.onTouchLeave(event);
67
+ this.onTouchLeave(event);
69
68
  };
70
69
  this._onTouchCancel = (event): void => {
71
- scope.onTouchCancel(event);
70
+ this.onTouchCancel(event);
72
71
  };
73
72
  this.game.canvas.addEventListener('touchstart', this._onTouchStart, false);
74
73
  this.game.canvas.addEventListener('touchmove', this._onTouchMove, false);
@@ -132,8 +131,8 @@ export class Touch {
132
131
  // event.touches = list of all touches on the ENTIRE DOCUMENT, not just the target element
133
132
  // event.changedTouches = the touches that CHANGED in this event, not the total number of them
134
133
  const changed = event.changedTouches ?? [];
135
- for (let i = 0; i < changed.length; i += 1) {
136
- this.game.input.startPointer(changed[i]!);
134
+ for (const touch of changed) {
135
+ this.game.input.startPointer(touch);
137
136
  }
138
137
  }
139
138
 
@@ -153,8 +152,8 @@ export class Touch {
153
152
  // Touch cancel - touches that were disrupted (perhaps by moving into a plugin or browser chrome)
154
153
  // http://www.w3.org/TR/touch-events/#dfn-touchcancel
155
154
  const changed = event.changedTouches ?? [];
156
- for (let i = 0; i < changed.length; i += 1) {
157
- this.game.input.stopPointer(changed[i]!);
155
+ for (const touch of changed) {
156
+ this.game.input.stopPointer(touch);
158
157
  }
159
158
  }
160
159
 
@@ -196,8 +195,8 @@ export class Touch {
196
195
  }
197
196
  this.eventPreventDefault(event);
198
197
  const changed = event.changedTouches ?? [];
199
- for (let i = 0; i < changed.length; i += 1) {
200
- this.game.input.updatePointer(changed[i]!);
198
+ for (const touch of changed) {
199
+ this.game.input.updatePointer(touch);
201
200
  }
202
201
  }
203
202
 
@@ -215,8 +214,8 @@ export class Touch {
215
214
  // https://developer.mozilla.org/en-US/docs/DOM/TouchList
216
215
  // event.changedTouches = the touches that CHANGED in this event, not the total number of them
217
216
  const changed = event.changedTouches ?? [];
218
- for (let i = 0; i < changed.length; i += 1) {
219
- this.game.input.stopPointer(changed[i]!);
217
+ for (const touch of changed) {
218
+ this.game.input.stopPointer(touch);
220
219
  }
221
220
  }
222
221
 
@@ -225,10 +224,8 @@ export class Touch {
225
224
  * @param {TouchEvent} event - TBD.
226
225
  */
227
226
  public eventPreventDefault(event: InputEvent): void {
228
- if (this.preventDefault) {
229
- if (typeof event.cancelable !== 'boolean' || event.cancelable) {
230
- event.preventDefault();
231
- }
227
+ if (this.preventDefault && (typeof event.cancelable !== 'boolean' || event.cancelable)) {
228
+ event.preventDefault();
232
229
  }
233
230
  }
234
231
  }
@@ -5,6 +5,7 @@ import type { Cache } from './cache.js';
5
5
  import type { Game } from './game.js';
6
6
  import type { Image } from '../display/image.js';
7
7
  import { Signal } from './signal.js';
8
+ import type { AppliedCallback, Callback } from './callback.js';
8
9
 
9
10
  const TEXTURE_ATLAS_JSON_HASH = 1;
10
11
 
@@ -163,13 +164,8 @@ export class Loader {
163
164
  height: sprite.height,
164
165
  rect: null,
165
166
  };
166
- if (direction === 0) {
167
- // Horizontal rect
168
- this.preloadSprite.rect = new Rectangle(0, 0, 1, sprite.height);
169
- } else {
170
- // Vertical rect
171
- this.preloadSprite.rect = new Rectangle(0, 0, sprite.width, 1);
172
- }
167
+ this.preloadSprite.rect =
168
+ direction === 0 ? new Rectangle(0, 0, 1, sprite.height) : new Rectangle(0, 0, sprite.width, 1);
173
169
  sprite.crop(this.preloadSprite.rect);
174
170
  sprite.visible = true;
175
171
  }
@@ -585,10 +581,10 @@ export class Loader {
585
581
  * @param {object} callbackContext - The context in which to call the callback.
586
582
  * @returns {Loader} This Loader instance for chaining.
587
583
  */
588
- public withSyncPoint(callback: Function, callbackContext: unknown): this {
584
+ public withSyncPoint(callback: Callback, callbackContext: unknown): this {
589
585
  this._withSyncPointDepth += 1;
590
586
  try {
591
- callback.call(callbackContext ?? this, this);
587
+ (callback as AppliedCallback).call(callbackContext ?? this, this);
592
588
  } finally {
593
589
  this._withSyncPointDepth -= 1;
594
590
  }
@@ -616,10 +612,8 @@ export class Loader {
616
612
  */
617
613
  public removeFile(type: string, key: string): void {
618
614
  const asset = this.getAsset(type, key);
619
- if (asset) {
620
- if (!asset.file.loaded && !asset.file.loading) {
621
- this._fileList.splice(asset.index, 1);
622
- }
615
+ if (asset && !asset.file.loaded && !asset.file.loading) {
616
+ this._fileList.splice(asset.index, 1);
623
617
  }
624
618
  }
625
619
 
@@ -735,9 +729,8 @@ export class Loader {
735
729
  // Flight queue is empty but file list is not done being processed.
736
730
  // This indicates a critical internal error with no known recovery.
737
731
  this.game.logger.warn('Aborting: processing queue empty, loading may have stalled');
738
- const scope = this;
739
732
  setTimeout((): void => {
740
- scope.finishedLoading(true);
733
+ this.finishedLoading(true);
741
734
  }, 2000);
742
735
  }
743
736
  }
@@ -929,32 +922,34 @@ export class Loader {
929
922
  */
930
923
  public loadImageTag(file: LoaderFile): void {
931
924
  this.log('loadImageTag', file);
932
- const scope = this;
933
925
  const image = new globalThis.Image();
934
926
  file.data = image;
935
- image.name = file.key;
927
+ image.dataset['key'] = file.key;
936
928
  if (typeof this.crossOrigin === 'string' && this.crossOrigin) {
937
929
  image.crossOrigin = this.crossOrigin;
938
930
  }
939
- image.onload = (): void => {
940
- if (image.onload) {
941
- image.onload = null;
942
- image.onerror = null;
943
- scope.fileComplete(file);
944
- }
945
- };
946
- image.onerror = (): void => {
947
- if (scope.isUseRetry && (!file.numRetry || file.numRetry < scope.maxRetry)) {
931
+ let settled = false;
932
+ image.addEventListener(
933
+ 'load',
934
+ (): void => {
935
+ if (!settled) {
936
+ settled = true;
937
+ this.fileComplete(file);
938
+ }
939
+ },
940
+ { once: true }
941
+ );
942
+ image.addEventListener('error', (): void => {
943
+ if (this.isUseRetry && (!file.numRetry || file.numRetry < this.maxRetry)) {
948
944
  setTimeout((): void => {
949
- file.numRetry = !file.numRetry ? 1 : (file.numRetry += 1);
950
- scope.loadImageTag(file);
945
+ file.numRetry = file.numRetry ? file.numRetry + 1 : 1;
946
+ this.loadImageTag(file);
951
947
  }, 1000);
952
- } else if (image.onload) {
953
- image.onload = null;
954
- image.onerror = null;
955
- scope.fileError(file);
948
+ } else if (!settled) {
949
+ settled = true;
950
+ this.fileError(file);
956
951
  }
957
- };
952
+ });
958
953
  const src = this.transformUrl(file.url, file);
959
954
  if (src === false) {
960
955
  this.fileError(file, null, 'Could not resolve the file URL');
@@ -975,15 +970,14 @@ export class Loader {
975
970
  file: LoaderFile,
976
971
  url: string | false,
977
972
  type: XMLHttpRequestResponseType,
978
- onload: Function,
979
- onerror: Function | null = null
973
+ onload: Callback,
974
+ onerror: Callback | null = null
980
975
  ): void {
981
976
  if (url === false) {
982
977
  this.fileError(file, null, 'Could not resolve the file URL');
983
978
  return;
984
979
  }
985
980
  this.log('xhrLoad', file);
986
- const scope = this;
987
981
  const xhr = new XMLHttpRequest();
988
982
  xhr.open('GET', url, true);
989
983
  xhr.responseType = type;
@@ -995,53 +989,51 @@ export class Loader {
995
989
  if (acceptHeader) {
996
990
  xhr.setRequestHeader('Accept', acceptHeader);
997
991
  }
998
- onerror = onerror ?? this.fileError;
999
- xhr.onload = () => {
992
+ const handleError = onerror ?? this.fileError;
993
+ const retry = (): boolean => {
994
+ if (!this.isUseRetry || (file.numRetry && file.numRetry >= this.maxRetry)) {
995
+ return false;
996
+ }
997
+ setTimeout((): void => {
998
+ file.numRetry = file.numRetry ? file.numRetry + 1 : 1;
999
+ this.xhrLoad(file, url, type, onload, handleError);
1000
+ }, 1000);
1001
+ return true;
1002
+ };
1003
+ const reportException = (error: unknown): void => {
1004
+ const typedError = error instanceof Error ? error : new Error(String(error));
1005
+ // If this was the last file in the queue and an error is thrown in the create method
1006
+ // then it's caught here, so be sure we don't carry on processing it
1007
+ if (this.hasLoaded) {
1008
+ this.game.logger.fatal('Loader', { error: typedError });
1009
+ } else {
1010
+ this.asyncComplete(file, typedError.message || 'Exception');
1011
+ }
1012
+ };
1013
+ xhr.addEventListener('load', (): void => {
1000
1014
  try {
1015
+ // Handle HTTP status codes of 4xx and 5xx as errors, even if xhr.onerror was not called.
1001
1016
  if (xhr.readyState === 4 && xhr.status >= 400 && xhr.status <= 599) {
1002
- // Handle HTTP status codes of 4xx and 5xx as errors, even if xhr.onerror was not called.
1003
- if (scope.isUseRetry && (!file.numRetry || file.numRetry < scope.maxRetry)) {
1004
- setTimeout((): void => {
1005
- file.numRetry = !file.numRetry ? 1 : (file.numRetry += 1);
1006
- scope.xhrLoad(file, url, type, onload, onerror);
1007
- }, 1000);
1008
- return null;
1017
+ if (!retry()) {
1018
+ (handleError as AppliedCallback).call(this, file, xhr);
1009
1019
  }
1010
- return onerror.call(scope, file, xhr);
1020
+ return;
1011
1021
  }
1012
- return onload.call(scope, file, xhr);
1022
+ (onload as AppliedCallback).call(this, file, xhr);
1013
1023
  } catch (error) {
1014
- const typedError = error instanceof Error ? error : new Error(String(error));
1015
- // If this was the last file in the queue and an error is thrown in the create method
1016
- // then it's caught here, so be sure we don't carry on processing it
1017
- if (!scope.hasLoaded) {
1018
- scope.asyncComplete(file, typedError.message || 'Exception');
1019
- } else {
1020
- scope.game.logger.fatal('Loader', { error: typedError });
1021
- }
1024
+ reportException(error);
1022
1025
  }
1023
- return null;
1024
- };
1025
- xhr.onerror = () => {
1026
- if (scope.isUseRetry && (!file.numRetry || file.numRetry < scope.maxRetry)) {
1027
- setTimeout((): void => {
1028
- file.numRetry = !file.numRetry ? 1 : (file.numRetry += 1);
1029
- scope.xhrLoad(file, url, type, onload, onerror);
1030
- }, 1000);
1031
- } else {
1032
- try {
1033
- return onerror.call(scope, file, xhr);
1034
- } catch (error) {
1035
- const typedError = error instanceof Error ? error : new Error(String(error));
1036
- if (!scope.hasLoaded) {
1037
- scope.asyncComplete(file, typedError.message || 'Exception');
1038
- } else {
1039
- scope.game.logger.fatal('Loader', { error: typedError });
1040
- }
1041
- }
1026
+ });
1027
+ xhr.addEventListener('error', (): void => {
1028
+ if (retry()) {
1029
+ return;
1042
1030
  }
1043
- return null;
1044
- };
1031
+ try {
1032
+ (handleError as AppliedCallback).call(this, file, xhr);
1033
+ } catch (error) {
1034
+ reportException(error);
1035
+ }
1036
+ });
1045
1037
  file.requestObject = xhr;
1046
1038
  file.requestUrl = url;
1047
1039
  xhr.send();
@@ -1051,7 +1043,6 @@ export class Loader {
1051
1043
  * Placeholder for XDomainRequest loading (not implemented).
1052
1044
  */
1053
1045
  public xhrLoadWithXDR(): void {
1054
- // TODO
1055
1046
  this.game.logger.warn('loader.xhrLoadWithXDR() is not implemented');
1056
1047
  }
1057
1048
 
@@ -1065,8 +1056,7 @@ export class Loader {
1065
1056
  return null;
1066
1057
  }
1067
1058
  const candidates = typeof urls === 'string' ? [urls] : urls;
1068
- for (let i = 0; i < candidates.length; i += 1) {
1069
- const candidate = candidates[i]!;
1059
+ for (const candidate of candidates) {
1070
1060
  let url: string;
1071
1061
  let audioType = null;
1072
1062
  if (typeof candidate !== 'string') {
@@ -1107,7 +1097,7 @@ export class Loader {
1107
1097
  if (!reason && xhr) {
1108
1098
  reason = xhr.status;
1109
1099
  }
1110
- const message = `Error loading asset (${reason})`;
1100
+ const message = `Error loading asset (${String(reason)})`;
1111
1101
  this.asyncComplete(file, message);
1112
1102
  }
1113
1103
 
@@ -1153,7 +1143,7 @@ export class Loader {
1153
1143
  if (file.format === TEXTURE_ATLAS_JSON_HASH) {
1154
1144
  this.xhrLoad(file, this.transformUrl(file.atlasURL, file), 'text', this.jsonLoadComplete);
1155
1145
  } else {
1156
- throw new Error(`Invalid Texture Atlas format: ${file.format}`);
1146
+ throw new Error(`Invalid Texture Atlas format: ${String(file.format)}`);
1157
1147
  }
1158
1148
  }
1159
1149
  break;
@@ -1201,7 +1191,7 @@ export class Loader {
1201
1191
  file.data = audio;
1202
1192
  this.cache.addSound(file.key, url, audio);
1203
1193
  if (file.autoDecode) {
1204
- this.game.sound.decode(file.key);
1194
+ void this.game.sound.decode(file.key);
1205
1195
  }
1206
1196
  break;
1207
1197
  }
@@ -1251,7 +1241,6 @@ export class Loader {
1251
1241
  * TBD.
1252
1242
  */
1253
1243
  public csvLoadComplete(): void {
1254
- // TODO
1255
1244
  this.game.logger.warn('loader.csvLoadComplete() is not implemented');
1256
1245
  }
1257
1246
 
@@ -1337,13 +1326,13 @@ export class Loader {
1337
1326
  /**
1338
1327
  * Logs a message to the console if logging is enabled.
1339
1328
  * @param {string} message - The message to log.
1340
- * @param {string|object} data - Additional data to log with the message.
1329
+ * @param {object} file - The file the message is about.
1341
1330
  */
1342
- public log(message: string, data: string | any = ''): void {
1331
+ public log(message: string, file: LoaderFile): void {
1343
1332
  if (!this.isUseLog) {
1344
1333
  return;
1345
1334
  }
1346
- this.game.logger.info(message, data);
1335
+ this.game.logger.info(message, { key: file.key, type: file.type });
1347
1336
  }
1348
1337
 
1349
1338
  /**
@@ -50,8 +50,8 @@ export const xmlBitmapFont = (
50
50
  xSpacing: number,
51
51
  ySpacing: number
52
52
  ): BitmapFontData => {
53
- const info = xml.querySelectorAll('info')[0];
54
- const common = xml.querySelectorAll('common')[0];
53
+ const [info] = xml.querySelectorAll('info');
54
+ const [common] = xml.querySelectorAll('common');
55
55
  const attr = (element: Element | undefined, name: string): number => Math.trunc(Number(element?.getAttribute(name)));
56
56
  const data: BitmapFontData = {
57
57
  font: info?.getAttribute('face') ?? '',
@@ -115,7 +115,7 @@ export const jsonBitmapFont = (
115
115
  lineHeight: Math.trunc(Number(json.font.common._lineHeight)) + ySpacing,
116
116
  chars: {},
117
117
  };
118
- json.font.chars.char.forEach((letter: Record<string, string>): void => {
118
+ for (const letter of json.font.chars.char as Record<string, string>[]) {
119
119
  const charCode = Math.trunc(Number(letter['_id']));
120
120
  data.chars[charCode] = {
121
121
  x: Math.trunc(Number(letter['_x'])),
@@ -127,14 +127,14 @@ export const jsonBitmapFont = (
127
127
  xAdvance: Math.trunc(Number(letter['_xadvance'])) + xSpacing,
128
128
  kerning: {},
129
129
  };
130
- });
130
+ }
131
131
  if (json.font.kernings && json.font.kernings.kerning) {
132
- json.font.kernings.kerning.forEach((kerning: Record<string, string>): void => {
132
+ for (const kerning of json.font.kernings.kerning as Record<string, string>[]) {
133
133
  const char = data.chars[Math.trunc(Number(kerning['_second']))];
134
134
  if (char) {
135
135
  char.kerning[Math.trunc(Number(kerning['_first']))] = Math.trunc(Number(kerning['_amount']));
136
136
  }
137
- });
137
+ }
138
138
  }
139
139
  return finalizeBitmapFont(baseTexture, data);
140
140
  };