/
Resource.ts
95 lines (87 loc) · 3.24 KB
/
Resource.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import { Loadable } from '../Interfaces/Loadable';
import { Logger } from '../Util/Log';
import { EventEmitter } from '../EventEmitter';
export type ResourceEvents = {
complete: any;
load: ProgressEvent<XMLHttpRequestEventTarget>;
loadstart: ProgressEvent<XMLHttpRequestEventTarget>;
progress: ProgressEvent<XMLHttpRequestEventTarget>;
error: ProgressEvent<XMLHttpRequestEventTarget>;
};
export const ResourceEvents = {
Complete: 'complete',
Load: 'load',
LoadStart: 'loadstart',
Progress: 'progress',
Error: 'error'
};
/**
* The [[Resource]] type allows games built in Excalibur to load generic resources.
* For any type of remote resource it is recommended to use [[Resource]] for preloading.
*/
export class Resource<T> implements Loadable<T> {
public data: T = null;
public logger: Logger = Logger.getInstance();
public events = new EventEmitter();
/**
* @param path Path to the remote resource
* @param responseType The type to expect as a response: "" | "arraybuffer" | "blob" | "document" | "json" | "text";
* @param bustCache Whether or not to cache-bust requests
*/
constructor(
public path: string,
public responseType: '' | 'arraybuffer' | 'blob' | 'document' | 'json' | 'text',
public bustCache: boolean = false
) {}
/**
* Returns true if the Resource is completely loaded and is ready
* to be drawn.
*/
public isLoaded(): boolean {
return this.data !== null;
}
private _cacheBust(uri: string): string {
const query: RegExp = /\?\w*=\w*/;
if (query.test(uri)) {
uri += '&__=' + Date.now();
} else {
uri += '?__=' + Date.now();
}
return uri;
}
/**
* Begin loading the resource and returns a promise to be resolved on completion
*/
public load(): Promise<T> {
return new Promise((resolve, reject) => {
// Exit early if we already have data
if (this.data !== null) {
this.logger.debug('Already have data for resource', this.path);
this.events.emit('complete', this.data as any);
resolve(this.data);
return;
}
const request = new XMLHttpRequest();
request.open('GET', this.bustCache ? this._cacheBust(this.path) : this.path, true);
request.responseType = this.responseType;
request.addEventListener('loadstart', (e) => this.events.emit('loadstart', e as any));
request.addEventListener('progress', (e) => this.events.emit('progress', e as any));
request.addEventListener('error', (e) => this.events.emit('error', e as any));
request.addEventListener('load', (e) => this.events.emit('load', e as any));
request.addEventListener('load', () => {
// XHR on file:// success status is 0, such as with PhantomJS
if (request.status !== 0 && request.status !== 200) {
this.logger.error('Failed to load resource ', this.path, ' server responded with error code', request.status);
this.events.emit('error', request.response);
reject(new Error(request.statusText));
return;
}
this.data = request.response;
this.events.emit('complete', this.data as any);
this.logger.debug('Completed loading resource', this.path);
resolve(this.data);
});
request.send();
});
}
}