@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,946 @@
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 Signal from './signal';
7
+ import Rectangle from '../geom/rectangle';
8
+ import { canPlayAudio } from './device_util';
9
+ import { TEXTURE_ATLAS_JSON_ARRAY, TEXTURE_ATLAS_JSON_HASH, TEXTURE_ATLAS_XML_STARLING, TEXTURE_ATLAS_JSON_PYXEL } from './const';
10
+
11
+ export const TILED_JSON = 6;
12
+ export const TILEMAP_CSV = 7;
13
+
14
+ export default class {
15
+
16
+ constructor(game) {
17
+ this.game = game;
18
+ this.cache = game.cache;
19
+ this.resetLocked = false;
20
+ this.isLoading = false;
21
+ this.hasLoaded = false;
22
+ this.preloadSprite = null;
23
+ this.crossOrigin = false;
24
+ this.baseURL = '';
25
+ this.path = '';
26
+ this.headers = {
27
+ requestedWith: false,
28
+ json: 'application/json',
29
+ xml: 'application/xml',
30
+ };
31
+ this.onLoadStart = new Signal();
32
+ this.onLoadComplete = new Signal();
33
+ this.onPackComplete = new Signal();
34
+ this.onFileStart = new Signal();
35
+ this.onFileComplete = new Signal();
36
+ this.onFileError = new Signal();
37
+ this.useXDomainRequest = false;
38
+ this._warnedAboutXDomainRequest = false;
39
+ this.enableParallel = true;
40
+ this.maxParallelDownloads = 6;
41
+ this._withSyncPointDepth = 0;
42
+ this._fileList = [];
43
+ this._flightQueue = [];
44
+ this._processingHead = 0;
45
+ this._fileLoadStarted = false;
46
+ this._totalPackCount = 0;
47
+ this._totalFileCount = 0;
48
+ this._loadedPackCount = 0;
49
+ this._loadedFileCount = 0;
50
+ }
51
+
52
+ setPreloadSprite(sprite, direction = 0) {
53
+ this.preloadSprite = {
54
+ sprite,
55
+ direction,
56
+ width: sprite.width,
57
+ height: sprite.height,
58
+ rect: null,
59
+ };
60
+ if (direction === 0) {
61
+ // Horizontal rect
62
+ this.preloadSprite.rect = new Rectangle(0, 0, 1, sprite.height);
63
+ } else {
64
+ // Vertical rect
65
+ this.preloadSprite.rect = new Rectangle(0, 0, sprite.width, 1);
66
+ }
67
+ sprite.crop(this.preloadSprite.rect);
68
+ sprite.visible = true;
69
+ }
70
+
71
+ resize() {
72
+ if (this.preloadSprite && this.preloadSprite.height !== this.preloadSprite.sprite.height) {
73
+ this.preloadSprite.rect.height = this.preloadSprite.sprite.height;
74
+ }
75
+ }
76
+
77
+ checkKeyExists(type, key) {
78
+ return this.getAssetIndex(type, key) > -1;
79
+ }
80
+
81
+ getAssetIndex(type, key) {
82
+ let bestFound = -1;
83
+ for (let i = 0; i < this._fileList.length; i += 1) {
84
+ const file = this._fileList[i];
85
+ if (file.type === type && file.key === key) {
86
+ bestFound = i;
87
+ // An already loaded/loading file may be superceded.
88
+ if (!file.loaded && !file.loading) {
89
+ break;
90
+ }
91
+ }
92
+ }
93
+
94
+ return bestFound;
95
+ }
96
+
97
+ getAsset(type, key) {
98
+ const fileIndex = this.getAssetIndex(type, key);
99
+ if (fileIndex > -1) {
100
+ return { index: fileIndex, file: this._fileList[fileIndex] };
101
+ }
102
+ return null;
103
+ }
104
+
105
+ reset(hard, clearEvents = false) {
106
+ if (this.resetLocked) {
107
+ return;
108
+ }
109
+ if (hard) {
110
+ this.preloadSprite = null;
111
+ }
112
+ this.isLoading = false;
113
+ this._processingHead = 0;
114
+ this._fileList.length = 0;
115
+ this._flightQueue.length = 0;
116
+ this._fileLoadStarted = false;
117
+ this._totalFileCount = 0;
118
+ this._totalPackCount = 0;
119
+ this._loadedPackCount = 0;
120
+ this._loadedFileCount = 0;
121
+ if (clearEvents) {
122
+ this.onLoadStart.removeAll();
123
+ this.onLoadComplete.removeAll();
124
+ this.onPackComplete.removeAll();
125
+ this.onFileStart.removeAll();
126
+ this.onFileComplete.removeAll();
127
+ this.onFileError.removeAll();
128
+ }
129
+ }
130
+
131
+ addToFileList(type, key = '', url = null, properties = null, overwrite = false, extension = null) {
132
+ if (key === undefined || key === '') {
133
+ console.warn('Loader: Invalid or no key given of type ' + type);
134
+ return this;
135
+ }
136
+ if (url === undefined || url === null) {
137
+ if (extension) {
138
+ url = key + extension;
139
+ } else {
140
+ console.warn('Loader: No URL given for file type: ' + type + ' key: ' + key);
141
+ return this;
142
+ }
143
+ }
144
+ const file = {
145
+ type,
146
+ key,
147
+ path: this.path,
148
+ url,
149
+ syncPoint: this._withSyncPointDepth > 0,
150
+ data: null,
151
+ loading: false,
152
+ loaded: false,
153
+ error: false,
154
+ };
155
+ if (properties) {
156
+ const keys = Object.keys(properties);
157
+ for (let i = 0; i < keys.length; i += 1) {
158
+ const prop = keys[i];
159
+ file[prop] = properties[prop];
160
+ }
161
+ }
162
+ const fileIndex = this.getAssetIndex(type, key);
163
+ if (overwrite && fileIndex > -1) {
164
+ const currentFile = this._fileList[fileIndex];
165
+ if (!currentFile.loading && !currentFile.loaded) {
166
+ this._fileList[fileIndex] = file;
167
+ } else {
168
+ this._fileList.push(file);
169
+ this._totalFileCount += 1;
170
+ }
171
+ } else if (fileIndex === -1) {
172
+ this._fileList.push(file);
173
+ this._totalFileCount += 1;
174
+ }
175
+ return this;
176
+ }
177
+
178
+ replaceInFileList(type, key, url, properties) {
179
+ return this.addToFileList(type, key, url, properties, true);
180
+ }
181
+
182
+ pack() {
183
+ // TODO
184
+ console.warn('loader.pack() is not implemented');
185
+ }
186
+
187
+ image(key, url, overwrite) {
188
+ return this.addToFileList('image', key, url, undefined, overwrite, '.png');
189
+ }
190
+
191
+ images() {
192
+ // TODO
193
+ console.warn('loader.images() is not implemented');
194
+ }
195
+
196
+ text(key, url, overwrite) {
197
+ return this.addToFileList('text', key, url, undefined, overwrite, '.txt');
198
+ }
199
+
200
+ json(key, url, overwrite) {
201
+ return this.addToFileList('json', key, url, undefined, overwrite, '.json');
202
+ }
203
+
204
+ shader(key, url, overwrite) {
205
+ return this.addToFileList('shader', key, url, undefined, overwrite, '.frag');
206
+ }
207
+
208
+ xml(key, url, overwrite) {
209
+ return this.addToFileList('xml', key, url, undefined, overwrite, '.xml');
210
+ }
211
+
212
+ script(key, url, callback = false, callbackContext = this) {
213
+ return this.addToFileList('script', key, url, { syncPoint: true, callback, callbackContext }, false, '.js');
214
+ }
215
+
216
+ binary(key, url, callback = false, callbackContext = this) {
217
+ return this.addToFileList('binary', key, url, { callback, callbackContext }, false, '.bin');
218
+ }
219
+
220
+ spritesheet(key, url, frameWidth, frameHeight, frameMax = -1, margin = 0, spacing = 0) {
221
+ return this.addToFileList('spritesheet', key, url, { frameWidth, frameHeight, frameMax, margin, spacing }, false, '.png');
222
+ }
223
+
224
+ audio(key, urls, autoDecode = true) {
225
+ if (this.game.sound.noAudio) {
226
+ return this;
227
+ }
228
+ if (typeof urls === 'string') {
229
+ urls = [urls];
230
+ }
231
+ return this.addToFileList('audio', key, urls, { buffer: null, autoDecode });
232
+ }
233
+
234
+ audioSprite() {
235
+ // TODO
236
+ console.warn('loader.audioSprite() is not implemented');
237
+ }
238
+
239
+ video() {
240
+ // TODO
241
+ console.warn('loader.video() is not implemented');
242
+ }
243
+
244
+ tilemap() {
245
+ // TODO
246
+ console.warn('loader.tilemap() is not implemented');
247
+ }
248
+
249
+ bitmapFont(key, textureURL = null, atlasURL = null, atlasData = null, xSpacing = 0, ySpacing = 0) {
250
+ if (textureURL === undefined || textureURL === null) {
251
+ textureURL = key + '.png';
252
+ }
253
+ if (atlasURL === null && atlasData === null) {
254
+ atlasURL = key + '.xml';
255
+ }
256
+ // A URL to a json/xml atlas has been given
257
+ if (atlasURL) {
258
+ this.addToFileList('bitmapfont', key, textureURL, { atlasURL, xSpacing, ySpacing });
259
+ } else if (typeof atlasData === 'string') {
260
+ // A stringified xml/json atlas has been given
261
+ let json = null;
262
+ let xml = null;
263
+ try {
264
+ json = JSON.parse(atlasData);
265
+ } catch (e) {
266
+ xml = this.parseXml(atlasData);
267
+ }
268
+ if (!xml && !json) {
269
+ throw new Error('Loader. Invalid Bitmap Font atlas given');
270
+ }
271
+ this.addToFileList('bitmapfont', key, textureURL, { atlasURL: null, atlasData: json || xml, atlasType: (json ? 'json' : 'xml'), xSpacing, ySpacing });
272
+ }
273
+ return this;
274
+ }
275
+
276
+ atlasJSONArray(key, textureURL, atlasURL, atlasData) {
277
+ return this.atlas(key, textureURL, atlasURL, atlasData, TEXTURE_ATLAS_JSON_ARRAY);
278
+ }
279
+
280
+ atlasJSONHash(key, textureURL, atlasURL, atlasData) {
281
+ return this.atlas(key, textureURL, atlasURL, atlasData, TEXTURE_ATLAS_JSON_HASH);
282
+ }
283
+
284
+ atlasXML() {
285
+ // TODO
286
+ console.warn('loader.atlasXML() is not implemented');
287
+ }
288
+
289
+ atlas(key, textureURL, atlasURL = null, atlasData = null, format = TEXTURE_ATLAS_JSON_ARRAY) {
290
+ if (textureURL === undefined || textureURL === null) {
291
+ textureURL = key + '.png';
292
+ }
293
+ if (!atlasURL && !atlasData) {
294
+ if (format === TEXTURE_ATLAS_XML_STARLING) {
295
+ atlasURL = key + '.xml';
296
+ } else {
297
+ atlasURL = key + '.json';
298
+ }
299
+ }
300
+ // A URL to a json/xml file has been given
301
+ if (atlasURL) {
302
+ this.addToFileList('textureatlas', key, textureURL, { atlasURL, format });
303
+ } else {
304
+ switch (format) {
305
+ // A json string or object has been given
306
+ case TEXTURE_ATLAS_JSON_ARRAY:
307
+ if (typeof atlasData === 'string') {
308
+ atlasData = JSON.parse(atlasData);
309
+ }
310
+ break;
311
+ // An xml string or object has been given
312
+ case TEXTURE_ATLAS_XML_STARLING:
313
+ if (typeof atlasData === 'string') {
314
+ const xml = this.parseXml(atlasData);
315
+ if (!xml) {
316
+ throw new Error('Invalid Texture Atlas XML given');
317
+ }
318
+ atlasData = xml;
319
+ }
320
+ break;
321
+ default:
322
+ // pass
323
+ break;
324
+ }
325
+ this.addToFileList('textureatlas', key, textureURL, { atlasURL: null, atlasData, format });
326
+ }
327
+ return this;
328
+ }
329
+
330
+ withSyncPoint(callback, callbackContext) {
331
+ this._withSyncPointDepth += 1;
332
+ try {
333
+ callback.call(callbackContext || this, this);
334
+ } finally {
335
+ this._withSyncPointDepth -= 1;
336
+ }
337
+ return this;
338
+ }
339
+
340
+ addSyncPoint(type, key) {
341
+ const asset = this.getAsset(type, key);
342
+ if (asset) {
343
+ asset.file.syncPoint = true;
344
+ }
345
+ return this;
346
+ }
347
+
348
+ removeFile(type, key) {
349
+ const asset = this.getAsset(type, key);
350
+ if (asset) {
351
+ if (!asset.loaded && !asset.loading) {
352
+ this._fileList.splice(asset.index, 1);
353
+ }
354
+ }
355
+ }
356
+
357
+ removeAll() {
358
+ this._fileList.length = 0;
359
+ this._flightQueue.length = 0;
360
+ }
361
+
362
+ start() {
363
+ if (this.isLoading) {
364
+ return;
365
+ }
366
+ this.hasLoaded = false;
367
+ this.isLoading = true;
368
+ this.updateProgress();
369
+ this.processLoadQueue();
370
+ }
371
+
372
+ processLoadQueue() {
373
+ if (!this.isLoading) {
374
+ console.warn('Loader - active loading canceled / reset');
375
+ this.finishedLoading(true);
376
+ return;
377
+ }
378
+ // Empty the flight queue as applicable
379
+ for (let i = 0; i < this._flightQueue.length; i += 1) {
380
+ const file = this._flightQueue[i];
381
+ if (file.loaded || file.error) {
382
+ this._flightQueue.splice(i, 1);
383
+ i -= 1;
384
+ file.loading = false;
385
+ file.requestUrl = null;
386
+ file.requestObject = null;
387
+ if (file.error) {
388
+ this.onFileError.dispatch(file.key, file);
389
+ }
390
+ if (file.type !== 'packfile') {
391
+ this._loadedFileCount += 1;
392
+ this.onFileComplete.dispatch(this.progress, file.key, !file.error, this._loadedFileCount, this._totalFileCount);
393
+ } else if (file.type === 'packfile' && file.error) {
394
+ // Non-error pack files are handled when processing the file queue
395
+ this._loadedPackCount += 1;
396
+ this.onPackComplete.dispatch(file.key, !file.error, this._loadedPackCount, this._totalPackCount);
397
+ }
398
+ }
399
+ }
400
+ // When true further non-pack file downloads are suppressed
401
+ let syncblock = false;
402
+ const inflightLimit = this.enableParallel ? Math.max(1, this.maxParallelDownloads) : 1;
403
+ for (let i = this._processingHead; i < this._fileList.length; i += 1) {
404
+ const file = this._fileList[i];
405
+ // Pack is fetched (ie. has data) and is currently at the start of the process queue.
406
+ if (file.type === 'packfile' && !file.error && file.loaded && i === this._processingHead) {
407
+ // Processing the pack / adds more files
408
+ this.processPack(file);
409
+ this._loadedPackCount += 1;
410
+ this.onPackComplete.dispatch(file.key, !file.error, this._loadedPackCount, this._totalPackCount);
411
+ }
412
+ if (file.loaded || file.error) {
413
+ // Item at the start of file list finished, can skip it in future
414
+ if (i === this._processingHead) {
415
+ this._processingHead = i + 1;
416
+ }
417
+ } else if (!file.loading && this._flightQueue.length < inflightLimit) {
418
+ // -> not loaded/failed, not loading
419
+ if (file.type === 'packfile' && !file.data) {
420
+ // Fetches the pack data: the pack is processed above as it reaches queue-start.
421
+ // (Packs do not trigger onLoadStart or onFileStart.)
422
+ this._flightQueue.push(file);
423
+ file.loading = true;
424
+ this.loadFile(file);
425
+ } else if (!syncblock) {
426
+ if (!this._fileLoadStarted) {
427
+ this._fileLoadStarted = true;
428
+ this.onLoadStart.dispatch();
429
+ }
430
+ this._flightQueue.push(file);
431
+ file.loading = true;
432
+ this.onFileStart.dispatch(this.progress, file.key, file.url);
433
+ this.loadFile(file);
434
+ }
435
+ }
436
+ if (!file.loaded && file.syncPoint) {
437
+ syncblock = true;
438
+ }
439
+ // Stop looking if queue full - or if syncblocked and there are no more packs.
440
+ // (As only packs can be loaded around a syncblock)
441
+ if (this._flightQueue.length >= inflightLimit || (syncblock && this._loadedPackCount === this._totalPackCount)) {
442
+ break;
443
+ }
444
+ }
445
+ this.updateProgress();
446
+ // True when all items in the queue have been advanced over
447
+ // (There should be no inflight items as they are complete - loaded/error.)
448
+ if (this._processingHead >= this._fileList.length) {
449
+ this.finishedLoading();
450
+ } else if (!this._flightQueue.length) {
451
+ // Flight queue is empty but file list is not done being processed.
452
+ // This indicates a critical internal error with no known recovery.
453
+ console.warn('Loader - aborting: processing queue empty, loading may have stalled');
454
+ const _this = this;
455
+ setTimeout(() => {
456
+ _this.finishedLoading(true);
457
+ }, 2000);
458
+ }
459
+ }
460
+
461
+ finishedLoading(abnormal) {
462
+ if (this.hasLoaded) {
463
+ return;
464
+ }
465
+ this.hasLoaded = true;
466
+ this.isLoading = false;
467
+ // If there were no files make sure to trigger the event anyway, for consistency
468
+ if (!abnormal && !this._fileLoadStarted) {
469
+ this._fileLoadStarted = true;
470
+ this.onLoadStart.dispatch();
471
+ }
472
+ // https://github.com/photonstorm/phaser-ce/pull/54/
473
+ this.reset();
474
+ this.onLoadComplete.dispatch();
475
+ this.game.state.loadComplete();
476
+ // https://github.com/photonstorm/phaser-ce/pull/54/
477
+ // this.reset();
478
+ }
479
+
480
+ asyncComplete(file, errorMessage = '') {
481
+ file.loaded = true;
482
+ file.error = !!errorMessage;
483
+ if (errorMessage) {
484
+ file.errorMessage = errorMessage;
485
+ console.warn(file, errorMessage);
486
+ }
487
+ this.processLoadQueue();
488
+ }
489
+
490
+ processPack() {
491
+ // TODO
492
+ console.warn('loader.processPack() is not implemented');
493
+ }
494
+
495
+ transformUrl(url, file) {
496
+ if (!url) {
497
+ return false;
498
+ }
499
+ if (url.match(/^(?:blob:|data:|http:\/\/|https:\/\/|\/\/)/)) {
500
+ return url;
501
+ }
502
+ return this.baseURL + file.path + url;
503
+ }
504
+
505
+ loadFile(file) {
506
+ switch (file.type) {
507
+ case 'packfile':
508
+ this.xhrLoad(file, this.transformUrl(file.url, file), 'text', this.fileComplete);
509
+ break;
510
+ case 'image':
511
+ case 'spritesheet':
512
+ case 'textureatlas':
513
+ case 'bitmapfont':
514
+ this.loadImageTag(file);
515
+ break;
516
+ case 'audio':
517
+ file.url = this.getAudioURL(file.url);
518
+ if (file.url) {
519
+ // WebAudio or Audio Tag?
520
+ if (this.game.sound.usingWebAudio) {
521
+ this.xhrLoad(file, this.transformUrl(file.url, file), 'arraybuffer', this.fileComplete);
522
+ } else if (this.game.sound.usingAudioTag) {
523
+ this.loadAudioTag(file);
524
+ }
525
+ } else {
526
+ this.fileError(file, null, 'No supported audio URL specified or device does not have audio playback support');
527
+ }
528
+ break;
529
+ case 'video':
530
+ file.url = this.getVideoURL(file.url);
531
+ if (file.url) {
532
+ if (file.asBlob) {
533
+ this.xhrLoad(file, this.transformUrl(file.url, file), 'blob', this.fileComplete);
534
+ } else {
535
+ this.loadVideoTag(file);
536
+ }
537
+ } else {
538
+ this.fileError(file, null, 'No supported video URL specified or device does not have video playback support');
539
+ }
540
+ break;
541
+ case 'json':
542
+ this.xhrLoad(file, this.transformUrl(file.url, file), 'text', this.jsonLoadComplete);
543
+ break;
544
+ case 'xml':
545
+ this.xhrLoad(file, this.transformUrl(file.url, file), 'text', this.xmlLoadComplete);
546
+ break;
547
+ case 'tilemap':
548
+ if (file.format === TILED_JSON) {
549
+ this.xhrLoad(file, this.transformUrl(file.url, file), 'text', this.jsonLoadComplete);
550
+ } else if (file.format === TILEMAP_CSV) {
551
+ this.xhrLoad(file, this.transformUrl(file.url, file), 'text', this.csvLoadComplete);
552
+ } else {
553
+ this.asyncComplete(file, 'invalid Tilemap format: ' + file.format);
554
+ }
555
+ break;
556
+ case 'text':
557
+ case 'script':
558
+ case 'shader':
559
+ case 'physics':
560
+ this.xhrLoad(file, this.transformUrl(file.url, file), 'text', this.fileComplete);
561
+ break;
562
+ case 'binary':
563
+ this.xhrLoad(file, this.transformUrl(file.url, file), 'arraybuffer', this.fileComplete);
564
+ break;
565
+ default:
566
+ // pass
567
+ break;
568
+ }
569
+ }
570
+
571
+ loadImageTag(file) {
572
+ const _this = this;
573
+ file.data = new Image();
574
+ file.data.name = file.key;
575
+ if (this.crossOrigin) {
576
+ file.data.crossOrigin = this.crossOrigin;
577
+ }
578
+ file.data.onload = () => {
579
+ if (file.data.onload) {
580
+ file.data.onload = null;
581
+ file.data.onerror = null;
582
+ _this.fileComplete(file);
583
+ }
584
+ };
585
+ file.data.onerror = () => {
586
+ if (file.data.onload) {
587
+ file.data.onload = null;
588
+ file.data.onerror = null;
589
+ _this.fileError(file);
590
+ }
591
+ };
592
+ file.data.src = this.transformUrl(file.url, file);
593
+ // Image is immediately-available/cached
594
+ // Special Firefox magic, exclude from cached reload
595
+ // More info here: https://github.com/photonstorm/phaser/issues/2534
596
+ if (!this.game.device.firefox && file.data.complete && file.data.width && file.data.height) {
597
+ file.data.onload = null;
598
+ file.data.onerror = null;
599
+ this.fileComplete(file);
600
+ }
601
+ }
602
+
603
+ loadVideoTag() {
604
+ // TODO
605
+ console.warn('loader.loadVideoTag() is not implemented');
606
+ }
607
+
608
+ loadAudioTag(file) {
609
+ const scope = this;
610
+ if (this.game.sound.touchLocked) {
611
+ // If audio is locked we can't do this yet, so need to queue this load request. Bum.
612
+ file.data = new Audio();
613
+ file.data.name = file.key;
614
+ file.data.preload = 'auto';
615
+ file.data.src = this.transformUrl(file.url, file);
616
+ this.fileComplete(file);
617
+ } else {
618
+ file.data = new Audio();
619
+ file.data.name = file.key;
620
+ const playThroughEvent = () => {
621
+ file.data.removeEventListener('canplaythrough', playThroughEvent, false);
622
+ file.data.onerror = null;
623
+ scope.fileComplete(file);
624
+ };
625
+ file.data.onerror = () => {
626
+ file.data.removeEventListener('canplaythrough', playThroughEvent, false);
627
+ file.data.onerror = null;
628
+ scope.fileError(file);
629
+ };
630
+ file.data.preload = 'auto';
631
+ file.data.src = this.transformUrl(file.url, file);
632
+ file.data.addEventListener('canplaythrough', playThroughEvent, false);
633
+ file.data.load();
634
+ }
635
+ }
636
+
637
+ xhrLoad(file, url, type, onload, onerror) {
638
+ const xhr = new XMLHttpRequest();
639
+ xhr.open('GET', url, true);
640
+ xhr.responseType = type;
641
+ if (this.headers.requestedWith !== false) {
642
+ xhr.setRequestHeader('X-Requested-With', this.headers.requestedWith);
643
+ }
644
+ if (this.headers[file.type]) {
645
+ xhr.setRequestHeader('Accept', this.headers[file.type]);
646
+ }
647
+ onerror = onerror || this.fileError;
648
+ const scope = this;
649
+ xhr.onload = () => {
650
+ try {
651
+ if (xhr.readyState === 4 && xhr.status >= 400 && xhr.status <= 599) { // Handle HTTP status codes of 4xx and 5xx as errors, even if xhr.onerror was not called.
652
+ return onerror.call(scope, file, xhr);
653
+ }
654
+ return onload.call(scope, file, xhr);
655
+ } catch (e) {
656
+ // If this was the last file in the queue and an error is thrown in the create method
657
+ // then it's caught here, so be sure we don't carry on processing it
658
+ if (!scope.hasLoaded) {
659
+ scope.asyncComplete(file, e.message || 'Exception');
660
+ } else {
661
+ console.error(e);
662
+ }
663
+ }
664
+ return null;
665
+ };
666
+ xhr.onerror = () => {
667
+ try {
668
+ return onerror.call(scope, file, xhr);
669
+ } catch (e) {
670
+ if (!scope.hasLoaded) {
671
+ scope.asyncComplete(file, e.message || 'Exception');
672
+ } else {
673
+ console.error(e);
674
+ }
675
+ }
676
+ return null;
677
+ };
678
+ file.requestObject = xhr;
679
+ file.requestUrl = url;
680
+ xhr.send();
681
+ }
682
+
683
+ xhrLoadWithXDR() {
684
+ // TODO
685
+ console.warn('loader.xhrLoadWithXDR() is not implemented');
686
+ }
687
+
688
+ getAudioURL(urls) {
689
+ if (this.game.sound.noAudio) {
690
+ return null;
691
+ }
692
+ for (let i = 0; i < urls.length; i += 1) {
693
+ let url = urls[i];
694
+ let audioType = null;
695
+ if (url.uri) {
696
+ // {uri: .., type: ..} pair
697
+ audioType = url.type;
698
+ url = url.uri;
699
+ if (canPlayAudio(this.game.device, audioType)) {
700
+ return url;
701
+ }
702
+ } else {
703
+ // Assume direct-data URI can be played if not in a paired form; select immediately
704
+ if (url.indexOf('blob:') === 0 || url.indexOf('data:') === 0) {
705
+ return url;
706
+ }
707
+ if (url.indexOf('?') >= 0) {
708
+ // Remove query from URL
709
+ url = url.substr(0, url.indexOf('?'));
710
+ }
711
+ const extension = url.substr((Math.max(0, url.lastIndexOf('.')) || Infinity) + 1);
712
+ audioType = extension.toLowerCase();
713
+ if (canPlayAudio(this.game.device, audioType)) {
714
+ return urls[i];
715
+ }
716
+ }
717
+ }
718
+ return null;
719
+ }
720
+
721
+ fileError(file, xhr, reason) {
722
+ const url = file.requestUrl || this.transformUrl(file.url, file);
723
+ let message = 'error loading asset from URL ' + url;
724
+ if (!reason && xhr) {
725
+ reason = xhr.status;
726
+ }
727
+ if (reason) {
728
+ message = message + ' (' + reason + ')';
729
+ }
730
+ this.asyncComplete(file, message);
731
+ }
732
+
733
+ fileComplete(file, xhr) {
734
+ let loadNext = true;
735
+ switch (file.type) {
736
+ case 'packfile':
737
+ // Pack data must never be false-ish after it is fetched without error
738
+ file.data = JSON.parse(xhr.responseText) || {};
739
+ break;
740
+ case 'image':
741
+ this.cache.addImage(file.key, file.url, file.data);
742
+ break;
743
+ case 'spritesheet':
744
+ this.cache.addSpriteSheet(file.key, file.url, file.data, file.frameWidth, file.frameHeight, file.frameMax, file.margin, file.spacing);
745
+ break;
746
+ case 'textureatlas':
747
+ if (file.atlasURL == null) {
748
+ this.cache.addTextureAtlas(file.key, file.url, file.data, file.atlasData, file.format);
749
+ } else {
750
+ // Load the JSON or XML before carrying on with the next file
751
+ loadNext = false;
752
+ if (file.format === TEXTURE_ATLAS_JSON_ARRAY || file.format === TEXTURE_ATLAS_JSON_HASH || file.format === TEXTURE_ATLAS_JSON_PYXEL) {
753
+ this.xhrLoad(file, this.transformUrl(file.atlasURL, file), 'text', this.jsonLoadComplete);
754
+ } else if (file.format === TEXTURE_ATLAS_XML_STARLING) {
755
+ this.xhrLoad(file, this.transformUrl(file.atlasURL, file), 'text', this.xmlLoadComplete);
756
+ } else {
757
+ throw new Error('Invalid Texture Atlas format: ' + file.format);
758
+ }
759
+ }
760
+ break;
761
+ case 'bitmapfont':
762
+ if (!file.atlasURL) {
763
+ this.cache.addBitmapFont(file.key, file.url, file.data, file.atlasData, file.atlasType, file.xSpacing, file.ySpacing);
764
+ } else {
765
+ // Load the XML before carrying on with the next file
766
+ loadNext = false;
767
+ this.xhrLoad(file, this.transformUrl(file.atlasURL, file), 'text', (bitmapFontFile, bitmapFontXhr) => {
768
+ let json;
769
+ try {
770
+ // Try to parse as JSON, if it fails, then it's hopefully XML
771
+ json = JSON.parse(bitmapFontXhr.responseText);
772
+ } catch (e) {
773
+ // pass
774
+ }
775
+ if (json) {
776
+ bitmapFontFile.atlasType = 'json';
777
+ this.jsonLoadComplete(bitmapFontFile, bitmapFontXhr);
778
+ } else {
779
+ bitmapFontFile.atlasType = 'xml';
780
+ this.xmlLoadComplete(bitmapFontFile, bitmapFontXhr);
781
+ }
782
+ });
783
+ }
784
+ break;
785
+ case 'video':
786
+ if (file.asBlob) {
787
+ try {
788
+ file.data = xhr.response;
789
+ } catch (e) {
790
+ throw new Error('Unable to parse video file as Blob: ' + file.key);
791
+ }
792
+ }
793
+ this.cache.addVideo(file.key, file.url, file.data, file.asBlob);
794
+ break;
795
+ case 'audio':
796
+ if (this.game.sound.usingWebAudio) {
797
+ file.data = xhr.response;
798
+ this.cache.addSound(file.key, file.url, file.data, true, false);
799
+ if (file.autoDecode) {
800
+ this.game.sound.decode(file.key);
801
+ }
802
+ } else {
803
+ this.cache.addSound(file.key, file.url, file.data, false, true);
804
+ }
805
+ break;
806
+ case 'text':
807
+ file.data = xhr.responseText;
808
+ this.cache.addText(file.key, file.url, file.data);
809
+ break;
810
+ case 'shader':
811
+ file.data = xhr.responseText;
812
+ this.cache.addShader(file.key, file.url, file.data);
813
+ break;
814
+ case 'physics':
815
+ this.cache.addPhysicsData(file.key, file.url, JSON.parse(xhr.responseText), file.format);
816
+ break;
817
+ case 'script':
818
+ file.data = document.createElement('script');
819
+ file.data.language = 'javascript';
820
+ file.data.type = 'text/javascript';
821
+ file.data.defer = false;
822
+ file.data.text = xhr.responseText;
823
+ document.head.appendChild(file.data);
824
+ if (file.callback) {
825
+ file.data = file.callback.call(file.callbackContext, file.key, xhr.responseText);
826
+ }
827
+ break;
828
+ case 'binary':
829
+ if (file.callback) {
830
+ file.data = file.callback.call(file.callbackContext, file.key, xhr.response);
831
+ } else {
832
+ file.data = xhr.response;
833
+ }
834
+ this.cache.addBinary(file.key, file.data);
835
+ break;
836
+ default:
837
+ // pass
838
+ break;
839
+ }
840
+ if (loadNext) {
841
+ this.asyncComplete(file);
842
+ }
843
+ }
844
+
845
+ jsonLoadComplete(file, xhr) {
846
+ const data = JSON.parse(xhr.responseText);
847
+ if (file.type === 'tilemap') {
848
+ this.cache.addTilemap(file.key, file.url, data, file.format);
849
+ } else if (file.type === 'bitmapfont') {
850
+ this.cache.addBitmapFont(file.key, file.url, file.data, data, file.atlasType, file.xSpacing, file.ySpacing);
851
+ } else if (file.type === 'json') {
852
+ this.cache.addJSON(file.key, file.url, data);
853
+ } else {
854
+ this.cache.addTextureAtlas(file.key, file.url, file.data, data, file.format);
855
+ }
856
+ this.asyncComplete(file);
857
+ }
858
+
859
+ csvLoadComplete() {
860
+ // TODO
861
+ console.warn('loader.csvLoadComplete() is not implemented');
862
+ }
863
+
864
+ xmlLoadComplete(file, xhr) {
865
+ // Always try parsing the content as XML, regardless of actually response type
866
+ const data = xhr.responseText;
867
+ const xml = this.parseXml(data);
868
+ if (!xml) {
869
+ const responseType = xhr.responseType || xhr.contentType; // contentType for MS-XDomainRequest
870
+ console.warn('Loader - ' + file.key + ': invalid XML (' + responseType + ')');
871
+ this.asyncComplete(file, 'invalid XML');
872
+ return;
873
+ }
874
+ if (file.type === 'bitmapfont') {
875
+ this.cache.addBitmapFont(file.key, file.url, file.data, xml, file.atlasType, file.xSpacing, file.ySpacing);
876
+ } else if (file.type === 'textureatlas') {
877
+ this.cache.addTextureAtlas(file.key, file.url, file.data, xml, file.format);
878
+ } else if (file.type === 'xml') {
879
+ this.cache.addXML(file.key, file.url, xml);
880
+ }
881
+ this.asyncComplete(file);
882
+ }
883
+
884
+ parseXml(data) {
885
+ let xml = null;
886
+ try {
887
+ if (window.DOMParser) {
888
+ const domparser = new DOMParser();
889
+ xml = domparser.parseFromString(data, 'text/xml');
890
+ } else {
891
+ xml = new window.ActiveXObject('Microsoft.XMLDOM');
892
+ // Why is this 'false'?
893
+ xml.async = 'false';
894
+ xml.loadXML(data);
895
+ }
896
+ } catch (e) {
897
+ xml = null;
898
+ }
899
+ if (!xml || !xml.documentElement || xml.getElementsByTagName('parsererror').length) {
900
+ return null;
901
+ }
902
+ return xml;
903
+ }
904
+
905
+ updateProgress() {
906
+ if (this.preloadSprite) {
907
+ if (this.preloadSprite.direction === 0) {
908
+ this.preloadSprite.rect.width = Math.floor((this.preloadSprite.width / 100) * this.progress);
909
+ } else {
910
+ this.preloadSprite.rect.height = Math.floor((this.preloadSprite.height / 100) * this.progress);
911
+ }
912
+ if (this.preloadSprite.sprite) {
913
+ this.preloadSprite.sprite.updateCrop();
914
+ } else {
915
+ // We seem to have lost our sprite - maybe it was destroyed?
916
+ this.preloadSprite = null;
917
+ }
918
+ }
919
+ }
920
+
921
+ totalLoadedFiles() {
922
+ return this._loadedFileCount;
923
+ }
924
+
925
+ totalQueuedFiles() {
926
+ return this._totalFileCount - this._loadedFileCount;
927
+ }
928
+
929
+ totalLoadedPacks() {
930
+ return this._totalPackCount;
931
+ }
932
+
933
+ totalQueuedPacks() {
934
+ return this._totalPackCount - this._loadedPackCount;
935
+ }
936
+
937
+ get progressFloat() {
938
+ const progress = (this._loadedFileCount / this._totalFileCount) * 100;
939
+ return Math.max(0, Math.min(100, progress || 0));
940
+ }
941
+
942
+ get progress() {
943
+ return Math.round(this.progressFloat);
944
+ }
945
+
946
+ }