-
Notifications
You must be signed in to change notification settings - Fork 183
/
Copy pathpjs-resource-cache.js
152 lines (131 loc) · 4.65 KB
/
pjs-resource-cache.js
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
function PJSResourceCache(options) {
this.canvas = options.canvas; // customized Processing instance
this.output = options.output; // LiveEditorOutput instance
this.cache = {};
this.imageHolder = null;
// Insert the images into a hidden div to cause them to load
// but not be visible to the user
if (!this.imageHolder) {
this.imageHolder = document.createElement("div");
this.imageHolder.style.height = 0;
this.imageHolder.style.width = 0;
this.imageHolder.style.overflow = "hidden";
this.imageHolder.style.position = "absolute";
document.body.appendChild(this.imageHolder);
}
}
/**
* Load and cache all resources (images and sounds) referenced in the code.
*
* All resources are loaded as we don't have more details on exactly which
* images will be required. Execution is delayed if a getImage/getSound call
* is encountered in the source code and none of the resources have been loaded
* yet. Execution begins once all the resources have loaded.
*
* @param {Object} resources A object whose keys are filenames
* @returns {Promise}
*/
PJSResourceCache.prototype.cacheResources = function(resources) {
var promises = Object.keys(resources).map((filename) => {
return this.loadResource(filename);
});
return Promise.all(promises);
};
PJSResourceCache.prototype.loadResource = function(filename) {
if (filename.endsWith(".png")) {
return this.loadImage(filename);
} else if (filename.endsWith(".mp3")) {
return this.loadSound(filename);
}
};
PJSResourceCache.prototype.loadImage = function(filename) {
return new Promise((resolve) => {
var path = this.output.imagesDir + filename;
var img = document.createElement("img");
img.onload = () => {
this.cache[filename] = img;
resolve();
};
img.onerror = () => {
resolve(); // always resolve
};
img.src = path;
// Allow for cross-origin images to safely load (this will still be
// restricted by the CSP for the frame). More info:
// https://stackoverflow.com/questions/22097747/how-to-fix-getimagedata-error-the-canvas-has-been-tainted-by-cross-origin-data
img.setAttribute("crossOrigin", "");
this.imageHolder.appendChild(img);
});
};
PJSResourceCache.prototype.loadSound = function(filename) {
return new Promise((resolve) => {
var audio = document.createElement("audio");
var parts = filename.split("/");
var group = _.findWhere(OutputSounds[0].groups, { groupName: parts[0] });
var hasSound = group && group.sounds.includes(parts[1].replace(".mp3", ""));
if (!hasSound) {
resolve();
return;
}
audio.preload = "auto";
audio.oncanplaythrough = () => {
this.cache[filename] = {
audio: audio,
__id: function () {
return `getSound('${filename.replace(".mp3", "")}')`;
}
};
resolve();
};
audio.onerror = () => {
resolve();
};
audio.src = this.output.soundsDir + filename;
});
};
PJSResourceCache.prototype.getResource = function(filename, type) {
switch (type) {
case "image":
return this.getImage(filename);
case "sound":
return this.getSound(filename);
default:
throw "we can't load '" + type + "' resources yet";
}
};
PJSResourceCache.prototype.getImage = function(filename) {
var image = this.cache[filename + ".png"];
if (!image) {
throw {message:
i18n._("Image '%(file)s' was not found.", {file: filename})};
}
// cache <img> instead of PImage until we investigate how caching
// PImage instances affects loadPixels(), pixels[], updatePixels()
var pImage = new this.canvas.PImage(image);
pImage.__id = function() {
return "getImage('" + filename + "')";
};
return pImage;
};
PJSResourceCache.prototype.getSound = function(filename) {
var sound = this.cache[filename + ".mp3"];
if (!sound) {
throw {message:
i18n._("Sound '%(file)s' was not found.", {file: filename})};
}
return sound;
};
/**
* Searches for strings containing the name of any image or sound we providefor
* users and adds them to `resources` as a key.
*
* @param {string} code
* @returns {Object}
*/
PJSResourceCache.findResources = function(code) {
let ast = esprima.parse(code, { loc: true });
let resources = {};
walkAST(ast, null,
[ASTTransforms.findResources(resources)]);
return resources;
};