@vpmedia/phaser 1.0.1 → 1.0.2

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 (107) hide show
  1. package/dist/phaser.cjs.LICENSE.txt +1 -1
  2. package/dist/phaser.js.LICENSE.txt +1 -1
  3. package/package.json +2 -3
  4. package/src/index.js +99 -0
  5. package/src/phaser/core/animation.js +355 -0
  6. package/src/phaser/core/animation_manager.js +238 -0
  7. package/src/phaser/core/animation_parser.js +130 -0
  8. package/src/phaser/core/array_set.js +108 -0
  9. package/src/phaser/core/cache.js +558 -0
  10. package/src/phaser/core/const.js +106 -0
  11. package/src/phaser/core/device.js +67 -0
  12. package/src/phaser/core/device_util.js +386 -0
  13. package/src/phaser/core/dom.js +207 -0
  14. package/src/phaser/core/event_manager.js +243 -0
  15. package/src/phaser/core/factory.js +74 -0
  16. package/src/phaser/core/frame.js +75 -0
  17. package/src/phaser/core/frame_data.js +84 -0
  18. package/src/phaser/core/frame_util.js +31 -0
  19. package/src/phaser/core/game.js +412 -0
  20. package/src/phaser/core/input.js +401 -0
  21. package/src/phaser/core/input_button.js +102 -0
  22. package/src/phaser/core/input_handler.js +687 -0
  23. package/src/phaser/core/input_mouse.js +289 -0
  24. package/src/phaser/core/input_mspointer.js +197 -0
  25. package/src/phaser/core/input_pointer.js +427 -0
  26. package/src/phaser/core/input_touch.js +157 -0
  27. package/src/phaser/core/loader.js +946 -0
  28. package/src/phaser/core/loader_parser.js +105 -0
  29. package/src/phaser/core/raf.js +46 -0
  30. package/src/phaser/core/raf_fb.js +75 -0
  31. package/src/phaser/core/raf_to.js +34 -0
  32. package/src/phaser/core/scale_manager.js +806 -0
  33. package/src/phaser/core/scene.js +66 -0
  34. package/src/phaser/core/scene_manager.js +310 -0
  35. package/src/phaser/core/signal.js +175 -0
  36. package/src/phaser/core/signal_binding.js +69 -0
  37. package/src/phaser/core/sound.js +538 -0
  38. package/src/phaser/core/sound_manager.js +365 -0
  39. package/src/phaser/core/stage.js +108 -0
  40. package/src/phaser/core/time.js +203 -0
  41. package/src/phaser/core/timer.js +276 -0
  42. package/src/phaser/core/timer_event.js +21 -0
  43. package/src/phaser/core/tween.js +329 -0
  44. package/src/phaser/core/tween_data.js +258 -0
  45. package/src/phaser/core/tween_easing.js +316 -0
  46. package/src/phaser/core/tween_manager.js +185 -0
  47. package/src/phaser/core/world.js +18 -0
  48. package/src/phaser/display/bitmap_text.js +322 -0
  49. package/src/phaser/display/button.js +194 -0
  50. package/src/phaser/display/canvas/buffer.js +36 -0
  51. package/src/phaser/display/canvas/graphics.js +227 -0
  52. package/src/phaser/display/canvas/masker.js +39 -0
  53. package/src/phaser/display/canvas/pool.js +121 -0
  54. package/src/phaser/display/canvas/renderer.js +123 -0
  55. package/src/phaser/display/canvas/tinter.js +141 -0
  56. package/src/phaser/display/canvas/util.js +151 -0
  57. package/src/phaser/display/display_object.js +597 -0
  58. package/src/phaser/display/graphics.js +723 -0
  59. package/src/phaser/display/graphics_data.js +27 -0
  60. package/src/phaser/display/graphics_data_util.js +14 -0
  61. package/src/phaser/display/group.js +227 -0
  62. package/src/phaser/display/image.js +288 -0
  63. package/src/phaser/display/sprite_batch.js +15 -0
  64. package/src/phaser/display/sprite_util.js +248 -0
  65. package/src/phaser/display/text.js +1089 -0
  66. package/src/phaser/display/webgl/abstract_filter.js +25 -0
  67. package/src/phaser/display/webgl/base_texture.js +68 -0
  68. package/src/phaser/display/webgl/blend_manager.js +35 -0
  69. package/src/phaser/display/webgl/earcut.js +647 -0
  70. package/src/phaser/display/webgl/earcut_node.js +28 -0
  71. package/src/phaser/display/webgl/fast_sprite_batch.js +242 -0
  72. package/src/phaser/display/webgl/filter_manager.js +46 -0
  73. package/src/phaser/display/webgl/filter_texture.js +61 -0
  74. package/src/phaser/display/webgl/graphics.js +618 -0
  75. package/src/phaser/display/webgl/graphics_data.js +42 -0
  76. package/src/phaser/display/webgl/mask_manager.js +36 -0
  77. package/src/phaser/display/webgl/render_texture.js +81 -0
  78. package/src/phaser/display/webgl/renderer.js +234 -0
  79. package/src/phaser/display/webgl/shader/complex.js +74 -0
  80. package/src/phaser/display/webgl/shader/fast.js +97 -0
  81. package/src/phaser/display/webgl/shader/normal.js +225 -0
  82. package/src/phaser/display/webgl/shader/primitive.js +72 -0
  83. package/src/phaser/display/webgl/shader/strip.js +77 -0
  84. package/src/phaser/display/webgl/shader_manager.js +89 -0
  85. package/src/phaser/display/webgl/sprite_batch.js +320 -0
  86. package/src/phaser/display/webgl/stencil_manager.js +170 -0
  87. package/src/phaser/display/webgl/texture.js +117 -0
  88. package/src/phaser/display/webgl/texture_util.js +32 -0
  89. package/src/phaser/display/webgl/util.js +74 -0
  90. package/src/phaser/geom/circle.js +186 -0
  91. package/src/phaser/geom/ellipse.js +65 -0
  92. package/src/phaser/geom/line.js +190 -0
  93. package/src/phaser/geom/matrix.js +147 -0
  94. package/src/phaser/geom/point.js +164 -0
  95. package/src/phaser/geom/polygon.js +141 -0
  96. package/src/phaser/geom/rectangle.js +306 -0
  97. package/src/phaser/geom/rounded_rectangle.js +36 -0
  98. package/src/phaser/geom/util/circle.js +115 -0
  99. package/src/phaser/geom/util/ellipse.js +30 -0
  100. package/src/phaser/geom/util/line.js +130 -0
  101. package/src/phaser/geom/util/matrix.js +48 -0
  102. package/src/phaser/geom/util/point.js +276 -0
  103. package/src/phaser/geom/util/polygon.js +24 -0
  104. package/src/phaser/geom/util/rectangle.js +212 -0
  105. package/src/phaser/geom/util/rounded_rectangle.js +28 -0
  106. package/src/phaser/util/math.js +279 -0
  107. package/src/phaser/util/string.js +26 -0
@@ -0,0 +1,1089 @@
1
+ /**
2
+ * @author Andras Csizmadia <andras@vpmedia.hu>
3
+ * @author Richard Davey <rich@photonstorm.com>
4
+ * @copyright Copyright (c) 2018-present Richard Davey, Photon Storm Ltd., Andras Csizmadia <andras@vpmedia.hu> (www.vpmedia.hu)
5
+ */
6
+ import Point from '../geom/point';
7
+ import Rectangle from '../geom/rectangle';
8
+ import Image from './image';
9
+ import { create, remove } from './canvas/pool';
10
+ import { textureFromCanvas } from './webgl/texture_util';
11
+ import { TEXT } from '../core/const';
12
+ import { snapToCeil } from '../util/math';
13
+ import { renderCanvas, renderWebGL, getBounds } from './sprite_util';
14
+
15
+ export default class extends Image {
16
+
17
+ constructor(game, x, y, text = '', style = {}) {
18
+ super(game, x, y, null);
19
+ this.game = game;
20
+ this.type = TEXT;
21
+ this.canvas = create(this);
22
+ this.context = this.canvas.getContext('2d');
23
+ this.padding = new Point();
24
+ this.textBounds = null;
25
+ this.style = null;
26
+ this.colors = [];
27
+ this.strokeColors = [];
28
+ this.fontStyles = [];
29
+ this.fontWeights = [];
30
+ this.autoRound = false;
31
+ this.useAdvancedWrap = false;
32
+ this._res = game.renderer.resolution;
33
+ this._text = text.toString();
34
+ this._fontComponents = null;
35
+ this._lineSpacing = 0;
36
+ this._charCount = 0;
37
+ this._width = 0;
38
+ this._height = 0;
39
+ this.loadTexture(textureFromCanvas(this.canvas));
40
+ this.setStyle(style);
41
+ if (this._text !== '') {
42
+ this.updateText();
43
+ }
44
+ }
45
+
46
+ destroy() {
47
+ this.texture.destroy(true);
48
+ remove(this);
49
+ this.canvas = null;
50
+ this.context = null;
51
+ this.textBounds = null;
52
+ this.style = null;
53
+ this.colors = null;
54
+ this.strokeColors = null;
55
+ this.fontStyles = null;
56
+ this.fontWeights = null;
57
+ this.padding = null;
58
+ this._text = null;
59
+ this._fontComponents = null;
60
+ super.destroy();
61
+ }
62
+
63
+ setShadow(x = 0, y = 0, color = 'rgba(0, 0, 0, 1)', blur = 0, shadowStroke = true, shadowFill = true) {
64
+ this.style.shadowOffsetX = x;
65
+ this.style.shadowOffsetY = y;
66
+ this.style.shadowColor = color;
67
+ this.style.shadowBlur = blur;
68
+ this.style.shadowStroke = shadowStroke;
69
+ this.style.shadowFill = shadowFill;
70
+ this.dirty = true;
71
+ return this;
72
+ }
73
+
74
+ setStyle(style = null, update = false) {
75
+ style = JSON.parse(JSON.stringify(style)) || {};
76
+ style.font = style.font || 'bold 20pt Arial';
77
+ style.backgroundColor = style.backgroundColor || null;
78
+ style.fill = style.fill || 'black';
79
+ style.align = style.align || 'left';
80
+ style.boundsAlignH = style.boundsAlignH || 'left';
81
+ style.boundsAlignV = style.boundsAlignV || 'top';
82
+ style.stroke = style.stroke || 'black'; // provide a default, see: https://github.com/GoodBoyDigital/pixi.js/issues/136
83
+ style.strokeThickness = style.strokeThickness || 0;
84
+ style.wordWrap = style.wordWrap || false;
85
+ style.wordWrapWidth = style.wordWrapWidth || 100;
86
+ style.maxLines = style.maxLines || 0;
87
+ style.shadowOffsetX = style.shadowOffsetX || 0;
88
+ style.shadowOffsetY = style.shadowOffsetY || 0;
89
+ style.shadowColor = style.shadowColor || 'rgba(0,0,0,0)';
90
+ style.shadowBlur = style.shadowBlur || 0;
91
+ style.tabs = style.tabs || 0;
92
+ const components = this.fontToComponents(style.font);
93
+ if (style.fontStyle) {
94
+ components.fontStyle = style.fontStyle;
95
+ }
96
+ if (style.fontVariant) {
97
+ components.fontVariant = style.fontVariant;
98
+ }
99
+ if (style.fontWeight) {
100
+ components.fontWeight = style.fontWeight;
101
+ }
102
+ if (style.fontSize) {
103
+ if (typeof style.fontSize === 'number') {
104
+ style.fontSize += 'px';
105
+ }
106
+ components.fontSize = style.fontSize;
107
+ }
108
+ this._fontComponents = components;
109
+ style.font = this.componentsToFont(this._fontComponents);
110
+ this.style = style;
111
+ this.dirty = true;
112
+ if (update) {
113
+ this.updateText();
114
+ }
115
+ return this;
116
+ }
117
+
118
+ updateText() {
119
+ this.texture.baseTexture.resolution = this._res;
120
+ this.context.font = this.style.font;
121
+ let outputText = this.text;
122
+ if (this.style.wordWrap) {
123
+ outputText = this.runWordWrap(this.text);
124
+ }
125
+ // Split text into lines
126
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
127
+ // Calculate text width
128
+ const tabs = this.style.tabs;
129
+ const lineWidths = [];
130
+ let lineWidth = 0;
131
+ let maxLineWidth = 0;
132
+ const fontProperties = this.determineFontProperties(this.style.font);
133
+ let drawnLines = lines.length;
134
+ if (this.style.maxLines > 0 && this.style.maxLines < lines.length) {
135
+ drawnLines = this.style.maxLines;
136
+ }
137
+ this._charCount = 0;
138
+ for (let i = 0; i < drawnLines; i += 1) {
139
+ if (tabs === 0) {
140
+ // Simple layout (no tabs)
141
+ lineWidth = this.style.strokeThickness + this.padding.x;
142
+ if (this.colors.length > 0 || this.strokeColors.length > 0 || this.fontWeights.length > 0 || this.fontStyles.length > 0) {
143
+ lineWidth += this.measureLine(lines[i]);
144
+ } else {
145
+ lineWidth += this.context.measureText(lines[i]).width;
146
+ }
147
+ // Adjust for wrapped text
148
+ if (this.style.wordWrap) {
149
+ lineWidth -= this.context.measureText(' ').width;
150
+ }
151
+ } else {
152
+ // Complex layout (tabs)
153
+ const line = lines[i].split(/(?:\t)/);
154
+ lineWidth = this.padding.x + this.style.strokeThickness;
155
+ if (Array.isArray(tabs)) {
156
+ let tab = 0;
157
+ for (let c = 0; c < line.length; c += 1) {
158
+ let section = 0;
159
+ if (this.colors.length > 0 || this.strokeColors.length > 0 || this.fontWeights.length > 0 || this.fontStyles.length > 0) {
160
+ section = this.measureLine(line[c]);
161
+ } else {
162
+ section = Math.ceil(this.context.measureText(line[c]).width);
163
+ }
164
+ if (c > 0) {
165
+ tab += tabs[c - 1];
166
+ }
167
+ lineWidth = tab + section;
168
+ }
169
+ } else {
170
+ for (let c = 0; c < line.length; c += 1) {
171
+ // How far to the next tab?
172
+ if (this.colors.length > 0 || this.strokeColors.length > 0 || this.fontWeights.length > 0 || this.fontStyles.length > 0) {
173
+ lineWidth += this.measureLine(line[c]);
174
+ } else {
175
+ lineWidth += Math.ceil(this.context.measureText(line[c]).width);
176
+ }
177
+ const diff = snapToCeil(lineWidth, tabs) - lineWidth;
178
+ lineWidth += diff;
179
+ }
180
+ }
181
+ }
182
+ lineWidths[i] = Math.ceil(lineWidth);
183
+ maxLineWidth = Math.max(maxLineWidth, lineWidths[i]);
184
+ }
185
+ this.canvas.width = maxLineWidth * this._res;
186
+ // Calculate text height
187
+ const lineHeight = fontProperties.fontSize + this.style.strokeThickness + this.padding.y;
188
+ let height = lineHeight * drawnLines;
189
+ let lineSpacing = this._lineSpacing;
190
+ if (lineSpacing < 0 && Math.abs(lineSpacing) > lineHeight) {
191
+ lineSpacing = -lineHeight;
192
+ }
193
+ // Adjust for line spacing
194
+ if (lineSpacing !== 0) {
195
+ height += (lineSpacing > 0) ? lineSpacing * lines.length : lineSpacing * (lines.length - 1);
196
+ }
197
+ this.canvas.height = height * this._res;
198
+ this.context.scale(this._res, this._res);
199
+ if (navigator.isCocoonJS) {
200
+ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
201
+ }
202
+ if (this.style.backgroundColor) {
203
+ this.context.fillStyle = this.style.backgroundColor;
204
+ this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
205
+ }
206
+ this.context.fillStyle = this.style.fill;
207
+ this.context.font = this.style.font;
208
+ this.context.strokeStyle = this.style.stroke;
209
+ this.context.textBaseline = 'alphabetic';
210
+ this.context.lineWidth = this.style.strokeThickness;
211
+ this.context.lineCap = 'round';
212
+ this.context.lineJoin = 'round';
213
+ let linePositionX;
214
+ let linePositionY;
215
+ this._charCount = 0;
216
+ // Draw text line by line
217
+ for (let i = 0; i < drawnLines; i += 1) {
218
+ // Split the line by
219
+ linePositionX = this.style.strokeThickness / 2;
220
+ linePositionY = (this.style.strokeThickness / 2 + i * lineHeight) + fontProperties.ascent;
221
+ if (i > 0) {
222
+ linePositionY += (lineSpacing * i);
223
+ }
224
+ if (this.style.align === 'right') {
225
+ linePositionX += maxLineWidth - lineWidths[i];
226
+ } else if (this.style.align === 'center') {
227
+ linePositionX += (maxLineWidth - lineWidths[i]) / 2;
228
+ }
229
+ if (this.autoRound) {
230
+ linePositionX = Math.round(linePositionX);
231
+ linePositionY = Math.round(linePositionY);
232
+ }
233
+ if (this.colors.length > 0 || this.strokeColors.length > 0 || this.fontWeights.length > 0 || this.fontStyles.length > 0) {
234
+ this.updateLine(lines[i], linePositionX, linePositionY);
235
+ } else {
236
+ if (this.style.stroke && this.style.strokeThickness) {
237
+ this.updateShadow(this.style.shadowStroke);
238
+ if (tabs === 0) {
239
+ this.context.strokeText(lines[i], linePositionX, linePositionY);
240
+ } else {
241
+ this.renderTabLine(lines[i], linePositionX, linePositionY, false);
242
+ }
243
+ }
244
+ if (this.style.fill) {
245
+ this.updateShadow(this.style.shadowFill);
246
+ if (tabs === 0) {
247
+ this.context.fillText(lines[i], linePositionX, linePositionY);
248
+ } else {
249
+ this.renderTabLine(lines[i], linePositionX, linePositionY, true);
250
+ }
251
+ }
252
+ }
253
+ }
254
+ this.updateTexture();
255
+ this.dirty = false;
256
+ }
257
+
258
+ renderTabLine(line, x, y, fill) {
259
+ const text = line.split(/(?:\t)/);
260
+ const tabs = this.style.tabs;
261
+ let snap = 0;
262
+ if (Array.isArray(tabs)) {
263
+ let tab = 0;
264
+ for (let c = 0; c < text.length; c += 1) {
265
+ if (c > 0) {
266
+ tab += tabs[c - 1];
267
+ }
268
+ snap = x + tab;
269
+ if (fill) {
270
+ this.context.fillText(text[c], snap, y);
271
+ } else {
272
+ this.context.strokeText(text[c], snap, y);
273
+ }
274
+ }
275
+ } else {
276
+ for (let c = 0; c < text.length; c += 1) {
277
+ const section = Math.ceil(this.context.measureText(text[c]).width);
278
+ // How far to the next tab?
279
+ snap = snapToCeil(x, tabs);
280
+ if (fill) {
281
+ this.context.fillText(text[c], snap, y);
282
+ } else {
283
+ this.context.strokeText(text[c], snap, y);
284
+ }
285
+ x = snap + section;
286
+ }
287
+ }
288
+ }
289
+
290
+ updateShadow(state) {
291
+ if (state) {
292
+ this.context.shadowOffsetX = this.style.shadowOffsetX;
293
+ this.context.shadowOffsetY = this.style.shadowOffsetY;
294
+ this.context.shadowColor = this.style.shadowColor;
295
+ this.context.shadowBlur = this.style.shadowBlur;
296
+ } else {
297
+ this.context.shadowOffsetX = 0;
298
+ this.context.shadowOffsetY = 0;
299
+ this.context.shadowColor = 0;
300
+ this.context.shadowBlur = 0;
301
+ }
302
+ }
303
+
304
+ measureLine(line) {
305
+ let lineLength = 0;
306
+ for (let i = 0; i < line.length; i += 1) {
307
+ const letter = line[i];
308
+ if (this.fontWeights.length > 0 || this.fontStyles.length > 0) {
309
+ const components = this.fontToComponents(this.context.font);
310
+ if (this.fontStyles[this._charCount]) {
311
+ components.fontStyle = this.fontStyles[this._charCount];
312
+ }
313
+ if (this.fontWeights[this._charCount]) {
314
+ components.fontWeight = this.fontWeights[this._charCount];
315
+ }
316
+ this.context.font = this.componentsToFont(components);
317
+ }
318
+ if (this.style.stroke && this.style.strokeThickness) {
319
+ if (this.strokeColors[this._charCount]) {
320
+ this.context.strokeStyle = this.strokeColors[this._charCount];
321
+ }
322
+ this.updateShadow(this.style.shadowStroke);
323
+ }
324
+ if (this.style.fill) {
325
+ if (this.colors[this._charCount]) {
326
+ this.context.fillStyle = this.colors[this._charCount];
327
+ }
328
+ this.updateShadow(this.style.shadowFill);
329
+ }
330
+ lineLength += this.context.measureText(letter).width;
331
+ this._charCount += 1;
332
+ }
333
+ return Math.ceil(lineLength);
334
+ }
335
+
336
+ updateLine(line, x, y) {
337
+ for (let i = 0; i < line.length; i += 1) {
338
+ const letter = line[i];
339
+ if (this.fontWeights.length > 0 || this.fontStyles.length > 0) {
340
+ const components = this.fontToComponents(this.context.font);
341
+ if (this.fontStyles[this._charCount]) {
342
+ components.fontStyle = this.fontStyles[this._charCount];
343
+ }
344
+ if (this.fontWeights[this._charCount]) {
345
+ components.fontWeight = this.fontWeights[this._charCount];
346
+ }
347
+ this.context.font = this.componentsToFont(components);
348
+ }
349
+ if (this.style.stroke && this.style.strokeThickness) {
350
+ if (this.strokeColors[this._charCount]) {
351
+ this.context.strokeStyle = this.strokeColors[this._charCount];
352
+ }
353
+ this.updateShadow(this.style.shadowStroke);
354
+ this.context.strokeText(letter, x, y);
355
+ }
356
+ if (this.style.fill) {
357
+ if (this.colors[this._charCount]) {
358
+ this.context.fillStyle = this.colors[this._charCount];
359
+ }
360
+ this.updateShadow(this.style.shadowFill);
361
+ this.context.fillText(letter, x, y);
362
+ }
363
+ x += this.context.measureText(letter).width;
364
+ this._charCount += 1;
365
+ }
366
+ }
367
+
368
+ clearColors() {
369
+ this.colors = [];
370
+ this.strokeColors = [];
371
+ this.dirty = true;
372
+ return this;
373
+ }
374
+
375
+ clearFontValues() {
376
+ this.fontStyles = [];
377
+ this.fontWeights = [];
378
+ this.dirty = true;
379
+ return this;
380
+ }
381
+
382
+ addColor(color, position) {
383
+ this.colors[position] = color;
384
+ this.dirty = true;
385
+ return this;
386
+ }
387
+
388
+ addStrokeColor(color, position) {
389
+ this.strokeColors[position] = color;
390
+ this.dirty = true;
391
+ return this;
392
+ }
393
+
394
+ addFontStyle(style, position) {
395
+ this.fontStyles[position] = style;
396
+ this.dirty = true;
397
+ return this;
398
+ }
399
+
400
+ addFontWeight(weight, position) {
401
+ this.fontWeights[position] = weight;
402
+ this.dirty = true;
403
+ return this;
404
+ }
405
+
406
+ precalculateWordWrap(text) {
407
+ this.texture.baseTexture.resolution = this._res;
408
+ this.context.font = this.style.font;
409
+ const wrappedLines = this.runWordWrap(text);
410
+ return wrappedLines.split(/(?:\r\n|\r|\n)/);
411
+ }
412
+
413
+ runWordWrap(text) {
414
+ if (this.useAdvancedWrap) {
415
+ return this.advancedWordWrap(text);
416
+ }
417
+ return this.basicWordWrap(text);
418
+ }
419
+
420
+ advancedWordWrap(text) {
421
+ const context = this.context;
422
+ const wordWrapWidth = this.style.wordWrapWidth;
423
+ let output = '';
424
+ // (1) condense whitespace
425
+ // (2) split into lines
426
+ const lines = text.replace(/ +/gi, ' ').split(/\r?\n/gi);
427
+ let linesCount = lines.length;
428
+ for (let i = 0; i < linesCount; i += 1) {
429
+ let line = lines[i];
430
+ let out = '';
431
+ // trim whitespace
432
+ line = line.replace(/^ *|\s*$/gi, '');
433
+ // if entire line is less than wordWrapWidth
434
+ // append the entire line and exit early
435
+ const lineWidth = context.measureText(line).width;
436
+ if (lineWidth < wordWrapWidth) {
437
+ output += line + '\n';
438
+ } else {
439
+ // otherwise, calculate new lines
440
+ let currentLineWidth = wordWrapWidth;
441
+ // split into words
442
+ const words = line.split(' ');
443
+ for (let j = 0; j < words.length; j += 1) {
444
+ const word = words[j];
445
+ const wordWithSpace = word + ' ';
446
+ let wordWidth = context.measureText(wordWithSpace).width;
447
+ if (wordWidth > currentLineWidth) {
448
+ // break word
449
+ if (j === 0) {
450
+ // shave off letters from word until it's small enough
451
+ let newWord = wordWithSpace;
452
+ while (newWord.length) {
453
+ newWord = newWord.slice(0, -1);
454
+ wordWidth = context.measureText(newWord).width;
455
+ if (wordWidth <= currentLineWidth) {
456
+ break;
457
+ }
458
+ }
459
+ // if wordWrapWidth is too small for even a single
460
+ // letter, shame user failure with a fatal error
461
+ if (!newWord.length) {
462
+ throw new Error('This text\'s wordWrapWidth setting is less than a single character!');
463
+ }
464
+ // replace current word in array with remainder
465
+ const secondPart = word.substr(newWord.length);
466
+ words[j] = secondPart;
467
+ // append first piece to output
468
+ out += newWord;
469
+ }
470
+ // if existing word length is 0, don't include it
471
+ const offset = (words[j].length) ? j : j + 1;
472
+ // collapse rest of sentence
473
+ // remove any trailing white space
474
+ const remainder = words.slice(offset).join(' ').replace(/[ \n]*$/gi, '');
475
+ // prepend remainder to next line
476
+ lines[i + 1] = remainder + ' ' + (lines[i + 1] || '');
477
+ linesCount = lines.length;
478
+ break; // processing on this line
479
+ // append word with space to output
480
+ } else {
481
+ out += wordWithSpace;
482
+ currentLineWidth -= wordWidth;
483
+ }
484
+ }
485
+ // append processed line to output
486
+ output += out.replace(/[ \n]*$/gi, '') + '\n';
487
+ }
488
+ }
489
+ // trim the end of the string
490
+ output = output.replace(/[\s|\n]*$/gi, '');
491
+ return output;
492
+ }
493
+
494
+ basicWordWrap(text) {
495
+ let result = '';
496
+ const lines = text.split('\n');
497
+ for (let i = 0; i < lines.length; i += 1) {
498
+ let spaceLeft = this.style.wordWrapWidth;
499
+ const words = lines[i].split(' ');
500
+ for (let j = 0; j < words.length; j += 1) {
501
+ const wordWidth = this.context.measureText(words[j]).width;
502
+ const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
503
+ if (wordWidthWithSpace > spaceLeft) {
504
+ // Skip printing the newline if it's the first word of the line that is greater than the word wrap width.
505
+ if (j > 0) {
506
+ result += '\n';
507
+ }
508
+ result += words[j] + ' ';
509
+ spaceLeft = this.style.wordWrapWidth - wordWidth;
510
+ } else {
511
+ spaceLeft -= wordWidthWithSpace;
512
+ result += words[j] + ' ';
513
+ }
514
+ }
515
+ if (i < lines.length - 1) {
516
+ result += '\n';
517
+ }
518
+ }
519
+ return result;
520
+ }
521
+
522
+ updateFont(components) {
523
+ const font = this.componentsToFont(components);
524
+ if (this.style.font !== font) {
525
+ this.style.font = font;
526
+ this.dirty = true;
527
+ if (this.parent) {
528
+ this.updateTransform();
529
+ }
530
+ }
531
+ }
532
+
533
+ fontToComponents(font) {
534
+ // The format is specified in http://www.w3.org/TR/CSS2/fonts.html#font-shorthand:
535
+ // style - normal | italic | oblique | inherit
536
+ // variant - normal | small-caps | inherit
537
+ // weight - normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | inherit
538
+ // size - xx-small | x-small | small | medium | large | x-large | xx-large,
539
+ // larger | smaller
540
+ // {number} (em | ex | ch | rem | vh | vw | vmin | vmax | px | mm | cm | in | pt | pc | %)
541
+ // font-family - rest (but identifiers or quoted with comma separation)
542
+ const m = font.match(/^\s*(?:\b(normal|italic|oblique|inherit)?\b)\s*(?:\b(normal|small-caps|inherit)?\b)\s*(?:\b(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900|inherit)?\b)\s*(?:\b(xx-small|x-small|small|medium|large|x-large|xx-large|larger|smaller|0|\d*(?:[.]\d*)?(?:%|[a-z]{2,5}))?\b)\s*(.*)\s*$/);
543
+ if (m) {
544
+ let family = m[5].trim();
545
+ // If it looks like the value should be quoted, but isn't, then quote it.
546
+ if (!/^(?:inherit|serif|sans-serif|cursive|fantasy|monospace)$/.exec(family) && !/['",]/.exec(family)) {
547
+ family = "'" + family + "'";
548
+ }
549
+ return {
550
+ font,
551
+ fontStyle: m[1] || 'normal',
552
+ fontVariant: m[2] || 'normal',
553
+ fontWeight: m[3] || 'normal',
554
+ fontSize: m[4] || 'medium',
555
+ fontFamily: family,
556
+ };
557
+ }
558
+ console.warn('[Text] Error parsing CSS font: ' + font);
559
+ return { font };
560
+ }
561
+
562
+ componentsToFont(components) {
563
+ const parts = [];
564
+ let v;
565
+ v = components.fontStyle;
566
+ if (v && v !== 'normal') { parts.push(v); }
567
+ v = components.fontVariant;
568
+ if (v && v !== 'normal') { parts.push(v); }
569
+ v = components.fontWeight;
570
+ if (v && v !== 'normal') { parts.push(v); }
571
+ v = components.fontSize;
572
+ if (v && v !== 'medium') { parts.push(v); }
573
+ v = components.fontFamily;
574
+ if (v) { parts.push(v); }
575
+ if (!parts.length) {
576
+ // Fallback to whatever value the 'font' was
577
+ parts.push(components.font);
578
+ }
579
+ return parts.join(' ');
580
+ }
581
+
582
+ setText(text, immediate = false) {
583
+ this.text = text.toString() || '';
584
+ if (immediate) {
585
+ this.updateText();
586
+ } else {
587
+ this.dirty = true;
588
+ }
589
+ return this;
590
+ }
591
+
592
+ parseList(list) {
593
+ if (!Array.isArray(list)) {
594
+ return this;
595
+ }
596
+ let s = '';
597
+ for (let i = 0; i < list.length; i += 1) {
598
+ if (Array.isArray(list[i])) {
599
+ s += list[i].join('\t');
600
+ if (i < list.length - 1) {
601
+ s += '\n';
602
+ }
603
+ } else {
604
+ s += list[i];
605
+ if (i < list.length - 1) {
606
+ s += '\t';
607
+ }
608
+ }
609
+ }
610
+ this.text = s;
611
+ this.dirty = true;
612
+ return this;
613
+ }
614
+
615
+ setTextBounds(x, y, width, height) {
616
+ if (x === undefined) {
617
+ this.textBounds = null;
618
+ } else {
619
+ if (!this.textBounds) {
620
+ this.textBounds = new Rectangle(x, y, width, height);
621
+ } else {
622
+ this.textBounds.setTo(x, y, width, height);
623
+ }
624
+ if (this.style.wordWrapWidth > width) {
625
+ this.style.wordWrapWidth = width;
626
+ }
627
+ }
628
+ this.updateTexture();
629
+ return this;
630
+ }
631
+
632
+ updateTexture() {
633
+ const base = this.texture.baseTexture;
634
+ const crop = this.texture.crop;
635
+ const frame = this.texture.frame;
636
+ const w = this.canvas.width;
637
+ const h = this.canvas.height;
638
+ base.width = w;
639
+ base.height = h;
640
+ crop.width = w;
641
+ crop.height = h;
642
+ frame.width = w;
643
+ frame.height = h;
644
+ this.texture.width = w;
645
+ this.texture.height = h;
646
+ this._width = w;
647
+ this._height = h;
648
+ if (this.textBounds) {
649
+ let x = this.textBounds.x;
650
+ let y = this.textBounds.y;
651
+ // Align the canvas based on the bounds
652
+ if (this.style.boundsAlignH === 'right') {
653
+ x += this.textBounds.width - this.canvas.width / this.resolution;
654
+ } else if (this.style.boundsAlignH === 'center') {
655
+ x += this.textBounds.halfWidth - (this.canvas.width / this.resolution / 2);
656
+ }
657
+ if (this.style.boundsAlignV === 'bottom') {
658
+ y += this.textBounds.height - this.canvas.height / this.resolution;
659
+ } else if (this.style.boundsAlignV === 'middle') {
660
+ y += this.textBounds.halfHeight - (this.canvas.height / this.resolution / 2);
661
+ }
662
+ this.pivot.x = -x;
663
+ this.pivot.y = -y;
664
+ }
665
+ // Can't render something with a zero sized dimension
666
+ this.renderable = (w !== 0 && h !== 0);
667
+ this.texture.requiresReTint = true;
668
+ this.texture.baseTexture.dirty();
669
+ }
670
+
671
+ renderWebGL(renderSession) {
672
+ if (this.dirty) {
673
+ this.updateText();
674
+ this.dirty = false;
675
+ }
676
+ renderWebGL(this, renderSession);
677
+ }
678
+
679
+ renderCanvas(renderSession) {
680
+ if (this.dirty) {
681
+ this.updateText();
682
+ this.dirty = false;
683
+ }
684
+ renderCanvas(this, renderSession);
685
+ }
686
+
687
+ getFontPropertiesCache() {
688
+ if (!window.PhaserRegistry.fontPropertiesCache) {
689
+ window.PhaserRegistry.fontPropertiesCache = {};
690
+ }
691
+ return window.PhaserRegistry.fontPropertiesCache;
692
+ }
693
+
694
+ getFontPropertiesCanvas() {
695
+ if (!window.PhaserRegistry.fontPropertiesCanvas) {
696
+ window.PhaserRegistry.fontPropertiesCanvas = document.createElement('canvas');
697
+ }
698
+ return window.PhaserRegistry.fontPropertiesCanvas;
699
+ }
700
+
701
+ getFontPropertiesContext() {
702
+ if (!window.PhaserRegistry.fontPropertiesContext) {
703
+ window.PhaserRegistry.fontPropertiesContext = this.getFontPropertiesCanvas().getContext('2d');
704
+ }
705
+ return window.PhaserRegistry.fontPropertiesContext;
706
+ }
707
+
708
+ determineFontProperties(fontStyle) {
709
+ const fontPropertiesCache = this.getFontPropertiesCache();
710
+ let properties = fontPropertiesCache[fontStyle];
711
+ if (!properties) {
712
+ properties = {};
713
+ const canvas = this.getFontPropertiesCanvas();
714
+ const context = this.getFontPropertiesContext();
715
+ context.font = fontStyle;
716
+ const width = Math.ceil(context.measureText('|MÉq').width);
717
+ let baseline = Math.ceil(context.measureText('|MÉq').width);
718
+ const height = 2 * baseline;
719
+ baseline = baseline * 1.4 | 0;
720
+ canvas.width = width;
721
+ canvas.height = height;
722
+ context.fillStyle = '#f00';
723
+ context.fillRect(0, 0, width, height);
724
+ context.font = fontStyle;
725
+ context.textBaseline = 'alphabetic';
726
+ context.fillStyle = '#000';
727
+ context.fillText('|MÉq', 0, baseline);
728
+ if (!context.getImageData(0, 0, width, height)) {
729
+ properties.ascent = baseline;
730
+ properties.descent = baseline + 6;
731
+ properties.fontSize = properties.ascent + properties.descent;
732
+ fontPropertiesCache[fontStyle] = properties;
733
+ return properties;
734
+ }
735
+ const imagedata = context.getImageData(0, 0, width, height).data;
736
+ const pixels = imagedata.length;
737
+ const line = width * 4;
738
+ let i;
739
+ let j;
740
+ let idx = 0;
741
+ let stop = false;
742
+ // ascent. scan from top to bottom until we find a non red pixel
743
+ for (i = 0; i < baseline; i += 1) {
744
+ for (j = 0; j < line; j += 4) {
745
+ if (imagedata[idx + j] !== 255) {
746
+ stop = true;
747
+ break;
748
+ }
749
+ }
750
+ if (!stop) {
751
+ idx += line;
752
+ } else {
753
+ break;
754
+ }
755
+ }
756
+ properties.ascent = baseline - i;
757
+ idx = pixels - line;
758
+ stop = false;
759
+ // descent. scan from bottom to top until we find a non red pixel
760
+ for (i = height; i > baseline; i -= 1) {
761
+ for (j = 0; j < line; j += 4) {
762
+ if (imagedata[idx + j] !== 255) {
763
+ stop = true;
764
+ break;
765
+ }
766
+ }
767
+ if (!stop) {
768
+ idx -= line;
769
+ } else {
770
+ break;
771
+ }
772
+ }
773
+ properties.descent = i - baseline;
774
+ // TODO might need a tweak. kind of a temp fix!
775
+ properties.descent += 6;
776
+ properties.fontSize = properties.ascent + properties.descent;
777
+ fontPropertiesCache[fontStyle] = properties;
778
+ }
779
+ return properties;
780
+ }
781
+
782
+ getBounds(matrix = null) {
783
+ if (this.dirty) {
784
+ this.updateText();
785
+ this.dirty = false;
786
+ }
787
+ return getBounds(this, matrix);
788
+ }
789
+
790
+ get text() {
791
+ return this._text;
792
+ }
793
+
794
+ set text(value) {
795
+ if (value !== this._text) {
796
+ this._text = value.toString() || '';
797
+ this.dirty = true;
798
+ if (this.parent) {
799
+ this.updateTransform();
800
+ }
801
+ }
802
+ }
803
+
804
+ get cssFont() {
805
+ return this.componentsToFont(this._fontComponents);
806
+ }
807
+
808
+ set cssFont(value) {
809
+ this._fontComponents = this.fontToComponents(value || 'bold 20pt Arial');
810
+ this.updateFont(this._fontComponents);
811
+ }
812
+
813
+ get font() {
814
+ return this._fontComponents.fontFamily;
815
+ }
816
+
817
+ set font(value) {
818
+ let mutatedValue = value || 'Arial';
819
+ mutatedValue = mutatedValue.trim();
820
+ // If it looks like the value should be quoted, but isn't, then quote it.
821
+ if (!/^(?:inherit|serif|sans-serif|cursive|fantasy|monospace)$/.exec(mutatedValue) && !/['",]/.exec(mutatedValue)) {
822
+ mutatedValue = "'" + mutatedValue + "'";
823
+ }
824
+
825
+ this._fontComponents.fontFamily = mutatedValue;
826
+ this.updateFont(this._fontComponents);
827
+ }
828
+
829
+ get fontSize() {
830
+ const size = this._fontComponents.fontSize;
831
+ if (size && /(?:^0$|px$)/.exec(size)) {
832
+ return parseInt(size, 10);
833
+ }
834
+ return size;
835
+ }
836
+
837
+ set fontSize(value) {
838
+ let mutatedValue = value || '0';
839
+ if (typeof mutatedValue === 'number') {
840
+ mutatedValue += 'px';
841
+ }
842
+ this._fontComponents.fontSize = mutatedValue;
843
+ this.updateFont(this._fontComponents);
844
+ }
845
+
846
+ get fontWeight() {
847
+ return this._fontComponents.fontWeight || 'normal';
848
+ }
849
+
850
+ set fontWeight(value) {
851
+ this._fontComponents.fontWeight = value || 'normal';
852
+ this.updateFont(this._fontComponents);
853
+ }
854
+
855
+ get fontStyle() {
856
+ return this._fontComponents.fontStyle || 'normal';
857
+ }
858
+
859
+ set fontStyle(value) {
860
+ this._fontComponents.fontStyle = value || 'normal';
861
+ this.updateFont(this._fontComponents);
862
+ }
863
+
864
+ get fontVariant() {
865
+ return this._fontComponents.fontVariant || 'normal';
866
+ }
867
+
868
+ set fontVariant(value) {
869
+ this._fontComponents.fontVariant = value || 'normal';
870
+ this.updateFont(this._fontComponents);
871
+ }
872
+
873
+ get fill() {
874
+ return this.style.fill;
875
+ }
876
+
877
+ set fill(value) {
878
+ if (value !== this.style.fill) {
879
+ this.style.fill = value;
880
+ this.dirty = true;
881
+ }
882
+ }
883
+
884
+ get align() {
885
+ return this.style.align;
886
+ }
887
+
888
+ set align(value) {
889
+ if (value !== this.style.align) {
890
+ this.style.align = value;
891
+ this.dirty = true;
892
+ }
893
+ }
894
+
895
+ get resolution() {
896
+ return this._res;
897
+ }
898
+
899
+ set resolution(value) {
900
+ if (value !== this._res) {
901
+ this._res = value;
902
+ this.dirty = true;
903
+ }
904
+ }
905
+
906
+ get tabs() {
907
+ return this.style.tabs;
908
+ }
909
+
910
+ set tabs(value) {
911
+ if (value !== this.style.tabs) {
912
+ this.style.tabs = value;
913
+ this.dirty = true;
914
+ }
915
+ }
916
+
917
+ get boundsAlignH() {
918
+ return this.style.boundsAlignH;
919
+ }
920
+
921
+ set boundsAlignH(value) {
922
+ if (value !== this.style.boundsAlignH) {
923
+ this.style.boundsAlignH = value;
924
+ this.dirty = true;
925
+ }
926
+ }
927
+
928
+ get boundsAlignV() {
929
+ return this.style.boundsAlignV;
930
+ }
931
+
932
+ set boundsAlignV(value) {
933
+ if (value !== this.style.boundsAlignV) {
934
+ this.style.boundsAlignV = value;
935
+ this.dirty = true;
936
+ }
937
+ }
938
+
939
+ get stroke() {
940
+ return this.style.stroke;
941
+ }
942
+
943
+ set stroke(value) {
944
+ if (value !== this.style.stroke) {
945
+ this.style.stroke = value;
946
+ this.dirty = true;
947
+ }
948
+ }
949
+
950
+ get strokeThickness() {
951
+ return this.style.strokeThickness;
952
+ }
953
+
954
+ set strokeThickness(value) {
955
+ if (value !== this.style.strokeThickness) {
956
+ this.style.strokeThickness = value;
957
+ this.dirty = true;
958
+ }
959
+ }
960
+
961
+ get wordWrap() {
962
+ return this.style.wordWrap;
963
+ }
964
+
965
+ set wordWrap(value) {
966
+ if (value !== this.style.wordWrap) {
967
+ this.style.wordWrap = value;
968
+ this.dirty = true;
969
+ }
970
+ }
971
+
972
+ get wordWrapWidth() {
973
+ return this.style.wordWrapWidth;
974
+ }
975
+
976
+ set wordWrapWidth(value) {
977
+ if (value !== this.style.wordWrapWidth) {
978
+ this.style.wordWrapWidth = value;
979
+ this.dirty = true;
980
+ }
981
+ }
982
+
983
+ get lineSpacing() {
984
+ return this._lineSpacing;
985
+ }
986
+
987
+ set lineSpacing(value) {
988
+ if (value !== this._lineSpacing) {
989
+ this._lineSpacing = parseFloat(value);
990
+ this.dirty = true;
991
+ if (this.parent) {
992
+ this.updateTransform();
993
+ }
994
+ }
995
+ }
996
+
997
+ get shadowOffsetX() {
998
+ return this.style.shadowOffsetX;
999
+ }
1000
+
1001
+ set shadowOffsetX(value) {
1002
+ if (value !== this.style.shadowOffsetX) {
1003
+ this.style.shadowOffsetX = value;
1004
+ this.dirty = true;
1005
+ }
1006
+ }
1007
+
1008
+ get shadowOffsetY() {
1009
+ return this.style.shadowOffsetY;
1010
+ }
1011
+
1012
+ set shadowOffsetY(value) {
1013
+ if (value !== this.style.shadowOffsetY) {
1014
+ this.style.shadowOffsetY = value;
1015
+ this.dirty = true;
1016
+ }
1017
+ }
1018
+
1019
+ get shadowColor() {
1020
+ return this.style.shadowColor;
1021
+ }
1022
+
1023
+ set shadowColor(value) {
1024
+ if (value !== this.style.shadowColor) {
1025
+ this.style.shadowColor = value;
1026
+ this.dirty = true;
1027
+ }
1028
+ }
1029
+
1030
+ get shadowBlur() {
1031
+ return this.style.shadowBlur;
1032
+ }
1033
+
1034
+ set shadowBlur(value) {
1035
+ if (value !== this.style.shadowBlur) {
1036
+ this.style.shadowBlur = value;
1037
+ this.dirty = true;
1038
+ }
1039
+ }
1040
+
1041
+ get shadowStroke() {
1042
+ return this.style.shadowStroke;
1043
+ }
1044
+
1045
+ set shadowStroke(value) {
1046
+ if (value !== this.style.shadowStroke) {
1047
+ this.style.shadowStroke = value;
1048
+ this.dirty = true;
1049
+ }
1050
+ }
1051
+
1052
+ get shadowFill() {
1053
+ return this.style.shadowFill;
1054
+ }
1055
+
1056
+ set shadowFill(value) {
1057
+ if (value !== this.style.shadowFill) {
1058
+ this.style.shadowFill = value;
1059
+ this.dirty = true;
1060
+ }
1061
+ }
1062
+
1063
+ get width() {
1064
+ if (this.dirty) {
1065
+ this.updateText();
1066
+ this.dirty = false;
1067
+ }
1068
+ return this.scale.x * this.texture.frame.width;
1069
+ }
1070
+
1071
+ set width(value) {
1072
+ this.scale.x = value / this.texture.frame.width;
1073
+ this._width = value;
1074
+ }
1075
+
1076
+ get height() {
1077
+ if (this.dirty) {
1078
+ this.updateText();
1079
+ this.dirty = false;
1080
+ }
1081
+ return this.scale.y * this.texture.frame.height;
1082
+ }
1083
+
1084
+ set height(value) {
1085
+ this.scale.y = value / this.texture.frame.height;
1086
+ this._height = value;
1087
+ }
1088
+
1089
+ }