@m2c2kit/addons 0.3.13 → 0.3.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
- import { Composite, WebColors, Shape, Label, CanvasKitHelpers, EventType, MutablePath, Easings, Story, Transition, TransitionDirection, LabelHorizontalAlignmentMode, Scene, Dimensions, Sprite } from '@m2c2kit/core';
1
+ import { Composite, WebColors, Shape, Label, CanvasKitHelpers, M2EventType, MutablePath, Easings, Story, Transition, TransitionDirection, LabelHorizontalAlignmentMode, Scene, Dimensions, Sprite, Action } from '@m2c2kit/core';
2
2
 
3
3
  class Grid extends Composite {
4
4
  /**
5
- * A rectangular grid that supports placement of entities within the grid's
5
+ * A rectangular grid that supports placement of nodes within the grid's
6
6
  * cells.
7
7
  *
8
- * @remarks This composite entity is composed of rectangles and lines. It
9
- * has convenience functions for placing and clearing entities on the grid
8
+ * @remarks This composite node is composed of rectangles and lines. It
9
+ * has convenience functions for placing and clearing nodes on the grid
10
10
  * by row and column position (zero-based indexing)
11
11
  *
12
12
  * @param options - {@link GridOptions}
@@ -102,11 +102,11 @@ class Grid extends Composite {
102
102
  }
103
103
  const x = -this.size.width / 2 + this.cellWidth / 2 + gridChild.column * this.cellWidth;
104
104
  const y = -this.size.height / 2 + this.cellHeight / 2 + gridChild.row * this.cellHeight;
105
- gridChild.entity.position = {
106
- x: x + gridChild.entity.position.x,
107
- y: y + gridChild.entity.position.y
105
+ gridChild.node.position = {
106
+ x: x + gridChild.node.position.x,
107
+ y: y + gridChild.node.position.y
108
108
  };
109
- this.gridBackground.addChild(gridChild.entity);
109
+ this.gridBackground.addChild(gridChild.node);
110
110
  });
111
111
  }
112
112
  this.needsInitialization = false;
@@ -120,24 +120,24 @@ class Grid extends Composite {
120
120
  set gridBackground(gridBackground) {
121
121
  this._gridBackground = gridBackground;
122
122
  }
123
- // all entities that make up grid are added as children, so they
123
+ // all nodes that make up grid are added as children, so they
124
124
  // have their own dispose methods
125
125
  // eslint-disable-next-line @typescript-eslint/no-empty-function
126
126
  dispose() {
127
127
  }
128
128
  /**
129
- * Duplicates an entity using deep copy.
129
+ * Duplicates a node using deep copy.
130
130
  *
131
- * @remarks This is a deep recursive clone (entity and children).
132
- * The uuid property of all duplicated entities will be newly created,
131
+ * @remarks This is a deep recursive clone (node and children).
132
+ * The uuid property of all duplicated nodes will be newly created,
133
133
  * because uuid must be unique.
134
134
  *
135
- * @param newName - optional name of the new, duplicated entity. If not
135
+ * @param newName - optional name of the new, duplicated node. If not
136
136
  * provided, name will be the new uuid
137
137
  */
138
138
  duplicate(newName) {
139
139
  const dest = new Grid({
140
- ...this.getEntityOptions(),
140
+ ...this.getNodeOptions(),
141
141
  ...this.getDrawableOptions(),
142
142
  rows: this.rows,
143
143
  columns: this.columns,
@@ -168,8 +168,8 @@ class Grid extends Composite {
168
168
  child.warmup(canvas);
169
169
  });
170
170
  }
171
- // override Entity.RemoveAllChildren() so that when RemoveAllChildren() is called on a Grid,
172
- // it removes only entities added to the grid cells (what we call grid children), not the grid lines!
171
+ // override M2Node.RemoveAllChildren() so that when RemoveAllChildren() is called on a Grid,
172
+ // it removes only nodes added to the grid cells (what we call grid children), not the grid lines!
173
173
  /**
174
174
  * Removes all children from the grid, but retains grid lines.
175
175
  */
@@ -180,29 +180,29 @@ class Grid extends Composite {
180
180
  while (this.gridChildren.length) {
181
181
  const gridChild = this.gridChildren.pop();
182
182
  if (gridChild) {
183
- this.gridBackground.removeChild(gridChild.entity);
183
+ this.gridBackground.removeChild(gridChild.node);
184
184
  }
185
185
  }
186
186
  this.needsInitialization = true;
187
187
  }
188
188
  /**
189
- * Adds an entity to the grid at the specified row and column position.
189
+ * Adds a node to the grid at the specified row and column position.
190
190
  *
191
- * @param entity - entity to add to the grid
192
- * @param row - row position within grid to add entity; zero-based indexing
193
- * @param column - column position within grid to add entity; zero-based indexing
191
+ * @param node - node to add to the grid
192
+ * @param row - row position within grid to add node; zero-based indexing
193
+ * @param column - column position within grid to add node; zero-based indexing
194
194
  */
195
- addAtCell(entity, row, column) {
195
+ addAtCell(node, row, column) {
196
196
  if (row < 0 || row >= this.rows || column < 0 || column >= this.columns) {
197
197
  console.warn(
198
- `warning: addAtCell() requested to add entity at row ${row}, column ${column}. This is outside the bounds of grid ${this.name}, which is size ${this.rows}x${this.columns}. Note that addAtCell() uses zero-based indexing. AddAtCell() will proceed, but may draw entities outside the grid`
198
+ `warning: addAtCell() requested to add node at row ${row}, column ${column}. This is outside the bounds of grid ${this.name}, which is size ${this.rows}x${this.columns}. Note that addAtCell() uses zero-based indexing. AddAtCell() will proceed, but may draw nodes outside the grid`
199
199
  );
200
200
  }
201
- this.gridChildren.push({ entity, row, column });
201
+ this.gridChildren.push({ node, row, column });
202
202
  this.needsInitialization = true;
203
203
  }
204
204
  /**
205
- * Removes all child entities at the specified row and column position.
205
+ * Removes all child nodes at the specified row and column position.
206
206
  *
207
207
  * @param row - row position within grid at which to remove children; zero-based indexing
208
208
  * @param column - column position within grid at which to remove children; zero-based indexing
@@ -215,24 +215,24 @@ class Grid extends Composite {
215
215
  return;
216
216
  }
217
217
  this.gridBackground.removeChildren(
218
- gridChildrenToRemove.map((gridChild) => gridChild.entity)
218
+ gridChildrenToRemove.map((gridChild) => gridChild.node)
219
219
  );
220
220
  this.gridChildren = this.gridChildren.filter(
221
221
  (gridChild) => gridChild.row !== row && gridChild.column !== column
222
222
  );
223
223
  this.needsInitialization = true;
224
224
  }
225
- // override Entity.RemoveChild() so that when RemoveChild() is called on a Grid, it removes the
226
- // entity from the gridBackground rectangle AND our grid's own list of children (in gridChildren)
225
+ // override M2Node.RemoveChild() so that when RemoveChild() is called on a Grid, it removes the
226
+ // node from the gridBackground rectangle AND our grid's own list of children (in gridChildren)
227
227
  /**
228
- * Removes the child entity from the grid.
228
+ * Removes the child node from the grid.
229
229
  *
230
- * @param entity - entity to remove
230
+ * @param node - node to remove
231
231
  */
232
- removeChild(entity) {
233
- this.gridBackground.removeChild(entity);
232
+ removeChild(node) {
233
+ this.gridBackground.removeChild(node);
234
234
  this.gridChildren = this.gridChildren.filter(
235
- (gridChild) => gridChild.entity != entity
235
+ (gridChild) => gridChild.node != node
236
236
  );
237
237
  this.needsInitialization = true;
238
238
  }
@@ -244,7 +244,7 @@ class Button extends Composite {
244
244
  /**
245
245
  * A simple button of rectangle with text centered inside.
246
246
  *
247
- * @remarks This composite entity is composed of a rectangle and text. To
247
+ * @remarks This composite node is composed of a rectangle and text. To
248
248
  * respond to user taps, the isUserInteractionEnabled property must be set
249
249
  * to true and an appropriate callback must be set to handle the tap event.
250
250
  *
@@ -260,6 +260,8 @@ class Button extends Composite {
260
260
  this.fontSize = 20;
261
261
  this._text = "";
262
262
  this._fontColor = WebColors.White;
263
+ this._interpolation = {};
264
+ this._localize = true;
263
265
  if (options.text) {
264
266
  this.text = options.text;
265
267
  }
@@ -269,6 +271,12 @@ class Button extends Composite {
269
271
  if (options.cornerRadius !== void 0) {
270
272
  this.cornerRadius = options.cornerRadius;
271
273
  }
274
+ if (options.fontName) {
275
+ this.fontName = options.fontName;
276
+ }
277
+ if (options.fontNames) {
278
+ this.fontNames = options.fontNames;
279
+ }
272
280
  if (options.fontSize !== void 0) {
273
281
  this.fontSize = options.fontSize;
274
282
  }
@@ -278,6 +286,12 @@ class Button extends Composite {
278
286
  if (options.backgroundColor) {
279
287
  this.backgroundColor = options.backgroundColor;
280
288
  }
289
+ if (options.interpolation) {
290
+ this.interpolation = options.interpolation;
291
+ }
292
+ if (options.localize !== void 0) {
293
+ this.localize = options.localize;
294
+ }
281
295
  }
282
296
  initialize() {
283
297
  this.removeAllChildren();
@@ -299,6 +313,10 @@ class Button extends Composite {
299
313
  this.addChild(buttonRectangle);
300
314
  const buttonLabel = new Label({
301
315
  text: this.text,
316
+ localize: this.localize,
317
+ interpolation: this.interpolation,
318
+ fontName: this.fontName,
319
+ fontNames: this.fontNames,
302
320
  fontSize: this.fontSize,
303
321
  fontColor: this.fontColor
304
322
  });
@@ -329,26 +347,59 @@ class Button extends Composite {
329
347
  this._fontColor = fontColor;
330
348
  this.needsInitialization = true;
331
349
  }
350
+ get fontName() {
351
+ return this._fontName;
352
+ }
353
+ set fontName(fontName) {
354
+ this._fontName = fontName;
355
+ this.needsInitialization = true;
356
+ }
357
+ get fontNames() {
358
+ return this._fontNames;
359
+ }
360
+ set fontNames(fontNames) {
361
+ this._fontNames = fontNames;
362
+ this.needsInitialization = true;
363
+ }
364
+ get interpolation() {
365
+ return this._interpolation;
366
+ }
367
+ set interpolation(interpolation) {
368
+ this._interpolation = interpolation;
369
+ Object.freeze(this._interpolation);
370
+ this.needsInitialization = true;
371
+ }
372
+ get localize() {
373
+ return this._localize;
374
+ }
375
+ set localize(localize) {
376
+ this._localize = localize;
377
+ this.needsInitialization = true;
378
+ }
332
379
  /**
333
- * Duplicates an entity using deep copy.
380
+ * Duplicates a node using deep copy.
334
381
  *
335
- * @remarks This is a deep recursive clone (entity and children).
336
- * The uuid property of all duplicated entities will be newly created,
382
+ * @remarks This is a deep recursive clone (node and children).
383
+ * The uuid property of all duplicated nodes will be newly created,
337
384
  * because uuid must be unique.
338
385
  *
339
- * @param newName - optional name of the new, duplicated entity. If not
386
+ * @param newName - optional name of the new, duplicated node. If not
340
387
  * provided, name will be the new uuid
341
388
  */
342
389
  duplicate(newName) {
343
390
  const dest = new Button({
344
- ...this.getEntityOptions(),
391
+ ...this.getNodeOptions(),
345
392
  ...this.getDrawableOptions(),
346
393
  ...this.getTextOptions(),
347
394
  size: this.size,
348
395
  cornerRadius: this.cornerRadius,
349
396
  backgroundColor: this.backgroundColor,
350
397
  fontColor: this.fontColor,
351
- name: newName
398
+ name: newName,
399
+ localize: this.localize,
400
+ interpolation: JSON.parse(JSON.stringify(this.interpolation)),
401
+ fontName: this.fontName,
402
+ fontNames: JSON.parse(JSON.stringify(this.fontNames))
352
403
  });
353
404
  if (this.children.length > 0) {
354
405
  dest.children = this.children.map((child) => {
@@ -384,7 +435,7 @@ class Dialog extends Composite {
384
435
  // todo: add default "behaviors" (?) like button click animation?
385
436
  constructor(options) {
386
437
  super(options);
387
- this.compositeType = "dialog";
438
+ this.compositeType = "Dialog";
388
439
  this._backgroundColor = WebColors.White;
389
440
  this.cornerRadius = 9;
390
441
  this.overlayAlpha = 0.5;
@@ -425,15 +476,16 @@ class Dialog extends Composite {
425
476
  show() {
426
477
  this.hidden = false;
427
478
  }
428
- onDialogResult(callback, replaceExistingCallback = true) {
479
+ onDialogResult(callback, options) {
429
480
  const eventListener = {
430
- type: EventType.CompositeCustom,
431
- entityUuid: this.uuid,
481
+ type: M2EventType.CompositeCustom,
482
+ compositeType: "DialogResult",
483
+ nodeUuid: this.uuid,
432
484
  callback
433
485
  };
434
- if (replaceExistingCallback) {
486
+ if (options?.replaceExisting) {
435
487
  this.eventListeners = this.eventListeners.filter(
436
- (listener) => !(listener.entityUuid === eventListener.entityUuid && listener.type === eventListener.type)
488
+ (listener) => !(listener.nodeUuid === eventListener.nodeUuid && listener.type === eventListener.type)
437
489
  );
438
490
  }
439
491
  this.eventListeners.push(eventListener);
@@ -455,9 +507,9 @@ class Dialog extends Composite {
455
507
  e.handled = true;
456
508
  this.hidden = true;
457
509
  if (this.eventListeners.length > 0) {
458
- this.eventListeners.filter((listener) => listener.type === EventType.CompositeCustom).forEach((listener) => {
510
+ this.eventListeners.filter((listener) => listener.type === M2EventType.CompositeCustom).forEach((listener) => {
459
511
  const dialogEvent = {
460
- type: EventType.CompositeCustom,
512
+ type: M2EventType.CompositeCustom,
461
513
  target: this,
462
514
  handled: false,
463
515
  dialogResult: "Dismiss" /* Dismiss */
@@ -503,9 +555,9 @@ class Dialog extends Composite {
503
555
  e.handled = true;
504
556
  this.hidden = true;
505
557
  if (this.eventListeners.length > 0) {
506
- this.eventListeners.filter((listener) => listener.type === EventType.CompositeCustom).forEach((listener) => {
558
+ this.eventListeners.filter((listener) => listener.type === M2EventType.CompositeCustom).forEach((listener) => {
507
559
  const dialogEvent = {
508
- type: EventType.CompositeCustom,
560
+ type: M2EventType.CompositeCustom,
509
561
  target: this,
510
562
  handled: false,
511
563
  dialogResult: "Negative" /* Negative */
@@ -525,9 +577,9 @@ class Dialog extends Composite {
525
577
  e.handled = true;
526
578
  this.hidden = true;
527
579
  if (this.eventListeners.length > 0) {
528
- this.eventListeners.filter((listener) => listener.type === EventType.CompositeCustom).forEach((listener) => {
580
+ this.eventListeners.filter((listener) => listener.type === M2EventType.CompositeCustom).forEach((listener) => {
529
581
  const dialogEvent = {
530
- type: EventType.CompositeCustom,
582
+ type: M2EventType.CompositeCustom,
531
583
  target: this,
532
584
  handled: false,
533
585
  dialogResult: "Positive" /* Positive */
@@ -555,13 +607,13 @@ class Dialog extends Composite {
555
607
  this.needsInitialization = true;
556
608
  }
557
609
  /**
558
- * Duplicates an entity using deep copy.
610
+ * Duplicates a node using deep copy.
559
611
  *
560
- * @remarks This is a deep recursive clone (entity and children).
561
- * The uuid property of all duplicated entities will be newly created,
612
+ * @remarks This is a deep recursive clone (node and children).
613
+ * The uuid property of all duplicated nodes will be newly created,
562
614
  * because uuid must be unique.
563
615
  *
564
- * @param newName - optional name of the new, duplicated entity. If not
616
+ * @param newName - optional name of the new, duplicated node. If not
565
617
  * provided, name will be the new uuid
566
618
  */
567
619
  duplicate(newName) {
@@ -594,7 +646,7 @@ class DrawPad extends Composite {
594
646
  /**
595
647
  * A rectangular area on which the user can draw strokes (lines).
596
648
  *
597
- * @remarks This composite entity is composed of a rectangle Shape and
649
+ * @remarks This composite node is composed of a rectangle Shape and
598
650
  * another Shape that is formed from a path of points.
599
651
  *
600
652
  * @param options - {@link DrawPadOptions}
@@ -919,45 +971,45 @@ class DrawPad extends Composite {
919
971
  );
920
972
  }
921
973
  /**
922
- * Adds an entity to the DrawPad.
974
+ * Adds a node to the DrawPad.
923
975
  *
924
- * @remarks After the entity is added to the DrawPad, its
976
+ * @remarks After the node is added to the DrawPad, its
925
977
  * position is adjusted to be relative to the DrawPad's coordinate
926
978
  * system, and it is made interactive. The method returns an object
927
- * which is the entity as a DrawPadItem, which has additional methods,
928
- * properties, and events specific to it now being on a DrawPad. The entity
979
+ * which is the node as a DrawPadItem, which has additional methods,
980
+ * properties, and events specific to it now being on a DrawPad. The node
929
981
  * now **must** be manipulated only using the DrawPadItem object. Using
930
- * the original entity object will result in undefined behavior.
982
+ * the original node object will result in undefined behavior.
931
983
  *
932
- * @param entity - the entity to add to the DrawPad
933
- * @returns the entity as a DrawPadItem
984
+ * @param node - the node to add to the DrawPad
985
+ * @returns the node as a DrawPadItem
934
986
  */
935
- addItem(entity) {
936
- Object.defineProperty(entity, "drawPadPosition", {
987
+ addItem(node) {
988
+ Object.defineProperty(node, "drawPadPosition", {
937
989
  get: function() {
938
- const drawPad = entity.parent;
990
+ const drawPad = node.parent;
939
991
  return {
940
992
  get x() {
941
- return entity.position.x + drawPad.size.width / 2;
993
+ return node.position.x + drawPad.size.width / 2;
942
994
  },
943
995
  set x(value) {
944
- entity.position.x = value - drawPad.size.width / 2;
996
+ node.position.x = value - drawPad.size.width / 2;
945
997
  },
946
998
  get y() {
947
- return entity.position.y + drawPad.size.height / 2;
999
+ return node.position.y + drawPad.size.height / 2;
948
1000
  },
949
1001
  set y(value) {
950
- entity.position.y = value - drawPad.size.height / 2;
1002
+ node.position.y = value - drawPad.size.height / 2;
951
1003
  }
952
1004
  };
953
1005
  },
954
1006
  set: function(value) {
955
- const drawPad = entity.parent;
956
- entity.position.x = value.x - drawPad.size.width / 2;
957
- entity.position.y = value.y - drawPad.size.height / 2;
1007
+ const drawPad = node.parent;
1008
+ node.position.x = value.x - drawPad.size.width / 2;
1009
+ node.position.y = value.y - drawPad.size.height / 2;
958
1010
  }
959
1011
  });
960
- Object.defineProperty(entity, "onStrokeEnter", {
1012
+ Object.defineProperty(node, "onStrokeEnter", {
961
1013
  value: function(callback, options) {
962
1014
  this.addEventListener(
963
1015
  DrawPadItemEventType.StrokeEnter,
@@ -966,7 +1018,7 @@ class DrawPad extends Composite {
966
1018
  );
967
1019
  }
968
1020
  });
969
- Object.defineProperty(entity, "onStrokeLeave", {
1021
+ Object.defineProperty(node, "onStrokeLeave", {
970
1022
  value: function(callback, options) {
971
1023
  this.addEventListener(
972
1024
  DrawPadItemEventType.StrokeLeave,
@@ -975,62 +1027,62 @@ class DrawPad extends Composite {
975
1027
  );
976
1028
  }
977
1029
  });
978
- Object.defineProperty(entity, "isStrokeWithinBounds", {
1030
+ Object.defineProperty(node, "isStrokeWithinBounds", {
979
1031
  value: false,
980
1032
  writable: true
981
1033
  });
982
- entity.onPointerDown(() => {
1034
+ node.onPointerDown(() => {
983
1035
  if (this.isDrawingPointerDown) {
984
- if (entity.isStrokeWithinBounds === false) {
985
- entity.isStrokeWithinBounds = true;
1036
+ if (node.isStrokeWithinBounds === false) {
1037
+ node.isStrokeWithinBounds = true;
986
1038
  const drawPadItemEvent = {
987
1039
  type: DrawPadItemEventType.StrokeEnter,
988
- target: entity
1040
+ target: node
989
1041
  };
990
- this.raiseDrawPadItemEvent(entity, drawPadItemEvent);
1042
+ this.raiseDrawPadItemEvent(node, drawPadItemEvent);
991
1043
  }
992
1044
  }
993
1045
  });
994
- entity.onPointerMove(() => {
1046
+ node.onPointerMove(() => {
995
1047
  if (this.isDrawingPointerDown) {
996
- if (entity.isStrokeWithinBounds === false) {
997
- entity.isStrokeWithinBounds = true;
1048
+ if (node.isStrokeWithinBounds === false) {
1049
+ node.isStrokeWithinBounds = true;
998
1050
  const drawPadItemEvent = {
999
1051
  type: DrawPadItemEventType.StrokeEnter,
1000
- target: entity
1052
+ target: node
1001
1053
  };
1002
- this.raiseDrawPadItemEvent(entity, drawPadItemEvent);
1054
+ this.raiseDrawPadItemEvent(node, drawPadItemEvent);
1003
1055
  }
1004
1056
  }
1005
1057
  });
1006
- entity.onPointerLeave(() => {
1058
+ node.onPointerLeave(() => {
1007
1059
  if (this.isDrawingPointerDown) {
1008
- if (entity.isStrokeWithinBounds === true) {
1009
- entity.isStrokeWithinBounds = false;
1060
+ if (node.isStrokeWithinBounds === true) {
1061
+ node.isStrokeWithinBounds = false;
1010
1062
  const drawPadItemEvent = {
1011
1063
  type: DrawPadItemEventType.StrokeLeave,
1012
- target: entity
1064
+ target: node
1013
1065
  };
1014
- this.raiseDrawPadItemEvent(entity, drawPadItemEvent);
1066
+ this.raiseDrawPadItemEvent(node, drawPadItemEvent);
1015
1067
  }
1016
1068
  }
1017
1069
  });
1018
- entity.onPointerUp(() => {
1019
- if (entity.isStrokeWithinBounds === true) {
1020
- entity.isStrokeWithinBounds = false;
1070
+ node.onPointerUp(() => {
1071
+ if (node.isStrokeWithinBounds === true) {
1072
+ node.isStrokeWithinBounds = false;
1021
1073
  const drawPadItemEvent = {
1022
1074
  type: DrawPadItemEventType.StrokeLeave,
1023
- target: entity
1075
+ target: node
1024
1076
  };
1025
- this.raiseDrawPadItemEvent(entity, drawPadItemEvent);
1077
+ this.raiseDrawPadItemEvent(node, drawPadItemEvent);
1026
1078
  }
1027
1079
  });
1028
- this.addChild(entity);
1029
- entity.zPosition = -1;
1030
- entity.position.x = entity.position.x - this.size.width / 2;
1031
- entity.position.y = entity.position.y - this.size.height / 2;
1032
- entity.isUserInteractionEnabled = true;
1033
- return entity;
1080
+ this.addChild(node);
1081
+ node.zPosition = -1;
1082
+ node.position.x = node.position.x - this.size.width / 2;
1083
+ node.position.y = node.position.y - this.size.height / 2;
1084
+ node.isUserInteractionEnabled = true;
1085
+ return node;
1034
1086
  }
1035
1087
  /**
1036
1088
  * Takes a screenshot of the DrawPad.
@@ -1197,8 +1249,12 @@ class VirtualKeyboard extends Composite {
1197
1249
  */
1198
1250
  constructor(options) {
1199
1251
  super(options);
1252
+ this.compositeType = "VirtualKeyboard";
1200
1253
  this.rowsConfiguration = new Array();
1201
1254
  this.shiftActivated = false;
1255
+ this.keyShapes = new Array();
1256
+ // VirtualKeyboard defaults to being user interactive.
1257
+ this._isUserInteractionEnabled = true;
1202
1258
  this.size = options.size;
1203
1259
  this.position = options.position ?? { x: 0, y: 0 };
1204
1260
  this.keyboardHorizontalPaddingPercent = options.keyboardHorizontalPaddingPercent ?? 0.02;
@@ -1380,6 +1436,7 @@ class VirtualKeyboard extends Composite {
1380
1436
  const keyboardHorizontalPadding = (this.keyboardHorizontalPaddingPercent ?? 0.02) * this.size.width;
1381
1437
  const keyBoxHeight = (this.size.height - 2 * keyboardVerticalPadding) / rows.length;
1382
1438
  const keyBoxWidth = (this.size.width - 2 * keyboardHorizontalPadding) / this.keysPerRow;
1439
+ this.keyShapes = [];
1383
1440
  for (let r = 0; r < rows.length; r++) {
1384
1441
  const row = rows[r];
1385
1442
  const rowSumKeyWidths = row.reduce(
@@ -1418,9 +1475,10 @@ class VirtualKeyboard extends Composite {
1418
1475
  cornerRadius: 4,
1419
1476
  fillColor: this.keyColor,
1420
1477
  lineWidth: 0,
1421
- isUserInteractionEnabled: true
1478
+ isUserInteractionEnabled: this.isUserInteractionEnabled
1422
1479
  });
1423
1480
  keyBox.addChild(keyShape);
1481
+ this.keyShapes.push(keyShape);
1424
1482
  keyShape.onTapUp((tapEvent) => {
1425
1483
  let keyAsString = "";
1426
1484
  if (!key.isShift) {
@@ -1456,10 +1514,10 @@ class VirtualKeyboard extends Composite {
1456
1514
  letterCircle.hidden = true;
1457
1515
  if (this.eventListeners.length > 0) {
1458
1516
  this.eventListeners.filter(
1459
- (listener) => listener.type === EventType.CompositeCustom && listener.compositeType === "VirtualKeyboardKeyUp"
1517
+ (listener) => listener.type === M2EventType.CompositeCustom && listener.compositeType === "VirtualKeyboardKeyUp"
1460
1518
  ).forEach((listener) => {
1461
1519
  const virtualKeyboardEvent = {
1462
- type: EventType.CompositeCustom,
1520
+ type: M2EventType.CompositeCustom,
1463
1521
  target: this,
1464
1522
  handled: false,
1465
1523
  key: keyAsString,
@@ -1524,10 +1582,10 @@ class VirtualKeyboard extends Composite {
1524
1582
  }
1525
1583
  if (this.eventListeners.length > 0) {
1526
1584
  this.eventListeners.filter(
1527
- (listener) => listener.type === EventType.CompositeCustom && listener.compositeType === "VirtualKeyboardKeyDown"
1585
+ (listener) => listener.type === M2EventType.CompositeCustom && listener.compositeType === "VirtualKeyboardKeyDown"
1528
1586
  ).forEach((listener) => {
1529
1587
  const virtualKeyboardEvent = {
1530
- type: EventType.CompositeCustom,
1588
+ type: M2EventType.CompositeCustom,
1531
1589
  target: this,
1532
1590
  handled: false,
1533
1591
  key: keyAsString,
@@ -1581,54 +1639,55 @@ class VirtualKeyboard extends Composite {
1581
1639
  * Executes a callback when the user presses down on a key.
1582
1640
  *
1583
1641
  * @param callback - function to execute
1584
- * @param replaceExistingCallback - should the provided callback replace
1585
- * any existing callbacks of the same event type on this entity? Usually
1586
- * there should be only one callback defined, instead of chaining multiple
1587
- * ones. It is strongly recommended not to change this, unless you have a
1588
- * special use case. Default is true.
1642
+ * @param options
1589
1643
  */
1590
- onKeyDown(callback, replaceExistingCallback = true) {
1644
+ onKeyDown(callback, options) {
1591
1645
  const eventListener = {
1592
- type: EventType.CompositeCustom,
1646
+ type: M2EventType.CompositeCustom,
1593
1647
  compositeType: "VirtualKeyboardKeyDown",
1594
- entityUuid: this.uuid,
1648
+ nodeUuid: this.uuid,
1595
1649
  callback
1596
1650
  };
1597
- this.addVirtualKeyboardEventListener(
1598
- replaceExistingCallback,
1599
- eventListener
1600
- );
1651
+ this.addVirtualKeyboardEventListener(eventListener, options);
1601
1652
  }
1602
1653
  /**
1603
1654
  * Executes a callback when the user releases a key.
1604
1655
  *
1605
1656
  * @param callback - function to execute
1606
- * @param replaceExistingCallback - should the provided callback replace
1607
- * any existing callbacks of the same event type on this entity? Usually
1608
- * there should be only one callback defined, instead of chaining multiple
1609
- * ones. It is strongly recommended not to change this, unless you have a
1610
- * special use case. Default is true.
1657
+ * @param options
1611
1658
  */
1612
- onKeyUp(callback, replaceExistingCallback = true) {
1659
+ onKeyUp(callback, options) {
1613
1660
  const eventListener = {
1614
- type: EventType.CompositeCustom,
1661
+ type: M2EventType.CompositeCustom,
1615
1662
  compositeType: "VirtualKeyboardKeyUp",
1616
- entityUuid: this.uuid,
1663
+ nodeUuid: this.uuid,
1617
1664
  callback
1618
1665
  };
1619
- this.addVirtualKeyboardEventListener(
1620
- replaceExistingCallback,
1621
- eventListener
1622
- );
1666
+ this.addVirtualKeyboardEventListener(eventListener, options);
1623
1667
  }
1624
- addVirtualKeyboardEventListener(replaceExistingCallback, eventListener) {
1625
- if (replaceExistingCallback) {
1668
+ addVirtualKeyboardEventListener(eventListener, options) {
1669
+ if (options?.replaceExisting) {
1626
1670
  this.eventListeners = this.eventListeners.filter(
1627
- (listener) => !(listener.entityUuid === eventListener.entityUuid && listener.type === eventListener.type && listener.compositeType === eventListener.compositeType)
1671
+ (listener) => !(listener.nodeUuid === eventListener.nodeUuid && listener.type === eventListener.type && listener.compositeType === eventListener.compositeType)
1628
1672
  );
1629
1673
  }
1630
1674
  this.eventListeners.push(eventListener);
1631
1675
  }
1676
+ /**
1677
+ * Does the `VirtualKeyboard` respond to user events? Default is true.
1678
+ */
1679
+ get isUserInteractionEnabled() {
1680
+ return this._isUserInteractionEnabled;
1681
+ }
1682
+ /**
1683
+ * Does the `VirtualKeyboard` respond to user events? Default is true.
1684
+ */
1685
+ set isUserInteractionEnabled(isUserInteractionEnabled) {
1686
+ this._isUserInteractionEnabled = isUserInteractionEnabled;
1687
+ this.keyShapes.forEach((keyShape) => {
1688
+ keyShape.isUserInteractionEnabled = isUserInteractionEnabled;
1689
+ });
1690
+ }
1632
1691
  update() {
1633
1692
  super.update();
1634
1693
  }
@@ -1642,31 +1701,31 @@ class VirtualKeyboard extends Composite {
1642
1701
  });
1643
1702
  }
1644
1703
  duplicate(newName) {
1645
- throw new Error("Method not implemented.");
1704
+ throw new Error(`Method not implemented. ${newName}`);
1646
1705
  }
1647
1706
  }
1648
1707
 
1649
- const SCENE_TRANSITION_EASING = Easings.sinusoidalInOut;
1708
+ const SCENE_TRANSITION_EASING$1 = Easings.sinusoidalInOut;
1650
1709
  const SCENE_TRANSITION_DURATION = 500;
1651
1710
  class Instructions extends Story {
1652
1711
  /**
1653
- * Create an array of scenes containing instructions on how to complete the task
1712
+ * Creates an array of scenes containing instructions on how to complete the assessment
1654
1713
  *
1655
1714
  * @param options - {@link InstructionsOptions}
1656
- * @returns
1715
+ * @returns instruction scenes
1657
1716
  */
1658
- static Create(options) {
1717
+ static create(options) {
1659
1718
  const scenes = new Array();
1660
1719
  options.instructionScenes.forEach((s, i) => {
1661
1720
  const nextSceneTransition = s.nextSceneTransition ?? options.nextSceneTransition ?? Transition.slide({
1662
1721
  direction: TransitionDirection.Left,
1663
1722
  duration: SCENE_TRANSITION_DURATION,
1664
- easing: SCENE_TRANSITION_EASING
1723
+ easing: SCENE_TRANSITION_EASING$1
1665
1724
  });
1666
1725
  const backSceneTransition = s.backSceneTransition ?? options.backSceneTransition ?? Transition.slide({
1667
1726
  direction: TransitionDirection.Right,
1668
1727
  duration: SCENE_TRANSITION_DURATION,
1669
- easing: SCENE_TRANSITION_EASING
1728
+ easing: SCENE_TRANSITION_EASING$1
1670
1729
  });
1671
1730
  const backButtonText = s.backButtonText ?? options.backButtonText ?? "Back";
1672
1731
  const nextButtonText = s.nextButtonText ?? options.nextButtonText ?? "Next";
@@ -1851,9 +1910,605 @@ class Instructions extends Story {
1851
1910
  });
1852
1911
  return scenes;
1853
1912
  }
1913
+ /**
1914
+ * Creates an array of scenes containing instructions on how to complete the assessment
1915
+ *
1916
+ * @deprecated Use {@link Instructions.create} instead (lower case method name "create")
1917
+ *
1918
+ * @param options - {@link InstructionsOptions}
1919
+ * @returns instruction scenes
1920
+ */
1921
+ static Create(options) {
1922
+ return this.create(options);
1923
+ }
1924
+ }
1925
+
1926
+ const SCENE_TRANSITION_EASING = Easings.sinusoidalInOut;
1927
+ const SCENE_TRANSITION_DURATION_MS = 500;
1928
+ class CountdownScene extends Scene {
1929
+ /**
1930
+ * A scene that counts down from a specified number to zero, then transitions to the next scene.
1931
+ *
1932
+ * @param options - {@link CountdownSceneOptions}
1933
+ */
1934
+ constructor(options) {
1935
+ super(options);
1936
+ if (options?.transitionDurationMilliseconds !== void 0 && options?.transition) {
1937
+ throw new Error(
1938
+ "Both transition and transitionDurationMilliseconds options were provided. Only one should be provided. If using a custom transition, then the duration of that transition must be specified within the custom transition."
1939
+ );
1940
+ }
1941
+ let timerShape;
1942
+ if (options?.timerShape?.circle === void 0 && options?.timerShape?.rectangle === void 0 || options?.timerShape.circle !== void 0) {
1943
+ timerShape = new Shape({
1944
+ circleOfRadius: options?.timerShape?.circle?.radius ?? 100,
1945
+ layout: {
1946
+ constraints: {
1947
+ topToTopOf: this,
1948
+ bottomToBottomOf: this,
1949
+ startToStartOf: this,
1950
+ endToEndOf: this,
1951
+ verticalBias: options?.timerShape?.verticalBias ?? 0.5
1952
+ }
1953
+ },
1954
+ fillColor: options?.timerShape?.fillColor ?? WebColors.RoyalBlue
1955
+ });
1956
+ this.addChild(timerShape);
1957
+ } else if (options?.timerShape.rectangle !== void 0) {
1958
+ timerShape = new Shape({
1959
+ rect: {
1960
+ width: options?.timerShape?.rectangle?.width ?? 200,
1961
+ height: options?.timerShape?.rectangle?.height ?? 200
1962
+ },
1963
+ cornerRadius: options?.timerShape?.rectangle?.cornerRadius,
1964
+ layout: {
1965
+ constraints: {
1966
+ topToTopOf: this,
1967
+ bottomToBottomOf: this,
1968
+ startToStartOf: this,
1969
+ endToEndOf: this,
1970
+ verticalBias: options?.timerShape?.verticalBias ?? 0.5
1971
+ }
1972
+ },
1973
+ fillColor: options?.timerShape?.fillColor ?? WebColors.RoyalBlue
1974
+ });
1975
+ this.addChild(timerShape);
1976
+ } else {
1977
+ throw new Error("Invalid timer shape options.");
1978
+ }
1979
+ const timerInitialNumber = Math.floor(options.milliseconds / 1e3);
1980
+ const timerNumberLabel = new Label({
1981
+ // Number text will be set in onSetup()
1982
+ text: "",
1983
+ fontSize: options?.timerNumbersFontSize ?? 50,
1984
+ fontName: options?.timerNumbersFontName,
1985
+ fontColor: options?.timerNumbersFontColor ?? WebColors.White
1986
+ });
1987
+ timerShape.addChild(timerNumberLabel);
1988
+ const textLabel = new Label({
1989
+ text: options?.text ?? "GET READY",
1990
+ fontSize: options?.textFontSize ?? 50,
1991
+ fontName: options?.textFontName,
1992
+ fontColor: options?.textFontColor,
1993
+ layout: {
1994
+ marginTop: options?.textMarginTop ?? 32,
1995
+ constraints: {
1996
+ topToBottomOf: timerShape,
1997
+ startToStartOf: this,
1998
+ endToEndOf: this
1999
+ }
2000
+ }
2001
+ });
2002
+ this.addChild(textLabel);
2003
+ const countdownSequence = new Array();
2004
+ for (let i = timerInitialNumber - 1; i > 0; i--) {
2005
+ countdownSequence.push(Action.wait({ duration: 1e3 }));
2006
+ countdownSequence.push(
2007
+ Action.custom({
2008
+ callback: () => {
2009
+ timerNumberLabel.text = i.toString();
2010
+ }
2011
+ })
2012
+ );
2013
+ }
2014
+ countdownSequence.push(Action.wait({ duration: 1e3 }));
2015
+ countdownSequence.push(
2016
+ Action.custom({
2017
+ callback: () => {
2018
+ timerNumberLabel.text = options?.timerZeroString ?? "0";
2019
+ }
2020
+ })
2021
+ );
2022
+ if (options?.zeroDwellMilliseconds !== void 0) {
2023
+ countdownSequence.push(
2024
+ Action.wait({ duration: options.zeroDwellMilliseconds })
2025
+ );
2026
+ }
2027
+ countdownSequence.push(
2028
+ Action.custom({
2029
+ callback: () => {
2030
+ const game = this.game;
2031
+ const isLastScene = game.scenes.indexOf(this) === game.scenes.length - 1;
2032
+ if (isLastScene) {
2033
+ game.end();
2034
+ }
2035
+ const nextScene = game.scenes[game.scenes.indexOf(this) + 1];
2036
+ game.presentScene(
2037
+ nextScene,
2038
+ options?.transition ?? Transition.slide({
2039
+ direction: TransitionDirection.Left,
2040
+ duration: options?.transitionDurationMilliseconds ?? SCENE_TRANSITION_DURATION_MS,
2041
+ easing: SCENE_TRANSITION_EASING
2042
+ })
2043
+ );
2044
+ }
2045
+ })
2046
+ );
2047
+ this.onSetup(() => {
2048
+ timerNumberLabel.text = timerInitialNumber.toString();
2049
+ });
2050
+ this.onAppear(() => {
2051
+ this.run(Action.sequence(countdownSequence));
2052
+ });
2053
+ }
2054
+ }
2055
+
2056
+ class LocalePicker extends Composite {
2057
+ /**
2058
+ * An icon and dialog box for selecting a locale from a list of options.
2059
+ *
2060
+ * @remarks This composite node is composed of a dialog box that appears
2061
+ * when the user taps a globe icon. Typically, the `LocalePicker` will be
2062
+ * added as a free node to the game so that it exists independently of
2063
+ * the game's scenes. The dialog box contains a list of locales that the
2064
+ * user can choose from. By default, this list is populated with the locales
2065
+ * in the game's `Translation` object. When the user selects a locale, the
2066
+ * dialog box disappears and the locale is set as the game's current locale.
2067
+ * The dialog box is automatically sized to fit the number of locale
2068
+ * options.
2069
+ *
2070
+ * @example
2071
+ * let localePicker: LocalePicker;
2072
+ * if (game.getParameter<boolean>("show_locale_picker")) {
2073
+ * localePicker = new LocalePicker();
2074
+ * game.addFreeNode(localePicker);
2075
+ * }
2076
+ *
2077
+ * @param options - {@link LocalePickerOptions}
2078
+ */
2079
+ constructor(options) {
2080
+ super(options);
2081
+ this.compositeType = "LocalePicker";
2082
+ this.zPosition = Number.MAX_VALUE;
2083
+ this.DEFAULT_FONT_SIZE = 24;
2084
+ this.automaticallyChangeLocale = true;
2085
+ this._localeOptions = new Array();
2086
+ this._backgroundColor = WebColors.White;
2087
+ this._fontSize = this.DEFAULT_FONT_SIZE;
2088
+ this._fontColor = WebColors.Black;
2089
+ this._cornerRadius = 8;
2090
+ this._overlayAlpha = 0.5;
2091
+ this._icon = {
2092
+ // public domain SVG from https://commons.wikimedia.org/wiki/File:Globe_icon.svg
2093
+ svgString: `<svg xmlns="http://www.w3.org/2000/svg" width="420" height="420" stroke="#000" fill="none"><path stroke-width="26" d="M209 15a195 195 0 1 0 2 0z"/><path stroke-width="18" d="M210 15v390m195-195H15M59 90a260 260 0 0 0 302 0m0 240a260 260 0 0 0-302 0M195 20a250 250 0 0 0 0 382m30 0a250 250 0 0 0 0-382"/></svg>`,
2094
+ height: 32,
2095
+ width: 32
2096
+ };
2097
+ this._iconPosition = { x: 32, y: 32 };
2098
+ /**
2099
+ * Wrap displayed locale in double angle quotes if it is the current locale.
2100
+ * Note: Although the code editor will allow us to enter almost any
2101
+ * unicode character, it will not render correctly if the font does
2102
+ * not support the character. Thus, be careful to use characters that
2103
+ * are supported by the font. For example, check a page like
2104
+ * https://www.fontspace.com/roboto-font-f13281 to see which characters
2105
+ * are supported by Roboto Regular, which is often the default font in
2106
+ * m2c2kit. Emoji or checkmarks like ✓ are not in Roboto Regular!
2107
+ */
2108
+ this.LEFT_SELECTION_INDICATOR = "\xAB";
2109
+ this.RIGHT_SELECTION_INDICATOR = "\xBB";
2110
+ if (!options) {
2111
+ return;
2112
+ }
2113
+ if (options.localeOptions) {
2114
+ this.localeOptions = options.localeOptions;
2115
+ }
2116
+ if (options.backgroundColor) {
2117
+ this.backgroundColor = options.backgroundColor;
2118
+ }
2119
+ if (options.overlayAlpha !== void 0) {
2120
+ this.overlayAlpha = options.overlayAlpha;
2121
+ }
2122
+ if (options.fontSize !== void 0) {
2123
+ this.fontSize = options.fontSize;
2124
+ }
2125
+ if (options.fontColor) {
2126
+ this.fontColor = options.fontColor;
2127
+ }
2128
+ if (options.cornerRadius) {
2129
+ this.cornerRadius = options.cornerRadius;
2130
+ }
2131
+ if (options.currentLocale !== void 0) {
2132
+ this.currentLocale = options.currentLocale;
2133
+ }
2134
+ if (options.icon) {
2135
+ this.icon = options.icon;
2136
+ }
2137
+ if (options.automaticallyChangeLocale !== void 0) {
2138
+ this.automaticallyChangeLocale = options.automaticallyChangeLocale;
2139
+ }
2140
+ }
2141
+ /**
2142
+ * Executes a callback when the user selects a locale.
2143
+ *
2144
+ * @param callback - function to execute
2145
+ * @param options - {@link CallbackOptions}
2146
+ */
2147
+ onResult(callback, options) {
2148
+ const eventListener = {
2149
+ type: M2EventType.CompositeCustom,
2150
+ compositeType: "LocalePickerResult",
2151
+ nodeUuid: this.uuid,
2152
+ callback
2153
+ };
2154
+ if (options?.replaceExisting) {
2155
+ this.eventListeners = this.eventListeners.filter(
2156
+ (listener) => !(listener.nodeUuid === eventListener.nodeUuid && listener.type === "LocalePickerResult")
2157
+ );
2158
+ }
2159
+ this.eventListeners.push(eventListener);
2160
+ }
2161
+ initialize() {
2162
+ if (this.currentLocale === void 0) {
2163
+ this.currentLocale = this.game.i18n?.locale;
2164
+ }
2165
+ if (this.localeOptions.length === 0) {
2166
+ const locales = Object.keys(this.game.i18n?.translation || {});
2167
+ locales.filter((locale) => locale !== "configuration").forEach((locale) => {
2168
+ this.localeOptions.push({
2169
+ text: this.game.i18n?.translation[locale].localeName || locale,
2170
+ locale,
2171
+ svg: this.game.i18n?.translation[locale].localeSvg
2172
+ });
2173
+ });
2174
+ }
2175
+ if (this.localeOptions.length === 0) {
2176
+ throw new Error("No locales available for LocalePicker");
2177
+ }
2178
+ this.children.filter((child) => child.name !== "localePickerIcon").forEach((child) => this.removeChild(child));
2179
+ this.game.imageManager.loadImages([
2180
+ {
2181
+ imageName: "__localePickerIcon",
2182
+ svgString: this.icon.svgString,
2183
+ height: this.icon.height,
2184
+ width: this.icon.width
2185
+ }
2186
+ ]);
2187
+ if (!this.iconSprite) {
2188
+ this.iconSprite = new Sprite({
2189
+ // name is how we refer to this sprite, as a node.
2190
+ name: "localePickerIcon",
2191
+ // imageName is the loaded image that we can assign to the sprite
2192
+ imageName: "__localePickerIcon",
2193
+ position: this.iconPosition,
2194
+ isUserInteractionEnabled: true
2195
+ });
2196
+ this.addChild(this.iconSprite);
2197
+ this.iconSprite.onTapDown(() => {
2198
+ this.setDialogVisibility(true);
2199
+ });
2200
+ }
2201
+ const overlay = new Shape({
2202
+ rect: {
2203
+ width: Globals.canvasCssWidth,
2204
+ height: Globals.canvasCssHeight,
2205
+ x: Globals.canvasCssWidth / 2,
2206
+ y: Globals.canvasCssHeight / 2
2207
+ },
2208
+ fillColor: [0, 0, 0, this.overlayAlpha],
2209
+ zPosition: -1,
2210
+ isUserInteractionEnabled: true,
2211
+ hidden: true
2212
+ });
2213
+ overlay.onTapDown((e) => {
2214
+ e.handled = true;
2215
+ if (this.eventListeners.length > 0) {
2216
+ this.eventListeners.filter((listener) => listener.type === "LocalePickerResult").forEach((listener) => {
2217
+ const languagePickerEvent = {
2218
+ type: "LocalePickerResult",
2219
+ target: this,
2220
+ handled: false,
2221
+ result: {
2222
+ locale: void 0
2223
+ }
2224
+ };
2225
+ listener.callback(languagePickerEvent);
2226
+ });
2227
+ }
2228
+ this.setDialogVisibility(false);
2229
+ });
2230
+ this.addChild(overlay);
2231
+ const lineHeight = this.fontSize / this.DEFAULT_FONT_SIZE * 50;
2232
+ const dialogHeight = this.localeOptions.length * lineHeight;
2233
+ const dialogWidth = Globals.canvasCssWidth / 2;
2234
+ const sceneCenter = {
2235
+ x: Globals.canvasCssWidth / 2,
2236
+ y: Globals.canvasCssHeight / 2
2237
+ };
2238
+ const localeDialog = new Shape({
2239
+ rect: {
2240
+ width: dialogWidth,
2241
+ height: dialogHeight,
2242
+ x: sceneCenter.x,
2243
+ y: sceneCenter.y
2244
+ },
2245
+ cornerRadius: this.cornerRadius,
2246
+ fillColor: this.backgroundColor,
2247
+ isUserInteractionEnabled: true,
2248
+ hidden: true
2249
+ });
2250
+ localeDialog.onTapDown((e) => {
2251
+ e.handled = true;
2252
+ });
2253
+ this.addChild(localeDialog);
2254
+ for (let i = 0; i < this.localeOptions.length; i++) {
2255
+ const localeOption = this.localeOptions[i];
2256
+ if (!localeOption.svg) {
2257
+ let labelText = localeOption.text;
2258
+ if (this.currentLocale === localeOption.locale) {
2259
+ labelText = `${this.LEFT_SELECTION_INDICATOR} ${labelText} ${this.RIGHT_SELECTION_INDICATOR}`;
2260
+ }
2261
+ const text = new Label({
2262
+ text: labelText,
2263
+ fontSize: this.fontSize,
2264
+ fontColor: this.fontColor,
2265
+ position: {
2266
+ x: sceneCenter.x,
2267
+ y: sceneCenter.y + i * lineHeight - dialogHeight / 2 + lineHeight / 2
2268
+ },
2269
+ isUserInteractionEnabled: true,
2270
+ zPosition: 1,
2271
+ hidden: true,
2272
+ // do not localize the text of each language option
2273
+ localize: false
2274
+ });
2275
+ text.onTapDown((e) => {
2276
+ this.handleLocaleSelection(e, localeOption);
2277
+ });
2278
+ this.addChild(text);
2279
+ } else {
2280
+ this.game.imageManager.loadImages([
2281
+ {
2282
+ imageName: localeOption.text,
2283
+ svgString: localeOption.svg.svgString,
2284
+ height: localeOption.svg.height,
2285
+ width: localeOption.svg.width
2286
+ }
2287
+ ]);
2288
+ const localeSprite = new Sprite({
2289
+ imageName: localeOption.text,
2290
+ position: {
2291
+ x: sceneCenter.x,
2292
+ y: sceneCenter.y + i * lineHeight - dialogHeight / 2 + lineHeight / 2
2293
+ },
2294
+ isUserInteractionEnabled: true,
2295
+ zPosition: 1,
2296
+ hidden: true
2297
+ });
2298
+ this.addChild(localeSprite);
2299
+ if (this.currentLocale === localeOption.locale) {
2300
+ const leftSelectionIndicator = new Label({
2301
+ text: this.LEFT_SELECTION_INDICATOR,
2302
+ fontSize: this.fontSize,
2303
+ fontColor: this.fontColor,
2304
+ position: {
2305
+ x: sceneCenter.x - localeOption.svg.width / 2,
2306
+ y: sceneCenter.y + i * lineHeight - dialogHeight / 2 + lineHeight / 2
2307
+ },
2308
+ hidden: true,
2309
+ // do not localize the left selection indicator
2310
+ localize: false
2311
+ });
2312
+ this.addChild(leftSelectionIndicator);
2313
+ const rightSelectionIndicator = new Label({
2314
+ text: this.RIGHT_SELECTION_INDICATOR,
2315
+ fontSize: this._fontSize,
2316
+ fontColor: this.fontColor,
2317
+ position: {
2318
+ x: sceneCenter.x + localeOption.svg.width / 2,
2319
+ y: sceneCenter.y + i * lineHeight - dialogHeight / 2 + lineHeight / 2
2320
+ },
2321
+ hidden: true,
2322
+ // do not localize the left selection indicator
2323
+ localize: false
2324
+ });
2325
+ this.addChild(rightSelectionIndicator);
2326
+ }
2327
+ localeSprite.onTapDown((e) => {
2328
+ this.handleLocaleSelection(e, localeOption);
2329
+ });
2330
+ }
2331
+ }
2332
+ this.needsInitialization = false;
2333
+ }
2334
+ handleLocaleSelection(e, localeOption) {
2335
+ e.handled = true;
2336
+ if (this.eventListeners.length > 0) {
2337
+ this.eventListeners.filter(
2338
+ (listener) => listener.type === M2EventType.CompositeCustom && listener.compositeType === "LocalePickerResult" && listener.nodeUuid == this.uuid
2339
+ ).forEach((listener) => {
2340
+ const languagePickerEvent = {
2341
+ type: M2EventType.CompositeCustom,
2342
+ compositeType: "LocalePickerResult",
2343
+ target: this,
2344
+ handled: false,
2345
+ result: {
2346
+ locale: localeOption.locale
2347
+ }
2348
+ };
2349
+ listener.callback(languagePickerEvent);
2350
+ });
2351
+ }
2352
+ this.setDialogVisibility(false);
2353
+ if (this.automaticallyChangeLocale) {
2354
+ this.game.i18n?.switchToLocale(localeOption.locale);
2355
+ this.currentLocale = localeOption.locale;
2356
+ }
2357
+ }
2358
+ setDialogVisibility(visible) {
2359
+ this.children.filter((child) => child.name !== "localePickerIcon").forEach((child) => {
2360
+ child.hidden = !visible;
2361
+ });
2362
+ }
2363
+ get backgroundColor() {
2364
+ return this._backgroundColor;
2365
+ }
2366
+ set backgroundColor(backgroundColor) {
2367
+ this._backgroundColor = backgroundColor;
2368
+ this.needsInitialization = true;
2369
+ }
2370
+ get fontSize() {
2371
+ return this._fontSize;
2372
+ }
2373
+ set fontSize(fontSize) {
2374
+ this._fontSize = fontSize;
2375
+ this.needsInitialization = true;
2376
+ }
2377
+ get fontColor() {
2378
+ return this._fontColor;
2379
+ }
2380
+ set fontColor(fontColor) {
2381
+ this._fontColor = fontColor;
2382
+ this.needsInitialization = true;
2383
+ }
2384
+ get cornerRadius() {
2385
+ return this._cornerRadius;
2386
+ }
2387
+ set cornerRadius(cornerRadius) {
2388
+ this._cornerRadius = cornerRadius;
2389
+ this.needsInitialization = true;
2390
+ }
2391
+ get overlayAlpha() {
2392
+ return this._overlayAlpha;
2393
+ }
2394
+ set overlayAlpha(alpha) {
2395
+ this._overlayAlpha = alpha;
2396
+ this.needsInitialization = true;
2397
+ }
2398
+ get icon() {
2399
+ const localePicker = this;
2400
+ return {
2401
+ get svgString() {
2402
+ return localePicker._icon.svgString;
2403
+ },
2404
+ set svgString(svgString) {
2405
+ localePicker._icon.svgString = svgString;
2406
+ localePicker.needsInitialization = true;
2407
+ },
2408
+ get imageName() {
2409
+ return localePicker._icon.imageName;
2410
+ },
2411
+ set imageName(imageName) {
2412
+ localePicker._icon.imageName = imageName;
2413
+ localePicker.needsInitialization = true;
2414
+ },
2415
+ get height() {
2416
+ return localePicker._icon.height;
2417
+ },
2418
+ set height(height) {
2419
+ localePicker._icon.height = height;
2420
+ localePicker.needsInitialization = true;
2421
+ },
2422
+ get width() {
2423
+ return localePicker._icon.width;
2424
+ },
2425
+ set width(width) {
2426
+ localePicker._icon.width = width;
2427
+ localePicker.needsInitialization = true;
2428
+ }
2429
+ };
2430
+ }
2431
+ set icon(icon) {
2432
+ this._icon = icon;
2433
+ this.needsInitialization = true;
2434
+ }
2435
+ get iconPosition() {
2436
+ const localePicker = this;
2437
+ return {
2438
+ get x() {
2439
+ return localePicker._iconPosition.x;
2440
+ },
2441
+ set x(x) {
2442
+ localePicker._iconPosition.x = x;
2443
+ if (localePicker.iconSprite) {
2444
+ localePicker.iconSprite.position = localePicker._iconPosition;
2445
+ }
2446
+ localePicker.needsInitialization = true;
2447
+ },
2448
+ get y() {
2449
+ return localePicker._iconPosition.y;
2450
+ },
2451
+ set y(y) {
2452
+ localePicker._iconPosition.y = y;
2453
+ if (localePicker.iconSprite) {
2454
+ localePicker.iconSprite.position = localePicker._iconPosition;
2455
+ }
2456
+ localePicker.needsInitialization = true;
2457
+ }
2458
+ };
2459
+ }
2460
+ set iconPosition(position) {
2461
+ this._iconPosition = position;
2462
+ if (this.iconSprite) {
2463
+ this.iconSprite.position = position;
2464
+ }
2465
+ this.needsInitialization = true;
2466
+ }
2467
+ get localeOptions() {
2468
+ return this._localeOptions;
2469
+ }
2470
+ set localeOptions(options) {
2471
+ this._localeOptions = options;
2472
+ this.needsInitialization = true;
2473
+ }
2474
+ get currentLocale() {
2475
+ return this._currentLocale;
2476
+ }
2477
+ set currentLocale(locale) {
2478
+ if (locale === this.currentLocale) {
2479
+ return;
2480
+ }
2481
+ this._currentLocale = locale;
2482
+ this.needsInitialization = true;
2483
+ }
2484
+ update() {
2485
+ super.update();
2486
+ }
2487
+ draw(canvas) {
2488
+ super.drawChildren(canvas);
2489
+ }
2490
+ warmup(canvas) {
2491
+ this.initialize();
2492
+ this.children.filter((child) => child.isDrawable).forEach((child) => {
2493
+ child.warmup(canvas);
2494
+ });
2495
+ }
2496
+ /**
2497
+ * Duplicates a node using deep copy.
2498
+ *
2499
+ * @remarks This is a deep recursive clone (node and children).
2500
+ * The uuid property of all duplicated nodes will be newly created,
2501
+ * because uuid must be unique.
2502
+ *
2503
+ * @param newName - optional name of the new, duplicated node. If not
2504
+ * provided, name will be the new uuid
2505
+ */
2506
+ duplicate(newName) {
2507
+ throw new Error(`duplicate not implemented. ${newName}`);
2508
+ }
1854
2509
  }
1855
2510
 
1856
- console.log("\u26AA @m2c2kit/addons version 0.3.13 (d8a00e86)");
2511
+ console.log("\u26AA @m2c2kit/addons version 0.3.15 (b3a70752)");
1857
2512
 
1858
- export { Button, Dialog, DialogResult, DrawPad, DrawPadEventType, DrawPadItemEventType, Grid, Instructions, VirtualKeyboard };
2513
+ export { Button, CountdownScene, Dialog, DialogResult, DrawPad, DrawPadEventType, DrawPadItemEventType, Grid, Instructions, LocalePicker, VirtualKeyboard };
1859
2514
  //# sourceMappingURL=index.js.map