'+val);
202 | });
203 | c.find('.list-index').width(c.find('.list-index').last().width());
204 | c.find('.list-item').width(c.width()-c.find('.list-index').width()-15);
205 | var s = this.scrollbar.height(c.height());
206 | s.children('.list-scrollbar').height(s.height()/c[0].scrollHeight*s.height()).css('display', s.children('.list-scrollbar').height()===c.height() ? 'none' : 'inline-block');
207 | this.el.find('.list-length').text('length: '+this.contents.length);
208 | };
209 |
210 | List.prototype.updateLayer = function() {
211 | this.el.css('z-index', this.z);
212 | };
213 |
214 |
--------------------------------------------------------------------------------
/js/Runtime.js:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Massachusetts Institute of Technology
2 | //
3 | // This program is free software; you can redistribute it and/or
4 | // modify it under the terms of the GNU General Public License version 2,
5 | // as published by the Free Software Foundation.
6 | //
7 | // This program is distributed in the hope that it will be useful,
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | // GNU General Public License for more details.
11 | //
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 |
16 | // Scratch HTML5 Player
17 | // Runtime.js
18 | // Tim Mickel, July 2011
19 |
20 | // Runtime takes care of the rendering and stepping logic.
21 |
22 | 'use strict';
23 |
24 | var t = new Timer();
25 |
26 | var Runtime = function() {
27 | this.scene = null;
28 | this.sprites = [];
29 | this.reporters = [];
30 | this.keysDown = {};
31 | this.mouseDown = false;
32 | this.mousePos = [0, 0];
33 | this.audioContext = null;
34 | this.audioGain = null;
35 | this.audioPlaying = [];
36 | this.notesPlaying = [];
37 | this.projectLoaded = false;
38 | };
39 |
40 | // Initializer for the drawing and audio contexts.
41 | Runtime.prototype.init = function() {
42 | this.scene = $('#container');
43 | window.AudioContext = window.AudioContext || window.webkitAudioContext;
44 | this.audioContext = new AudioContext();
45 | try {
46 | this.audioGain = this.audioContext.createGain();
47 | } catch(err) {
48 | this.audioGain = this.audioContext.createGainNode();
49 | }
50 | this.audioGain.connect(runtime.audioContext.destination);
51 | };
52 |
53 | // Load start waits for the stage and the sprites to be loaded, without
54 | // hanging the browser. When the loading is finished, we begin the step
55 | // and animate methods.
56 | Runtime.prototype.loadStart = function() {
57 | if (!runtime.stage.isLoaded()) {
58 | setTimeout(function(runtime) { runtime.loadStart(); }, 50, this);
59 | return;
60 | }
61 | for (var obj = 0; obj < runtime.sprites.length; obj++) {
62 | if (typeof(runtime.sprites[obj]) == 'object' && runtime.sprites[obj].constructor == Sprite) {
63 | if (!runtime.sprites[obj].isLoaded()) {
64 | setTimeout(function(runtime) { runtime.loadStart(); }, 50, this);
65 | return;
66 | }
67 | }
68 | }
69 | if (Instr.wavsLoaded != Instr.wavCount) {
70 | setTimeout(function(runtime) { runtime.loadStart(); }, 50, this);
71 | return;
72 | }
73 | $('#preloader').css('display', 'none');
74 | setInterval(this.step, 33);
75 | this.projectLoaded = true;
76 | };
77 |
78 | Runtime.prototype.greenFlag = function() {
79 | if (this.projectLoaded) {
80 | interp.activeThread = new Thread(null);
81 | interp.threads = [];
82 | interp.primitiveTable.timerReset();
83 | this.startGreenFlags();
84 | }
85 | };
86 |
87 | Runtime.prototype.stopAll = function() {
88 | interp.activeThread = new Thread(null);
89 | interp.threads = [];
90 | stopAllSounds();
91 | // Hide sprite bubbles, resetFilters and doAsk prompts
92 | for (var s = 0; s < runtime.sprites.length; s++) {
93 | if (runtime.sprites[s].hideBubble) runtime.sprites[s].hideBubble();
94 | if (runtime.sprites[s].resetFilters) runtime.sprites[s].resetFilters();
95 | if (runtime.sprites[s].hideAsk) runtime.sprites[s].hideAsk();
96 | }
97 | // Reset graphic effects
98 | runtime.stage.resetFilters();
99 | };
100 |
101 | // Step method for execution - called every 33 milliseconds
102 | Runtime.prototype.step = function() {
103 | interp.stepThreads();
104 | for (var r = 0; r < runtime.reporters.length; r++) {
105 | runtime.reporters[r].update();
106 | }
107 | };
108 |
109 | // Stack functions -- push and remove stacks
110 | // to be run by the interpreter as threads.
111 | Runtime.prototype.allStacksDo = function(f) {
112 | var stage = runtime.stage;
113 | var stack;
114 | for (var i = runtime.sprites.length-1; i >= 0; i--) {
115 | var o = runtime.sprites[i];
116 | if (typeof(o) == 'object' && o.constructor == Sprite) {
117 | $.each(o.stacks, function(index, stack) {
118 | f(stack, o);
119 | });
120 | }
121 | }
122 | $.each(stage.stacks, function(index, stack) {
123 | f(stack, stage);
124 | });
125 | };
126 |
127 | // Hat triggers
128 | Runtime.prototype.startGreenFlags = function() {
129 | function startIfGreenFlag(stack, target) {
130 | if (stack.op == 'whenGreenFlag') interp.toggleThread(stack, target);
131 | }
132 | this.allStacksDo(startIfGreenFlag);
133 | };
134 |
135 | Runtime.prototype.startKeyHats = function(ch) {
136 | var keyName = null;
137 | if (('A'.charCodeAt(0) <= ch) && (ch <= 'Z'.charCodeAt(0)) ||
138 | ('a'.charCodeAt(0) <= ch) && (ch <= 'z'.charCodeAt(0)))
139 | keyName = String.fromCharCode(ch).toLowerCase();
140 | if (('0'.charCodeAt(0) <= ch) && (ch <= '9'.charCodeAt(0)))
141 | keyName = String.fromCharCode(ch);
142 |
143 | if (ch == 37) keyName = "left arrow";
144 | if (ch == 39) keyName = "right arrow";
145 | if (ch == 38) keyName = "up arrow";
146 | if (ch == 40) keyName = "down arrow";
147 | if (ch == 32) keyName = "space";
148 |
149 | if (keyName == null) return;
150 | var startMatchingKeyHats = function(stack, target) {
151 | if ((stack.op == "whenKeyPressed") && (stack.args[0] == keyName)) {
152 | // Only start the stack if it is not already running
153 | if (!interp.isRunning(stack)) {
154 | interp.toggleThread(stack, target);
155 | }
156 | }
157 | }
158 | runtime.allStacksDo(startMatchingKeyHats);
159 | };
160 |
161 | Runtime.prototype.startClickedHats = function(sprite) {
162 | function startIfClicked(stack, target) {
163 | if (target == sprite && stack.op == "whenClicked" && !interp.isRunning(stack)) {
164 | interp.toggleThread(stack, target);
165 | }
166 | }
167 | runtime.allStacksDo(startIfClicked);
168 | };
169 |
170 | // Returns true if a key is pressed.
171 | Runtime.prototype.keyIsDown = function(ch) {
172 | return this.keysDown[ch] || false;
173 | };
174 |
175 | // Sprite named -- returns one of the sprites on the stage.
176 | Runtime.prototype.spriteNamed = function(n) {
177 | if (n == 'Stage') return this.stage;
178 | var selected_sprite = null;
179 | $.each(this.sprites, function(index, s) {
180 | if (s.objName == n) {
181 | selected_sprite = s;
182 | return false;
183 | }
184 | });
185 | return selected_sprite;
186 | };
187 |
188 | Runtime.prototype.getTimeString = function(which) {
189 | // Return local time properties.
190 | var now = new Date();
191 | switch (which) {
192 | case 'hour': return now.getHours();
193 | case 'minute': return now.getMinutes();
194 | case 'second': return now.getSeconds();
195 | case 'year': return now.getFullYear(); // four digit year (e.g. 2012)
196 | case 'month': return now.getMonth() + 1; // 1-12
197 | case 'date': return now.getDate(); // 1-31
198 | case 'day of week': return now.getDay() + 1; // 1-7, where 1 is Sunday
199 | }
200 | return ''; // shouldn't happen
201 | };
202 |
203 | // Reassigns z-indices for layer functions
204 | Runtime.prototype.reassignZ = function(target, move) {
205 | var sprites = this.sprites;
206 | var oldIndex = -1;
207 | $.each(this.sprites, function(index, sprite) {
208 | if (sprite == target) {
209 | // Splice out the sprite from its old position
210 | oldIndex = index;
211 | sprites.splice(index, 1);
212 | }
213 | });
214 |
215 | if (move == null) {
216 | // Move to the front
217 | this.sprites.splice(this.sprites.length, 0, target);
218 | } else if (oldIndex - move >= 0 && oldIndex - move < this.sprites.length + 1) {
219 | // Move to the new position
220 | this.sprites.splice(oldIndex - move, 0, target);
221 | } else {
222 | // No change is required
223 | this.sprites.splice(oldIndex, 0, target);
224 | }
225 |
226 | // Renumber the z-indices
227 | var newZ = 1;
228 | $.each(this.sprites, function(index, sprite) {
229 | sprite.z = newZ;
230 | sprite.updateLayer();
231 | newZ++;
232 | });
233 | };
234 |
--------------------------------------------------------------------------------
/js/Scratch.js:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Massachusetts Institute of Technology
2 | //
3 | // This program is free software; you can redistribute it and/or
4 | // modify it under the terms of the GNU General Public License version 2,
5 | // as published by the Free Software Foundation.
6 | //
7 | // This program is distributed in the hope that it will be useful,
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | // GNU General Public License for more details.
11 | //
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 |
16 | // Scratch HTML5 Player
17 | // Scratch.js
18 | // Tim Mickel, July 2011
19 |
20 | // Here we define the actions taken on window load.
21 | // The three application-wide global variables are defined here.
22 |
23 | 'use strict';
24 |
25 | var runtime, interp, io, iosAudioActive = false;
26 | function Scratch(project_id) {
27 | runtime = new Runtime();
28 | runtime.init();
29 |
30 | $(window).keydown(function(e) {
31 | runtime.keysDown[e.which] = true;
32 | runtime.startKeyHats(e.which);
33 | });
34 |
35 | $(window).keyup(function(e) {
36 | delete runtime.keysDown[e.which];
37 | });
38 |
39 | var address = $('#address-hint');
40 | var project = $('#project-id');
41 |
42 | // Update the project ID field
43 | project.val(project_id);
44 |
45 | // Validate project ID field
46 | project.keyup(function() {
47 | var n = this.value;
48 |
49 | // Allow URL pasting
50 | var e = /projects\/(\d+)/.exec(n);
51 | if (e) {
52 | n = this.value = e[1];
53 | }
54 |
55 | // Eventually, this will xhr to /projects/{{this.value}}/ and
56 | // change color based on whether the response is 404 or 200.
57 | $('#project-id, #address-hint').toggleClass('error', isNaN(n));
58 | });
59 |
60 | // Focus the actual input when the user clicks on the URL hint
61 | address.click(function() {
62 | project.select();
63 | });
64 |
65 | var width = address.outerWidth();
66 | project.css({
67 | paddingLeft: width,
68 | marginLeft: -width
69 | });
70 |
71 | // Go project button behavior
72 | $('#go-project').click(function() {
73 | window.location = '#' + parseInt($('#project-id').val());
74 | window.location.reload(true);
75 | });
76 |
77 | // Green flag behavior
78 | $('#trigger-green-flag, #overlay').click(function() {
79 | if (!runtime.projectLoaded) return;
80 | $('#overlay').css('display', 'none');
81 | runtime.greenFlag()
82 | });
83 |
84 | // Stop button behavior
85 | $('#trigger-stop').click(function() {
86 | runtime.stopAll();
87 | });
88 |
89 | // Canvas container mouse events
90 | $('#container').mousedown(function(e) {
91 | runtime.mouseDown = true;
92 | //e.preventDefault();
93 | });
94 |
95 | $('#container').mouseup(function(e) {
96 | runtime.mouseDown = false;
97 | //e.preventDefault();
98 | });
99 |
100 | $('#container').mousemove(function(e) {
101 | var bb = this.getBoundingClientRect();
102 | var absX = e.clientX - bb.left;
103 | var absY = e.clientY - bb.top;
104 | runtime.mousePos = [absX-240, -absY+180];
105 | });
106 |
107 | // Touch events - EXPERIMENTAL
108 | $(window).bind('touchstart', function(e) {
109 | // On iOS, we need to activate the Web Audio API
110 | // with an empty sound play on the first touch event.
111 | if (!iosAudioActive) {
112 | var ibuffer = runtime.audioContext.createBuffer(1, 1, 22050);
113 | var isource = runtime.audioContext.createBufferSource();
114 | isource.buffer = ibuffer;
115 | isource.connect(runtime.audioContext.destination);
116 | isource.start();
117 | iosAudioActive = true;
118 | }
119 | });
120 |
121 | $('#container').bind('touchstart', function(e) {
122 | runtime.mouseDown = true;
123 | });
124 |
125 | $('#container').bind('touchend', function(e) {
126 | runtime.mouseDown = true;
127 | });
128 |
129 | $('#container').bind('touchmove', function(e) {
130 | var touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
131 | var bb = this.getBoundingClientRect();
132 | var absX = touch.clientX - bb.left;
133 | var absY = touch.clientY - bb.top;
134 | runtime.mousePos = [absX-240, -absY+180];
135 | });
136 |
137 | // Border touch events - EXPERIMENTAL
138 | $('#left').bind('touchstart touchmove', function(e) { runtime.keysDown[37] = true; runtime.startKeyHats(37); });
139 | $('#left').bind('touchend', function(e) { delete runtime.keysDown[37]; });
140 | $('#up').bind('touchstart touchmove', function(e) { runtime.keysDown[38] = true; runtime.startKeyHats(38); });
141 | $('#up').bind('touchend', function(e) { delete runtime.keysDown[38]; });
142 | $('#right').bind('touchstart touchmove', function(e) { runtime.keysDown[39] = true; runtime.startKeyHats(39); });
143 | $('#right').bind('touchend', function(e) { delete runtime.keysDown[39]; });
144 | $('#down').bind('touchstart touchmove', function(e) { runtime.keysDown[40] = true; runtime.startKeyHats(40); });
145 | $('#down').bind('touchend', function(e) { delete runtime.keysDown[40]; });
146 |
147 | // Load the interpreter and primitives
148 | interp = new Interpreter();
149 | interp.initPrims();
150 |
151 | // Load the requested project and go!
152 | io = new IO();
153 | io.loadProject(project_id);
154 | };
155 |
--------------------------------------------------------------------------------
/js/Stage.js:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Massachusetts Institute of Technology
2 | //
3 | // This program is free software; you can redistribute it and/or
4 | // modify it under the terms of the GNU General Public License version 2,
5 | // as published by the Free Software Foundation.
6 | //
7 | // This program is distributed in the hope that it will be useful,
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | // GNU General Public License for more details.
11 | //
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 |
16 | // Scratch HTML5 Player
17 | // Stage.js
18 | // Tim Mickel, July 2011 - March 2012
19 |
20 | // Provides the basic logic for the Stage, a special kind of Sprite.
21 |
22 | 'use strict';
23 |
24 | var Stage = function(data) {
25 | // Place the background layer in the very back.
26 | // The pen layer is right above the stage background,
27 | // and all sprites are above that.
28 | this.z = -2;
29 |
30 | // Pen layer and canvas cache.
31 | this.penLayerLoaded = false;
32 | this.lineCanvas = document.createElement('canvas');
33 | this.lineCanvas.width = 480;
34 | this.lineCanvas.height = 360;
35 | this.lineCache = this.lineCanvas.getContext('2d');
36 | this.isStage = true;
37 | this.askAnswer = ""; //this is a private variable and should be blank
38 |
39 | Sprite.call(this, data);
40 | };
41 |
42 | Stage.prototype = Object.create(Sprite.prototype);
43 | Stage.prototype.constructor = Stage;
44 |
45 | Stage.prototype.attachPenLayer = function(scene) {
46 | if (this.penLayerLoaded) return;
47 | this.penLayerLoaded = true;
48 | $(this.lineCanvas).css('position', 'absolute');
49 | $(this.lineCanvas).css('z-index', '-1');
50 | scene.append(this.lineCanvas);
51 | };
52 |
53 | Stage.prototype.isLoaded = function() {
54 | return this.penLayerLoaded && this.costumesLoaded == this.costumes.length && this.soundsLoaded == Object.keys(this.sounds).length;
55 | };
56 |
57 | // Pen functions
58 | Stage.prototype.clearPenStrokes = function() {
59 | this.lineCache.clearRect(0, 0, 480, 360);
60 | };
61 |
62 | Stage.prototype.stroke = function(from, to, width, color) {
63 | this.lineCache.lineWidth = width;
64 | this.lineCache.lineCap = 'round';
65 | this.lineCache.beginPath();
66 | // Use .5 offsets for canvas rigid pixel drawing
67 | this.lineCache.moveTo(from[0] + 240.5, 180.5 - from[1]);
68 | this.lineCache.lineTo(to[0] + 240.5, 180.5 - to[1]);
69 | this.lineCache.strokeStyle = 'rgb(' + (color >>> 16 & 255) + ',' + (color >>> 8 & 255) + ',' + (color >>> 0 & 255) + ')';
70 | this.lineCache.stroke();
71 | };
72 |
--------------------------------------------------------------------------------
/js/primitives/LooksPrims.js:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Massachusetts Institute of Technology
2 | //
3 | // This program is free software; you can redistribute it and/or
4 | // modify it under the terms of the GNU General Public License version 2,
5 | // as published by the Free Software Foundation.
6 | //
7 | // This program is distributed in the hope that it will be useful,
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | // GNU General Public License for more details.
11 | //
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 |
16 | 'use strict';
17 |
18 | var LooksPrims = function() {};
19 |
20 | LooksPrims.prototype.addPrimsTo = function(primTable) {
21 | primTable['show'] = this.primShow;
22 | primTable['hide'] = this.primHide;
23 |
24 | primTable['nextCostume'] = this.primNextCostume;
25 | primTable['lookLike:'] = this.primShowCostume;
26 | primTable['costumeIndex'] = this.primCostumeNum;
27 |
28 | primTable['nextScene'] = this.primNextCostume;
29 | primTable['showBackground:'] = this.primShowCostume;
30 | primTable['backgroundIndex'] = this.primCostumeNum;
31 |
32 | primTable['startScene'] = this.primStartScene;
33 | primTable['backgroundIndex'] = this.primCostumeNum;
34 |
35 | primTable['changeSizeBy:'] = this.primChangeSize;
36 | primTable['setSizeTo:'] = this.primSetSize;
37 | primTable['scale'] = this.primSize;
38 |
39 | primTable['comeToFront'] = this.primGoFront;
40 | primTable['goBackByLayers:'] = this.primGoBack;
41 |
42 | primTable['changeGraphicEffect:by:'] = this.primChangeEffect;
43 | primTable['setGraphicEffect:to:'] = this.primSetEffect;
44 | primTable['filterReset'] = this.primClearEffects;
45 |
46 | primTable['say:'] = function(b) { showBubble(b, 'say'); };
47 | primTable['say:duration:elapsed:from:'] = function(b) { showBubbleAndWait(b, 'say'); };
48 | primTable['think:'] = function(b) { showBubble(b, 'think'); };
49 | primTable['think:duration:elapsed:from:'] = function(b) { showBubbleAndWait(b, 'think'); };
50 | };
51 |
52 | LooksPrims.prototype.primShow = function(b) {
53 | interp.targetSprite().setVisible(true);
54 | interp.redraw();
55 | };
56 |
57 | LooksPrims.prototype.primHide = function(b) {
58 | interp.targetSprite().setVisible(false);
59 | interp.redraw();
60 | };
61 |
62 | LooksPrims.prototype.primNextCostume = function(b) {
63 | interp.targetSprite().showCostume(interp.targetSprite().currentCostumeIndex + 1);
64 | interp.redraw();
65 | };
66 |
67 | LooksPrims.prototype.primShowCostume = function(b) {
68 | var s = interp.targetSprite();
69 | if (s == null) return;
70 | var arg = interp.arg(b, 0);
71 | if (typeof(arg) == 'number') {
72 | s.showCostume(arg - 1);
73 | } else {
74 | if ((arg == 'CAMERA') || (arg == 'CAMERA - MIRROR')) {
75 | s.showCostumeNamed(arg);
76 | return;
77 | }
78 | var i = s.indexOfCostumeNamed(arg);
79 | if (i >= 0) {
80 | s.showCostume(i);
81 | } else {
82 | var n = parseInt(arg, 10);
83 | if (n === n) { // if n is not NaN
84 | s.showCostume(n - 1);
85 | } else {
86 | return; // arg did not match a costume name nor is a valid number
87 | }
88 | }
89 | }
90 | if (s.visible) interp.redraw();
91 | };
92 |
93 | LooksPrims.prototype.primStartScene = function(b) {
94 | var s = runtime.stage;
95 | var arg = interp.arg(b, 0);
96 | if (typeof(arg) == 'number') {
97 | s.showCostume(arg - 1);
98 | } else {
99 | if ((arg == 'CAMERA') || (arg == 'CAMERA - MIRROR')) {
100 | s.showCostumeNamed(arg);
101 | return;
102 | }
103 | var i = s.indexOfCostumeNamed(arg);
104 | if (i >= 0) {
105 | s.showCostume(i);
106 | } else {
107 | var n = parseInt(arg, 10);
108 | if (n === n) { // fast !isNaN check
109 | s.showCostume(n - 1);
110 | } else {
111 | return; // arg did not match a costume name nor is a valid number
112 | }
113 | }
114 | }
115 | if (s.visible) interp.redraw();
116 | };
117 |
118 | LooksPrims.prototype.primCostumeNum = function(b) {
119 | var s = interp.targetSprite();
120 | return s == null ? 1 : s.currentCostumeIndex + 1;
121 | };
122 |
123 | LooksPrims.prototype.primChangeSize = function(b) {
124 | var s = interp.targetSprite();
125 | if (s == null) return;
126 | s.setSize(s.getSize() + interp.numarg(b, 0));
127 | if (s.visible) interp.redraw();
128 | };
129 |
130 | LooksPrims.prototype.primSetSize = function(b) {
131 | var s = interp.targetSprite();
132 | if (s == null) return;
133 | s.setSize(interp.numarg(b, 0));
134 | if (s.visible) interp.redraw();
135 | };
136 |
137 | LooksPrims.prototype.primSize = function(b) {
138 | var s = interp.targetSprite();
139 | if (s == null) return 100;
140 | return s.getSize();
141 | };
142 |
143 | LooksPrims.prototype.primGoFront = function(b) {
144 | var s = interp.targetSprite();
145 | runtime.reassignZ(s, null);
146 | if (s.visible) interp.redraw();
147 | };
148 |
149 | LooksPrims.prototype.primGoBack = function(b) {
150 | var s = interp.targetSprite();
151 | runtime.reassignZ(s, interp.numarg(b, 0));
152 | if(s.visible) interp.redraw();
153 | };
154 |
155 | LooksPrims.prototype.primChangeEffect = function(b) {
156 | var s = interp.targetSprite();
157 | s.filters[interp.arg(b, 0)] += interp.numarg(b, 1);
158 | s.updateFilters();
159 | };
160 |
161 | LooksPrims.prototype.primSetEffect = function(b) {
162 | var s = interp.targetSprite();
163 | s.filters[interp.arg(b, 0)] = interp.numarg(b, 1);
164 | s.updateFilters();
165 | };
166 |
167 | LooksPrims.prototype.primClearEffects = function(b) {
168 | var s = interp.targetSprite();
169 | s.resetFilters();
170 | s.updateFilters();
171 | };
172 |
173 | var showBubble = function(b, type) {
174 | var s = interp.targetSprite();
175 | if (s !== null) s.showBubble(interp.arg(b, 0), type);
176 | };
177 |
178 | var showBubbleAndWait = function(b, type) {
179 | var s = interp.targetSprite();
180 | if (s === null) return;
181 | if (interp.activeThread.firstTime) {
182 | var text = interp.arg(b, 0);
183 | var secs = interp.numarg(b, 1);
184 | s.showBubble(text, type);
185 | if (s.visible) interp.redraw();
186 | interp.startTimer(secs);
187 | } else {
188 | if (interp.checkTimer()) s.hideBubble();
189 | }
190 | };
191 |
--------------------------------------------------------------------------------
/js/primitives/MotionAndPenPrims.js:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Massachusetts Institute of Technology
2 | //
3 | // This program is free software; you can redistribute it and/or
4 | // modify it under the terms of the GNU General Public License version 2,
5 | // as published by the Free Software Foundation.
6 | //
7 | // This program is distributed in the hope that it will be useful,
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | // GNU General Public License for more details.
11 | //
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 |
16 | 'use strict';
17 |
18 | var MotionAndPenPrims = function() {};
19 |
20 | MotionAndPenPrims.prototype.addPrimsTo = function(primTable) {
21 | primTable['forward:'] = this.primMove;
22 | primTable['turnLeft:'] = this.primTurnLeft;
23 | primTable['turnRight:'] = this.primTurnRight;
24 | primTable['heading:'] = this.primSetDirection;
25 | primTable['pointTowards:'] = this.primPointTowards;
26 | primTable['gotoX:y:'] = this.primGoTo;
27 | primTable['gotoSpriteOrMouse:'] = this.primGoToSpriteOrMouse;
28 | primTable['glideSecs:toX:y:elapsed:from:'] = this.primGlide;
29 |
30 | primTable['changeXposBy:'] = this.primChangeX;
31 | primTable['xpos:'] = this.primSetX;
32 | primTable['changeYposBy:'] = this.primChangeY;
33 | primTable['ypos:'] = this.primSetY;
34 |
35 | primTable['bounceOffEdge'] = this.primBounceOffEdge;
36 | primTable['setRotationStyle'] = this.primSetRotationStyle;
37 |
38 | primTable['xpos'] = this.primXPosition;
39 | primTable['ypos'] = this.primYPosition;
40 | primTable['heading'] = this.primDirection;
41 |
42 | primTable['clearPenTrails'] = this.primClear;
43 | primTable['putPenDown'] = this.primPenDown;
44 | primTable['putPenUp'] = this.primPenUp;
45 | primTable['penColor:'] = this.primSetPenColor;
46 | primTable['setPenHueTo:'] = this.primSetPenHue;
47 | primTable['changePenHueBy:'] = this.primChangePenHue;
48 | primTable['setPenShadeTo:'] = this.primSetPenShade;
49 | primTable['changePenShadeBy:'] = this.primChangePenShade;
50 | primTable['penSize:'] = this.primSetPenSize;
51 | primTable['changePenSizeBy:'] = this.primChangePenSize;
52 |
53 | primTable['stampCostume'] = this.primStamp;
54 | primTable['stampTransparent'] = this.primStampTransparent;
55 | };
56 |
57 | MotionAndPenPrims.prototype.primMove = function(b) {
58 | var s = interp.targetSprite();
59 | var radians = (90 - s.direction) * Math.PI / 180;
60 | var d = interp.numarg(b, 0);
61 |
62 | moveSpriteTo(s, s.scratchX + d * Math.cos(radians), s.scratchY + d * Math.sin(radians));
63 | if (s.visible) interp.redraw();
64 | };
65 |
66 | MotionAndPenPrims.prototype.primTurnLeft = function(b) {
67 | var s = interp.targetSprite();
68 | var d = s.direction - interp.numarg(b, 0);
69 | s.setDirection(d);
70 | if (s.visible) interp.redraw();
71 | };
72 |
73 | MotionAndPenPrims.prototype.primTurnRight = function(b) {
74 | var s = interp.targetSprite();
75 | var d = s.direction + interp.numarg(b, 0);
76 | s.setDirection(d);
77 | if (s.visible) interp.redraw();
78 | };
79 |
80 | MotionAndPenPrims.prototype.primSetDirection = function(b) {
81 | var s = interp.targetSprite();
82 | s.setDirection(interp.numarg(b, 0));
83 | if (s.visible) interp.redraw();
84 | };
85 |
86 | MotionAndPenPrims.prototype.primPointTowards = function(b) {
87 | var s = interp.targetSprite();
88 | var p = mouseOrSpritePosition(interp.arg(b, 0));
89 | if (s == null || p == null) return;
90 | var dx = p.x - s.scratchX;
91 | var dy = p.y - s.scratchY;
92 | var angle = 90 - Math.atan2(dy, dx) * 180 / Math.PI;
93 | s.setDirection(angle);
94 | if (s.visible) interp.redraw();
95 | };
96 |
97 | MotionAndPenPrims.prototype.primGoTo = function(b) {
98 | var s = interp.targetSprite();
99 | if (s != null) moveSpriteTo(s, interp.numarg(b, 0), interp.numarg(b, 1));
100 | };
101 |
102 | MotionAndPenPrims.prototype.primGoToSpriteOrMouse = function(b) {
103 | var s = interp.targetSprite();
104 | var p = mouseOrSpritePosition(interp.arg(b, 0));
105 | if (s == null || p == null) return;
106 | moveSpriteTo(s, p.x, p.y);
107 | };
108 |
109 | MotionAndPenPrims.prototype.primGlide = function(b) {
110 | var s = interp.targetSprite();
111 | if (s == null) return;
112 | if (interp.activeThread.firstTime) {
113 | var secs = interp.numarg(b, 0);
114 | var destX = interp.numarg(b, 1);
115 | var destY = interp.numarg(b, 2);
116 | if (secs <= 0) {
117 | moveSpriteTo(s, destX, destY);
118 | return;
119 | }
120 | // record state: [0]start msecs, [1]duration, [2]startX, [3]startY, [4]endX, [5]endY
121 | interp.activeThread.tmpObj = [interp.currentMSecs, 1000 * secs, s.scratchX, s.scratchY, destX, destY];
122 | interp.startTimer(secs);
123 | } else {
124 | var state = interp.activeThread.tmpObj;
125 | if (!interp.checkTimer()) {
126 | // in progress: move to intermediate position along path
127 | var frac = (interp.currentMSecs - state[0]) / state[1];
128 | var newX = state[2] + frac * (state[4] - state[2]);
129 | var newY = state[3] + frac * (state[5] - state[3]);
130 | moveSpriteTo(s, newX, newY);
131 | } else {
132 | // finished: move to final position and clear state
133 | moveSpriteTo(s, state[4], state[5]);
134 | interp.activeThread.tmpObj = null;
135 | }
136 | }
137 | };
138 |
139 | MotionAndPenPrims.prototype.primChangeX = function(b) {
140 | var s = interp.targetSprite();
141 | if (s != null) moveSpriteTo(s, s.scratchX + interp.numarg(b, 0), s.scratchY);
142 | };
143 |
144 | MotionAndPenPrims.prototype.primSetX = function(b) {
145 | var s = interp.targetSprite();
146 | if (s != null) moveSpriteTo(s, interp.numarg(b, 0), s.scratchY);
147 | };
148 |
149 | MotionAndPenPrims.prototype.primChangeY = function(b) {
150 | var s = interp.targetSprite();
151 | if (s != null) moveSpriteTo(s, s.scratchX, s.scratchY + interp.numarg(b, 0));
152 | };
153 |
154 | MotionAndPenPrims.prototype.primSetY = function(b) {
155 | var s = interp.targetSprite();
156 | if (s != null) moveSpriteTo(s, s.scratchX, interp.numarg(b, 0));
157 | };
158 |
159 | MotionAndPenPrims.prototype.primBounceOffEdge = function(b) {
160 | var s = interp.targetSprite();
161 | if (s == null) return;
162 | if (!turnAwayFromEdge(s)) return;
163 | ensureOnStageOnBounce(s);
164 | if (s.visible) interp.redraw();
165 | };
166 |
167 | MotionAndPenPrims.prototype.primSetRotationStyle = function(b) {
168 | var s = interp.targetSprite();
169 | if (s == null) return;
170 | var request = interp.arg(b, 0);
171 | var rotationStyle = 'normal';
172 | if (request == 'all around') rotationStyle = 'normal';
173 | else if (request == 'left-right') rotationStyle = 'leftRight';
174 | else if (request == 'none') rotationStyle = 'none';
175 | s.setRotationStyle(rotationStyle);
176 | };
177 |
178 | MotionAndPenPrims.prototype.primXPosition = function(b) {
179 | var s = interp.targetSprite();
180 | return s != null ? s.scratchX : 0;
181 | };
182 |
183 | MotionAndPenPrims.prototype.primYPosition = function(b) {
184 | var s = interp.targetSprite();
185 | return s != null ? s.scratchY : 0;
186 | };
187 |
188 | MotionAndPenPrims.prototype.primDirection = function(b) {
189 | var s = interp.targetSprite();
190 | return s != null ? s.direction : 0;
191 | };
192 |
193 | MotionAndPenPrims.prototype.primClear = function(b) {
194 | runtime.stage.clearPenStrokes();
195 | interp.redraw();
196 | };
197 |
198 | MotionAndPenPrims.prototype.primPenDown = function(b) {
199 | var s = interp.targetSprite();
200 | if (s != null) s.penIsDown = true;
201 | stroke(s, s.scratchX, s.scratchY, s.scratchX + 0.2, s.scratchY + 0.2);
202 | interp.redraw();
203 | };
204 |
205 | MotionAndPenPrims.prototype.primPenUp = function(b) {
206 | var s = interp.targetSprite();
207 | if (s != null) s.penIsDown = false;
208 | };
209 |
210 | MotionAndPenPrims.prototype.primSetPenColor = function(b) {
211 | var s = interp.targetSprite();
212 | if (s != null) s.setPenColor(interp.numarg(b, 0));
213 | };
214 |
215 | MotionAndPenPrims.prototype.primSetPenHue = function(b) {
216 | var s = interp.targetSprite();
217 | if (s != null) s.setPenHue(interp.numarg(b, 0));
218 | };
219 |
220 | MotionAndPenPrims.prototype.primChangePenHue = function(b) {
221 | var s = interp.targetSprite();
222 | if (s != null) s.setPenHue(s.penHue + interp.numarg(b, 0));
223 | };
224 |
225 | MotionAndPenPrims.prototype.primSetPenShade = function(b) {
226 | var s = interp.targetSprite();
227 | if (s != null) s.setPenShade(interp.numarg(b, 0));
228 | };
229 |
230 | MotionAndPenPrims.prototype.primChangePenShade = function(b) {
231 | var s = interp.targetSprite();
232 | if (s != null) s.setPenShade(s.penShade + interp.numarg(b, 0));
233 | };
234 |
235 | MotionAndPenPrims.prototype.primSetPenSize = function(b) {
236 | var s = interp.targetSprite();
237 | var w = Math.max(0, Math.min(interp.numarg(b, 0), 100));
238 | if (s != null) s.penWidth = w;
239 | };
240 |
241 | MotionAndPenPrims.prototype.primChangePenSize = function(b) {
242 | var s = interp.targetSprite();
243 | var w = Math.max(0, Math.min(s.penWidth + interp.numarg(b, 0), 100));
244 | if (s != null) s.penWidth = w;
245 | };
246 |
247 | MotionAndPenPrims.prototype.primStamp = function(b) {
248 | var s = interp.targetSprite();
249 | s.stamp(runtime.stage.lineCache, 100);
250 | };
251 |
252 | MotionAndPenPrims.prototype.primStampTransparent = function(b) {
253 | var s = interp.targetSprite();
254 | var transparency = Math.max(0, Math.min(interp.numarg(b, 0), 100));
255 | var alpha = 100 - transparency;
256 | s.stamp(runtime.stage.lineCache, alpha);
257 | };
258 |
259 | // Helpers
260 | var stroke = function(s, oldX, oldY, newX, newY) {
261 | runtime.stage.stroke([oldX, oldY], [newX, newY], s.penWidth, s.penColorCache);
262 | interp.redraw();
263 | };
264 |
265 | var mouseOrSpritePosition = function(arg) {
266 | if (arg == '_mouse_') {
267 | var w = runtime.stage;
268 | return new Point(runtime.mousePos[0], runtime.mousePos[1]);
269 | } else {
270 | var s = runtime.spriteNamed(arg);
271 | if (s == null) return null;
272 | return new Point(s.scratchX, s.scratchY);
273 | }
274 | return null;
275 | };
276 |
277 | var moveSpriteTo = function(s, newX, newY) {
278 | var oldX = s.scratchX;
279 | var oldY = s.scratchY;
280 | s.setXY(newX, newY);
281 | s.keepOnStage();
282 | if (s.penIsDown) stroke(s, oldX, oldY, s.scratchX, s.scratchY);
283 | if (s.penIsDown || s.visible) interp.redraw();
284 | };
285 |
286 | var turnAwayFromEdge = function(s) {
287 | // turn away from the nearest edge if it's close enough; otherwise do nothing
288 | // Note: comparisions are in the stage coordinates, with origin (0, 0)
289 | // use bounding rect of the sprite to account for costume rotation and scale
290 | var r = s.getRect();
291 | // measure distance to edges
292 | var d1 = Math.max(0, r.left);
293 | var d2 = Math.max(0, r.top);
294 | var d3 = Math.max(0, 480 - r.right);
295 | var d4 = Math.max(0, 360 - r.bottom);
296 | // find the nearest edge
297 | var e = 0, minDist = 100000;
298 | if (d1 < minDist) { minDist = d1; e = 1; }
299 | if (d2 < minDist) { minDist = d2; e = 2; }
300 | if (d3 < minDist) { minDist = d3; e = 3; }
301 | if (d4 < minDist) { minDist = d4; e = 4; }
302 | if (minDist > 0) return false; // not touching to any edge
303 | // point away from nearest edge
304 | var radians = (90 - s.direction) * Math.PI / 180;
305 | var dx = Math.cos(radians);
306 | var dy = -Math.sin(radians);
307 | if (e == 1) { dx = Math.max(0.2, Math.abs(dx)); }
308 | if (e == 2) { dy = Math.max(0.2, Math.abs(dy)); }
309 | if (e == 3) { dx = 0 - Math.max(0.2, Math.abs(dx)); }
310 | if (e == 4) { dy = 0 - Math.max(0.2, Math.abs(dy)); }
311 | var newDir = Math.atan2(dy, dx) * 180 / Math.PI + 90;
312 | s.direction = newDir;
313 | return true;
314 | };
315 |
316 | var ensureOnStageOnBounce = function(s) {
317 | var r = s.getRect();
318 | if (r.left < 0) moveSpriteTo(s, s.scratchX - r.left, s.scratchY);
319 | if (r.top < 0) moveSpriteTo(s, s.scratchX, s.scratchY + r.top);
320 | if (r.right > 480) {
321 | moveSpriteTo(s, s.scratchX - (r.right - 480), s.scratchY);
322 | }
323 | if (r.bottom > 360) {
324 | moveSpriteTo(s, s.scratchX, s.scratchY + (r.bottom - 360));
325 | }
326 | };
327 |
--------------------------------------------------------------------------------
/js/primitives/Primitives.js:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Massachusetts Institute of Technology
2 | //
3 | // This program is free software; you can redistribute it and/or
4 | // modify it under the terms of the GNU General Public License version 2,
5 | // as published by the Free Software Foundation.
6 | //
7 | // This program is distributed in the hope that it will be useful,
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | // GNU General Public License for more details.
11 | //
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 |
16 | // Scratch HTML5 Player
17 | // Primitives.js
18 | // Tim Mickel, July 2011
19 |
20 | // Provides the basic primitives for the interpreter and loads in the more
21 | // complicated primitives, e.g. MotionAndPenPrims.
22 |
23 | 'use strict';
24 |
25 | var Primitives = function() {}
26 |
27 | Primitives.prototype.addPrimsTo = function(primTable) {
28 | // Math primitives
29 | primTable['+'] = function(b) { return interp.numarg(b, 0) + interp.numarg(b, 1); };
30 | primTable['-'] = function(b) { return interp.numarg(b, 0) - interp.numarg(b, 1); };
31 | primTable['*'] = function(b) { return interp.numarg(b, 0) * interp.numarg(b, 1); };
32 | primTable['/'] = function(b) { return interp.numarg(b, 0) / interp.numarg(b, 1); };
33 | primTable['%'] = this.primModulo;
34 | primTable['randomFrom:to:'] = this.primRandom;
35 | primTable['<'] = function(b) { return (interp.numarg(b, 0) < interp.numarg(b, 1)); };
36 | primTable['='] = function(b) { return (interp.arg(b, 0) == interp.arg(b, 1)); };
37 | primTable['>'] = function(b) { return (interp.numarg(b, 0) > interp.numarg(b, 1)); };
38 | primTable['&'] = function(b) { return interp.boolarg(b, 0) && interp.boolarg(b, 1); };
39 | primTable['|'] = function(b) { return interp.boolarg(b, 0) || interp.boolarg(b, 1); };
40 | primTable['not'] = function(b) { return !interp.boolarg(b, 0); };
41 | primTable['abs'] = function(b) { return Math.abs(interp.numarg(b, 0)); };
42 | primTable['sqrt'] = function(b) { return Math.sqrt(interp.numarg(b, 0)); };
43 |
44 | primTable['\\\\'] = this.primModulo;
45 | primTable['rounded'] = function(b) { return Math.round(interp.numarg(b, 0)); };
46 | primTable['computeFunction:of:'] = this.primMathFunction;
47 |
48 | // String primitives
49 | primTable['concatenate:with:'] = function(b) { return '' + interp.arg(b, 0) + interp.arg(b, 1); };
50 | primTable['letter:of:'] = this.primLetterOf;
51 | primTable['stringLength:'] = function(b) { return interp.arg(b, 0).length; };
52 |
53 | new VarListPrims().addPrimsTo(primTable);
54 | new MotionAndPenPrims().addPrimsTo(primTable);
55 | new LooksPrims().addPrimsTo(primTable);
56 | new SensingPrims().addPrimsTo(primTable);
57 | new SoundPrims().addPrimsTo(primTable);
58 | }
59 |
60 | Primitives.prototype.primRandom = function(b) {
61 | var n1 = interp.numarg(b, 0);
62 | var n2 = interp.numarg(b, 1);
63 | var low = n1 <= n2 ? n1 : n2;
64 | var hi = n1 <= n2 ? n2 : n1;
65 | if (low == hi) return low;
66 | // if both low and hi are ints, truncate the result to an int
67 | if (Math.floor(low) == low && Math.floor(hi) == hi) {
68 | return low + Math.floor(Math.random() * (hi + 1 - low));
69 | }
70 | return Math.random() * (hi - low) + low;
71 | }
72 |
73 | Primitives.prototype.primLetterOf = function(b) {
74 | var s = interp.arg(b, 1);
75 | var i = interp.numarg(b, 0) - 1;
76 | if (i < 0 || i >= s.length) return '';
77 | return s.charAt(i);
78 | }
79 |
80 | Primitives.prototype.primModulo = function(b) {
81 | var dividend = interp.numarg(b, 1);
82 | var n = interp.numarg(b, 0) % dividend;
83 | if (n / dividend < 0) n += dividend;
84 | return n;
85 | }
86 |
87 | Primitives.prototype.primMathFunction = function(b) {
88 | var op = interp.arg(b, 0);
89 | var n = interp.numarg(b, 1);
90 | switch(op) {
91 | case 'abs': return Math.abs(n);
92 | case 'sqrt': return Math.sqrt(n);
93 | case 'sin': return Math.sin(n * Math.PI / 180);
94 | case 'cos': return Math.cos(n * Math.PI / 180);
95 | case 'tan': return Math.tan(n * Math.PI / 180);
96 | case 'asin': return Math.asin(n) * 180 / Math.PI;
97 | case 'acos': return Math.acos(n) * 180 / Math.PI;
98 | case 'atan': return Math.atan(n) * 180 / Math.PI;
99 | case 'ln': return Math.log(n);
100 | case 'log': return Math.log(n) / Math.LN10;
101 | case 'e ^': return Math.exp(n);
102 | case '10 ^': return Math.exp(n * Math.LN10);
103 | case 'floor': return Math.floor(n);
104 | case 'ceiling': return Math.ceil(n);
105 | }
106 | return 0;
107 | }
108 |
109 |
--------------------------------------------------------------------------------
/js/primitives/SensingPrims.js:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Massachusetts Institute of Technology
2 | //
3 | // This program is free software; you can redistribute it and/or
4 | // modify it under the terms of the GNU General Public License version 2,
5 | // as published by the Free Software Foundation.
6 | //
7 | // This program is distributed in the hope that it will be useful,
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | // GNU General Public License for more details.
11 | //
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 |
16 | 'use strict';
17 |
18 | var SensingPrims = function() {};
19 |
20 | SensingPrims.prototype.addPrimsTo = function(primTable) {
21 | primTable['touching:'] = this.primTouching;
22 | primTable['touchingColor:'] = this.primTouchingColor;
23 | primTable['color:sees:'] = this.primColorTouchingColor;
24 |
25 | primTable['doAsk'] = this.primDoAsk;
26 | primTable['answer'] = this.primAnswer;
27 |
28 | primTable['keyPressed:'] = this.primKeyPressed;
29 | primTable['mousePressed'] = function(b) { return runtime.mouseDown; };
30 | primTable['mouseX'] = function(b) { return runtime.mousePos[0]; };
31 | primTable['mouseY'] = function(b) { return runtime.mousePos[1]; };
32 | primTable['distanceTo:'] = this.primDistanceTo;
33 |
34 | primTable['getAttribute:of:'] = this.primGetAttribute;
35 |
36 | primTable['timeAndDate'] = function(b) { return runtime.getTimeString(interp.arg(b, 0)); };
37 | primTable['timestamp'] = this.primTimestamp;
38 | };
39 |
40 | SensingPrims.prototype.primTouching = function(b) {
41 | var s = interp.targetSprite();
42 | if (s == null || !s.visible) return false;
43 |
44 | var arg = interp.arg(b, 0);
45 | if (arg == '_edge_') {
46 | return false; // TODO
47 | }
48 |
49 | if (arg == '_mouse_') {
50 | return false; // TODO
51 | }
52 |
53 | var s2 = runtime.spriteNamed(arg);
54 | if (s2 == null || !s2.visible) return false;
55 |
56 | return spriteHitTest(s, s2);
57 | };
58 |
59 | SensingPrims.prototype.primTouchingColor = function(b) {
60 | var s = interp.targetSprite();
61 | if (s == null || !s.visible) return false;
62 |
63 | var color = interp.arg(b, 0);
64 |
65 | return stageColorHitTest(s, color);
66 | };
67 |
68 | SensingPrims.prototype.primColorTouchingColor = function(b) {
69 | var s = interp.targetSprite();
70 | if (s == null || !s.visible) return false;
71 |
72 | var myColor = interp.arg(b, 0);
73 | var stageColor = interp.arg(b, 1);
74 |
75 | return stageColorByColorHitTest(s, myColor, stageColor);
76 | };
77 |
78 | var spriteHitTest = function(a, b) {
79 | var hitCanvas = document.createElement('canvas');
80 | hitCanvas.width = 480;
81 | hitCanvas.height = 360;
82 | var hitTester = hitCanvas.getContext('2d');
83 | hitTester.globalCompositeOperation = 'source-over';
84 | a.stamp(hitTester, 100);
85 | hitTester.globalCompositeOperation = 'source-in';
86 | b.stamp(hitTester, 100);
87 |
88 | var aData = hitTester.getImageData(0, 0, 480, 360).data;
89 |
90 | var pxCount = aData.length;
91 | for (var i = 0; i < pxCount; i += 4) {
92 | if (aData[i+3] > 0) {
93 | return true;
94 | }
95 | }
96 | return false;
97 | };
98 |
99 | var stageColorHitTest = function(target, color) {
100 | var r, g, b;
101 | r = (color >> 16);
102 | g = (color >> 8 & 255);
103 | b = (color & 255);
104 |
105 | var targetCanvas = document.createElement('canvas');
106 | targetCanvas.width = 480;
107 | targetCanvas.height = 360;
108 | var targetTester = targetCanvas.getContext('2d');
109 | target.stamp(targetTester, 100);
110 |
111 | var stageCanvas = document.createElement('canvas');
112 | stageCanvas.width = 480;
113 | stageCanvas.height = 360;
114 | var stageContext = stageCanvas.getContext('2d');
115 |
116 | $.each(runtime.sprites, function(i, sprite) {
117 | if (sprite != target)
118 | sprite.stamp(stageContext, 100);
119 | });
120 |
121 | var hitData = stageContext.getImageData(0, 0, stageCanvas.width, stageCanvas.height).data;
122 | var meshData = targetTester.getImageData(0, 0, targetCanvas.width, targetCanvas.height).data;
123 | var pxCount = meshData.length;
124 | for (var i = 0; i < pxCount; i += 4) {
125 | if (meshData[i+3] > 0 && hitData[i] == r && hitData[i+1] == g && hitData[i+2] == b)
126 | return true;
127 | }
128 | return false;
129 | };
130 |
131 | var stageColorByColorHitTest = function(target, myColor, otherColor) {
132 | var threshold_acceptable = function(a, b, c, x, y, z) {
133 | var diff_a = Math.abs(a-x);
134 | var diff_b = Math.abs(b-y);
135 | var diff_c = Math.abs(c-z);
136 | if (diff_a + diff_b + diff_c < 100) {
137 | return true;
138 | }
139 | return false;
140 | };
141 | var targetCanvas = document.createElement('canvas');
142 | targetCanvas.width = 480;
143 | targetCanvas.height = 360;
144 | var targetTester = targetCanvas.getContext('2d');
145 | target.stamp(targetTester, 100);
146 | var targetData = targetTester.getImageData(0, 0, targetCanvas.width, targetCanvas.height).data;
147 |
148 | // Calculate RGB values of the colors - TODO thresholding
149 | //myColor = Math.abs(myColor);
150 | //otherColor = Math.abs(otherColor);
151 | var mr, mg, mb, or, og, ob;
152 | mr = (myColor >> 16);
153 | mg = (myColor >> 8 & 255);
154 | mb = (myColor & 255);
155 | or = (otherColor >> 16);
156 | og = (otherColor >> 8 & 255);
157 | ob = (otherColor & 255);
158 |
159 | // Create the hit canvas for comparison
160 | var hitCanvas = document.createElement('canvas');
161 | hitCanvas.width = 480;
162 | hitCanvas.height = 360;
163 | var hitCtx = hitCanvas.getContext('2d');
164 | $.each(runtime.sprites, function(i, sprite) {
165 | if (sprite != target) {
166 | sprite.stamp(hitCtx, 100);
167 | }
168 | });
169 |
170 | var hitData = hitCtx.getImageData(0, 0, hitCanvas.width, hitCanvas.height).data;
171 | var pxCount = targetData.length;
172 | for (var i = 0; i < pxCount; i += 4) {
173 | if (threshold_acceptable(targetData[i], targetData[i+1], targetData[i+2], mr, mg, mb) && threshold_acceptable(hitData[i], hitData[i+1], hitData[i+2], or, og, ob)) {
174 | return true;
175 | }
176 | }
177 | return false;
178 | };
179 |
180 | SensingPrims.prototype.primDoAsk= function(b) {
181 | showBubble(b, "doAsk");
182 | var s = interp.targetSprite();
183 | if (s !== null) {
184 | interp.activeThread.paused = true;
185 | s.showAsk();
186 | }
187 | };
188 |
189 | SensingPrims.prototype.primAnswer = function(b) {
190 | var s = interp.targetStage();
191 | return (s !== null ? s.askAnswer : undefined);
192 | };
193 |
194 |
195 | SensingPrims.prototype.primKeyPressed = function(b) {
196 | var key = interp.arg(b, 0);
197 | var ch = key.charCodeAt(0);
198 | if (ch > 127) return false;
199 | if (key == "left arrow") ch = 37;
200 | if (key == "right arrow") ch = 39;
201 | if (key == "up arrow") ch = 38;
202 | if (key == "down arrow") ch = 40;
203 | if (key == "space") ch = 32;
204 | return (typeof(runtime.keysDown[ch]) != 'undefined');
205 | };
206 |
207 | SensingPrims.prototype.primDistanceTo = function(b) {
208 | var s = interp.targetSprite();
209 | var p = mouseOrSpritePosition(interp.arg(b, 0));
210 | if (s == null || p == null) return 0;
211 | var dx = p.x - s.scratchX;
212 | var dy = p.y - s.scratchY;
213 | return Math.sqrt((dx * dx) + (dy * dy));
214 | };
215 |
216 | SensingPrims.prototype.primGetAttribute = function(b) {
217 | var attr = interp.arg(b, 0);
218 | var targetSprite = runtime.spriteNamed(interp.arg(b, 1));
219 | if (targetSprite == null) return 0;
220 | if (attr == 'x position') return targetSprite.scratchX;
221 | if (attr == 'y position') return targetSprite.scratchY;
222 | if (attr == 'direction') return targetSprite.direction;
223 | if (attr == 'costume #') return targetSprite.currentCostumeIndex + 1;
224 | if (attr == 'costume name') return targetSprite.costumes[targetSprite.currentCostumeIndex]['costumeName'];
225 | if (attr == 'size') return targetSprite.getSize();
226 | if (attr == 'volume') return targetSprite.volume;
227 | return 0;
228 | };
229 |
230 | SensingPrims.prototype.primTimeDate = function(b) {
231 | var dt = interp.arg(b, 0);
232 | var now = new Date();
233 | if (dt == 'year') return now.getFullYear();
234 | if (dt == 'month') return now.getMonth()+1;
235 | if (dt == 'date') return now.getDate();
236 | if (dt == 'day of week') return now.getDay()+1;
237 | if (dt == 'hour') return now.getHours();
238 | if (dt == 'minute') return now.getMinutes();
239 | if (dt == 'second') return now.getSeconds();
240 | return 0;
241 | };
242 |
243 | SensingPrims.prototype.primTimestamp = function(b) {
244 | var now = new Date();
245 | var epoch = new Date(2000, 0, 1);
246 | var dst = now.getTimezoneOffset() - epoch.getTimezoneOffset();
247 | var msSince = now.getTime() - epoch.getTime();
248 | msSince -= dst * 60000;
249 | return msSince / 86400000;
250 | };
251 |
252 | // Helpers
253 | SensingPrims.prototype.mouseOrSpritePosition = function(arg) {
254 | if (arg == "_mouse_") {
255 | var w = runtime.stage;
256 | return new Point(runtime.mousePos[0], runtime.mousePos[1]);
257 | } else {
258 | var s = runtime.spriteNamed(arg);
259 | if (s == null) return null;
260 | return new Point(s.scratchX, s.scratchY);
261 | }
262 | return null;
263 | };
264 |
--------------------------------------------------------------------------------
/js/primitives/SoundPrims.js:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Massachusetts Institute of Technology
2 | //
3 | // This program is free software; you can redistribute it and/or
4 | // modify it under the terms of the GNU General Public License version 2,
5 | // as published by the Free Software Foundation.
6 | //
7 | // This program is distributed in the hope that it will be useful,
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | // GNU General Public License for more details.
11 | //
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 |
16 | 'use strict';
17 |
18 | var SoundPrims = function() {};
19 |
20 | SoundPrims.prototype.addPrimsTo = function(primTable) {
21 | primTable['playSound:'] = this.primPlaySound;
22 | primTable['doPlaySoundAndWait'] = this.primPlaySoundUntilDone;
23 | primTable['stopAllSounds'] = this.primStopAllSounds;
24 |
25 | primTable['playDrum'] = this.primPlayDrum;
26 | primTable['rest:elapsed:from:'] = this.primPlayRest;
27 | primTable['noteOn:duration:elapsed:from:'] = this.primPlayNote;
28 | primTable['instrument:'] = this.primSetInstrument;
29 |
30 | /*primTable['changeVolumeBy:'] = this.primChangeVolume;
31 | primTable['setVolumeTo:'] = this.primSetVolume;
32 | primTable['volume'] = this.primVolume;*/
33 |
34 | primTable['changeTempoBy:'] = function(b) { runtime.stage.data.tempoBPM = runtime.stage.data.tempoBPM + interp.arg(b, 0); };
35 | primTable['setTempoTo:'] = function(b) { runtime.stage.data.tempoBPM = interp.arg(b, 0); };
36 | primTable['tempo'] = function(b) { return runtime.stage.data.tempoBPM; };
37 | };
38 |
39 | var playSound = function(snd) {
40 | if (snd.source) {
41 | // If this particular sound is already playing, stop it.
42 | snd.source.disconnect();
43 | snd.source = null;
44 | }
45 |
46 | snd.source = runtime.audioContext.createBufferSource();
47 | snd.source.buffer = snd.buffer;
48 | snd.source.connect(runtime.audioGain);
49 |
50 | // Track the sound's completion state
51 | snd.source.done = false;
52 | snd.source.finished = function() {
53 | // Remove from the active audio list and disconnect the source from
54 | // the sound dictionary.
55 | var i = runtime.audioPlaying.indexOf(snd);
56 | if (i > -1 && runtime.audioPlaying[i].source != null) {
57 | runtime.audioPlaying[i].source.done = true;
58 | runtime.audioPlaying[i].source = null;
59 | runtime.audioPlaying.splice(i, 1);
60 | }
61 | }
62 | window.setTimeout(snd.source.finished, snd.buffer.duration * 1000);
63 | // Add the global list of playing sounds and start playing.
64 | runtime.audioPlaying.push(snd);
65 | snd.source.start();
66 | return snd.source;
67 | };
68 |
69 | var playDrum = function(drum, secs, client) {
70 | var player = SoundBank.getDrumPlayer(drum, secs);
71 | player.client = client;
72 | player.setDuration(secs);
73 | var source = runtime.audioContext.createScriptProcessor(4096, 1, 1);
74 | source.onaudioprocess = function(e) { player.writeSampleData(e); };
75 | source.soundPlayer = player;
76 | source.connect(runtime.audioGain);
77 | runtime.notesPlaying.push(source);
78 | source.finished = function() {
79 | var i = runtime.notesPlaying.indexOf(source);
80 | if (i > -1 && runtime.notesPlaying[i] != null) {
81 | runtime.notesPlaying.splice(i, 1);
82 | }
83 | }
84 | window.setTimeout(source.finished, secs * 1000);
85 | return player;
86 | };
87 |
88 | var playNote = function(instrument, midiKey, secs, client) {
89 | var player = SoundBank.getNotePlayer(instrument, midiKey);
90 | player.client = client;
91 | player.setNoteAndDuration(midiKey, secs);
92 | var source = runtime.audioContext.createScriptProcessor(4096, 1, 1);
93 | source.onaudioprocess = function(e) { player.writeSampleData(e); };
94 | source.connect(runtime.audioGain);
95 | runtime.notesPlaying.push(source);
96 | source.finished = function() {
97 | var i = runtime.notesPlaying.indexOf(source);
98 | if (i > -1 && runtime.notesPlaying[i] != null) {
99 | runtime.notesPlaying.splice(i, 1);
100 | }
101 | }
102 | window.setTimeout(source.finished, secs * 1000);
103 | return player;
104 | };
105 |
106 | var stopAllSounds = function() {
107 | var oldPlaying = runtime.audioPlaying;
108 | runtime.audioPlaying = [];
109 | for (var s = 0; s < oldPlaying.length; s++) {
110 | if (oldPlaying[s].source) {
111 | oldPlaying[s].source.disconnect();
112 | oldPlaying[s].source.finished();
113 | }
114 | }
115 |
116 | var oldPlaying = runtime.notesPlaying;
117 | runtime.notesPlaying = [];
118 | for (var s = 0; s < oldPlaying.length; s++) {
119 | if (oldPlaying[s]) {
120 | oldPlaying[s].disconnect();
121 | oldPlaying[s].finished();
122 | }
123 | }
124 | };
125 |
126 | SoundPrims.prototype.primPlaySound = function(b) {
127 | var s = interp.targetSprite();
128 | if (s == null) return;
129 | var snd = s.soundNamed(interp.arg(b, 0));
130 | if (snd != null) playSound(snd);
131 | };
132 |
133 | SoundPrims.prototype.primPlaySoundUntilDone = function(b) {
134 | var activeThread = interp.activeThread;
135 | if (activeThread.firstTime) {
136 | var snd = interp.targetSprite().soundNamed(interp.arg(b, 0));
137 | if (snd == null) return;
138 | activeThread.tmpObj = playSound(snd);
139 | activeThread.firstTime = false;
140 | }
141 | var player = activeThread.tmpObj;
142 | if (player == null || player.done || player.playbackState == 3) {
143 | activeThread.tmpObj = null;
144 | activeThread.firstTime = true;
145 | } else {
146 | interp.yield = true;
147 | }
148 | };
149 |
150 | var beatsToSeconds = function(beats) {
151 | return beats * 60 / runtime.stage.data.tempoBPM;
152 | };
153 |
154 | SoundPrims.prototype.primPlayNote = function(b) {
155 | var s = interp.targetSprite();
156 | if (s == null) return;
157 | if (interp.activeThread.firstTime) {
158 | var key = interp.numarg(b, 0);
159 | var secs = beatsToSeconds(interp.numarg(b, 1));
160 | playNote(s.instrument, key, secs, s);
161 | interp.startTimer(secs);
162 | } else {
163 | interp.checkTimer();
164 | }
165 | };
166 |
167 | SoundPrims.prototype.primPlayDrum = function(b) {
168 | var s = interp.targetSprite();
169 | if (s == null) return;
170 | if (interp.activeThread.firstTime) {
171 | var drum = Math.round(interp.numarg(b, 0));
172 | var secs = beatsToSeconds(interp.numarg(b, 1));
173 | playDrum(drum, secs, s);
174 | interp.startTimer(secs);
175 | } else {
176 | interp.checkTimer();
177 | }
178 | };
179 |
180 | SoundPrims.prototype.primPlayRest = function(b) {
181 | var s = interp.targetSprite();
182 | if (s == null) return;
183 | if (interp.activeThread.firstTime) {
184 | var secs = beatsToSeconds(interp.numarg(b, 0));
185 | interp.startTimer(secs);
186 | } else {
187 | interp.checkTimer();
188 | }
189 | };
190 |
191 | SoundPrims.prototype.primSetInstrument = function(b) {
192 | var s = interp.targetSprite();
193 | if (s != null) s.instrument = interp.arg(b, 0);
194 | };
195 |
196 | SoundPrims.prototype.primStopAllSounds = function(b) {
197 | stopAllSounds();
198 | };
199 |
200 | SoundPrims.prototype.primChangeVolume = function(b) {
201 | var s = interp.targetSprite();
202 | if (s != null) s.volume += interp.numarg(b, 0);
203 | };
204 |
205 | SoundPrims.prototype.primSetVolume = function(b) {
206 | var s = interp.targetSprite();
207 | if (s != null) s.volume = interp.numarg(b, 0);
208 | };
209 |
210 | SoundPrims.prototype.primVolume = function(b) {
211 | var s = interp.targetSprite();
212 | return s != null ? s.volume : 0;
213 | };
214 |
--------------------------------------------------------------------------------
/js/primitives/VarListPrims.js:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Massachusetts Institute of Technology
2 | //
3 | // This program is free software; you can redistribute it and/or
4 | // modify it under the terms of the GNU General Public License version 2,
5 | // as published by the Free Software Foundation.
6 | //
7 | // This program is distributed in the hope that it will be useful,
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | // GNU General Public License for more details.
11 | //
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 |
16 | 'use strict';
17 |
18 | var VarListPrims = function() {}
19 |
20 | VarListPrims.prototype.addPrimsTo = function(primTable) {
21 | // Variable primitives
22 | primTable['readVariable'] = this.primReadVar;
23 | primTable['setVar:to:'] = this.primSetVar;
24 | primTable['changeVar:by:'] = this.primChangeVar;
25 | primTable['hideVariable:'] = this.primHideVar;
26 | primTable['showVariable:'] = this.primShowVar;
27 |
28 | // List primitives
29 | primTable['contentsOfList:'] = this.primReadList;
30 | primTable['append:toList:'] = this.primListAppend;
31 | primTable['deleteLine:ofList:'] = this.primListDeleteLine;
32 | primTable['insert:at:ofList:'] = this.primListInsertAt;
33 | primTable['setLine:ofList:to:'] = this.primListSetLine;
34 | primTable['lineCountOfList:'] = this.primListLength;
35 | primTable['getLine:ofList:'] = this.primListGetLine;
36 | primTable['list:contains:'] = this.primListContains;
37 | primTable['hideList:'] = this.primHideList;
38 | primTable['showList:'] = this.primShowList;
39 | };
40 |
41 | // Variable primitive implementations
42 |
43 | VarListPrims.prototype.primReadVar = function(b) {
44 | var s = interp.targetSprite();
45 | if (s == null) return;
46 | var targetVar = interp.arg(b, 0);
47 | if (targetVar in s.variables) {
48 | return s.variables[targetVar];
49 | } else if (targetVar in runtime.stage.variables) {
50 | return runtime.stage.variables[targetVar];
51 | }
52 | };
53 |
54 | VarListPrims.prototype.primSetVar = function(b) {
55 | var s = interp.targetSprite();
56 | if (s == null) return;
57 | var targetVar = interp.arg(b, 0);
58 | if (targetVar in s.variables) {
59 | s.variables[targetVar] = interp.arg(b, 1);
60 | } else if (targetVar in runtime.stage.variables) {
61 | runtime.stage.variables[targetVar] = interp.arg(b, 1);
62 | }
63 | };
64 |
65 | VarListPrims.prototype.primChangeVar = function(b) {
66 | var s = interp.targetSprite();
67 | if (s == null) return;
68 | var targetVar = interp.arg(b, 0);
69 | if (targetVar in s.variables) {
70 | s.variables[targetVar] = parseFloat(s.variables[targetVar]) + interp.numarg(b, 1);
71 | } else if (targetVar in runtime.stage.variables) {
72 | runtime.stage.variables[targetVar] = parseFloat(runtime.stage.variables[targetVar]) + interp.numarg(b, 1);
73 | }
74 | };
75 |
76 | VarListPrims.prototype.primHideVar = function(b) {
77 | var targetVar = interp.arg(b, 0), targetSprite = interp.targetSprite().objName;
78 | for (var r = 0; r < runtime.reporters.length; r++) {
79 | if (runtime.reporters[r].cmd == 'getVar:' && runtime.reporters[r].param == targetVar && (runtime.reporters[r].target == targetSprite || runtime.reporters[r].target == 'Stage')) {
80 | runtime.reporters[r].visible = false;
81 | return;
82 | }
83 | }
84 | };
85 |
86 | VarListPrims.prototype.primShowVar = function(b) {
87 | var targetVar = interp.arg(b, 0), targetSprite = interp.targetSprite().objName;
88 | for (var r = 0; r < runtime.reporters.length; r++) {
89 | if (runtime.reporters[r].cmd == 'getVar:' && runtime.reporters[r].param == targetVar && (runtime.reporters[r].target == targetSprite || runtime.reporters[r].target == 'Stage')) {
90 | runtime.reporters[r].visible = true;
91 | return;
92 | }
93 | }
94 | };
95 |
96 | // List primitive implementations
97 |
98 | // Take a list name and target sprite and return the JS list itself
99 | function findList(targetSprite, listName) {
100 | if (targetSprite == null) targetSprite = runtime.stage;
101 | if (listName in targetSprite.lists) {
102 | return targetSprite.lists[listName].contents;
103 | } else if (listName in runtime.stage.lists) {
104 | return runtime.stage.lists[listName].contents;
105 | }
106 | return null;
107 | }
108 |
109 | VarListPrims.prototype.primReadList = function(b) {
110 | var list = findList(interp.targetSprite(), interp.arg(b, 0));
111 | if (list) {
112 | var allOne = list.map(function(val) { return val.length; }).reduce(function(old,val) { return old + val; }, 0) === list.length;
113 | return list.join(allOne ? '' : ' ');
114 | }
115 | };
116 |
117 | VarListPrims.prototype.primListAppend = function(b) {
118 | var list = findList(interp.targetSprite(), interp.arg(b, 1));
119 | if (list) list.push(interp.arg(b, 0));
120 | };
121 |
122 | VarListPrims.prototype.primListDeleteLine = function(b) {
123 | var list = findList(interp.targetSprite(), interp.arg(b, 1));
124 | if (!list) return;
125 | var line = interp.arg(b, 0);
126 | if (line == 'all' || list.length == 0) {
127 | list.length = 0;
128 | } else if (line == 'last') {
129 | list.splice(list.length - 1, 1);
130 | } else if (parseInt(line, 10) - 1 in list) {
131 | list.splice(parseInt(line, 10) - 1, 1);
132 | }
133 | };
134 |
135 | VarListPrims.prototype.primListInsertAt = function(b) {
136 | var list = findList(interp.targetSprite(), interp.arg(b, 2));
137 | if (!list) return;
138 | var newItem = interp.arg(b, 0);
139 |
140 | var position = interp.arg(b, 1);
141 | if (position == 'last') {
142 | position = list.length;
143 | } else if (position == 'random') {
144 | position = Math.round(Math.random() * list.length);
145 | } else {
146 | position = parseInt(position, 10) - 1;
147 | }
148 | if (position > list.length) return;
149 |
150 | list.splice(position, 0, newItem);
151 | };
152 |
153 | VarListPrims.prototype.primListSetLine = function(b) {
154 | var list = findList(interp.targetSprite(), interp.arg(b, 1));
155 | if (!list) return;
156 | var newItem = interp.arg(b, 2);
157 | var position = interp.arg(b, 0);
158 |
159 | if (position == 'last') {
160 | position = list.length - 1;
161 | } else if (position == 'random') {
162 | position = Math.floor(Math.random() * list.length);
163 | } else {
164 | position = parseInt(position, 10) - 1;
165 | }
166 |
167 | if (position > list.length - 1) return;
168 | list[position] = newItem;
169 | };
170 |
171 | VarListPrims.prototype.primListLength = function(b) {
172 | var list = findList(interp.targetSprite(), interp.arg(b, 0));
173 | if (!list) return 0;
174 | return list.length;
175 | };
176 |
177 | VarListPrims.prototype.primListGetLine = function(b) {
178 | var list = findList(interp.targetSprite(), interp.arg(b, 1));
179 | if (!list) return 0;
180 | var line = interp.arg(b, 0);
181 | if (list.length == 0) return 0;
182 | if (line == 'random') line = Math.round(Math.random() * list.length);
183 | else if (line == 'last') line = list.length;
184 | else if (list.length < line) return 0;
185 | return list[line - 1];
186 | };
187 |
188 | VarListPrims.prototype.primListContains = function(b) {
189 | var list = findList(interp.targetSprite(), interp.arg(b, 0));
190 | if (!list) return 0;
191 | var searchItem = interp.arg(b, 1);
192 | if (parseFloat(searchItem) == searchItem) searchItem = parseFloat(searchItem);
193 | return $.inArray(searchItem, list) > -1;
194 | };
195 |
196 | VarListPrims.prototype.primHideList = function(b) {
197 | var targetList = interp.arg(b, 0), targetSprite = interp.targetSprite().objName;
198 | for (var r = 0; r < runtime.reporters.length; r++) {
199 | if (runtime.reporters[r] instanceof List && runtime.reporters[r].listName == targetList && (runtime.reporters[r].target == targetSprite || runtime.reporters[r].target == 'Stage')) {
200 | runtime.reporters[r].visible = false;
201 | return;
202 | }
203 | }
204 | };
205 |
206 | VarListPrims.prototype.primShowList = function(b) {
207 | var targetList = interp.arg(b, 0), targetSprite = interp.targetSprite().objName;
208 | for (var r = 0; r < runtime.reporters.length; r++) {
209 | if (runtime.reporters[r] instanceof List && runtime.reporters[r].listName == targetList && (runtime.reporters[r].target == targetSprite || runtime.reporters[r].target == 'Stage')) {
210 | runtime.reporters[r].visible = true;
211 | return;
212 | }
213 | }
214 | };
215 |
--------------------------------------------------------------------------------
/js/sound/NotePlayer.js:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Massachusetts Institute of Technology
2 | //
3 | // This program is free software; you can redistribute it and/or
4 | // modify it under the terms of the GNU General Public License version 2,
5 | // as published by the Free Software Foundation.
6 | //
7 | // This program is distributed in the hope that it will be useful,
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | // GNU General Public License for more details.
11 | //
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 |
16 | // NotePlayer.js
17 | // Tim Mickel, 2013
18 | // Based entirely on the AS version by John Maloney
19 | //
20 | // Subclass of SoundDecoder to play notes on a sampled instrument or drum.
21 | //
22 | // A sampled instrument outputs interpolated sound samples from an array of signed,
23 | // 16-bit integers with an original sampling rate of 22050 samples/sec. The pitch is
24 | // shifted by change the step size while iterating through this array. An instrument
25 | // may also be looped so that it can be sustained and it may have a volume envelope
26 | // to control the attack and decay of the note.
27 |
28 | var NotePlayer = function(wavFileData, originalPitch, loopStart, loopEnd, env) {
29 | this.originalPitch = originalPitch || null;
30 | this.index = 0;
31 | this.samplesRemaining = 0; // determines note duration
32 |
33 | // Looping
34 | this.isLooped = false;
35 | this.loopPoint = 0; // final sample in loop
36 | this.loopLength = 0;
37 |
38 | // Volume Envelope
39 | this.envelopeValue = 1;
40 | this.samplesSinceStart = 0;
41 | this.attackEnd = 0;
42 | this.attackRate = 0;
43 | this.holdEnd = 0;
44 | this.decayRate = 1;
45 |
46 | if (wavFileData == null) wavFileData = new ArrayBuffer();
47 |
48 | var stepSize = 0.5; // default - no pitch shift
49 | var startOffset = 0;
50 | this.endOffset = wavFileData.byteLength / 2; // end of sample data
51 | var getSample = function() { return 0; } // called once at startup time
52 | this.soundData = new Uint8Array(wavFileData);
53 |
54 | if ((loopStart >= 0) && (loopStart < this.endOffset)) {
55 | this.isLooped = true;
56 | this.loopPoint = loopStart;
57 | if ((loopEnd > 0) && (loopEnd <= this.endOffset)) this.endOffset = loopEnd;
58 | this.loopLength = this.endOffset - this.loopPoint;
59 |
60 | // Compute the original pitch more exactly from the loop length:
61 | var oneCycle = 22050 / this.originalPitch;
62 | var cycles = Math.round(this.loopLength / oneCycle);
63 | this.originalPitch = 22050 / (this.loopLength / cycles);
64 | }
65 | if (env) {
66 | this.attackEnd = env[0] * 44.100;
67 | if (this.attackEnd > 0) this.attackRate = Math.pow(33000, 1 / this.attackEnd);
68 | this.holdEnd = this.attackEnd + env[1] * 44.100;
69 | var decayCount = env[2] * 44100;
70 | this.decayRate = decayCount == 0 ? 1 : Math.pow(33000, -1 / decayCount);
71 | }
72 | };
73 |
74 | NotePlayer.prototype = Object.create(SoundDecoder.prototype);
75 | NotePlayer.prototype.constructor = NotePlayer;
76 |
77 | NotePlayer.prototype.setNoteAndDuration = function(midiKey, secs) {
78 | midiKey = Math.max(0, Math.min(midiKey, 127));
79 | var pitch = 440 * Math.pow(2, (midiKey - 69) / 12); // midi key 69 is A (440 Hz)
80 | this.stepSize = pitch / (2 * this.originalPitch); // adjust for original sampling rate of 22050
81 | this.setDuration(secs);
82 | };
83 |
84 | NotePlayer.prototype.setDuration = function(secs) {
85 | this.samplesSinceStart = 0;
86 | this.samplesRemaining = 44100 * secs;
87 | if (!this.isLooped) this.samplesRemaining = Math.min(this.samplesRemaining, this.endOffset / this.stepSize);
88 | this.envelopeValue = this.attackEnd > 0 ? 1 / 33000 : 1;
89 | };
90 |
91 | NotePlayer.prototype.interpolatedSample = function() {
92 | if (this.samplesRemaining-- <= 0) { this.noteFinished(); return 0; }
93 | this.index += this.stepSize;
94 | while (this.index >= this.endOffset) {
95 | if (!this.isLooped) return 0;
96 | this.index -= this.loopLength;
97 | }
98 | var i = Math.floor(this.index);
99 | var frac = this.index - i;
100 | var curr = this.rawSample(i);
101 | var next = this.rawSample(i + 1);
102 | var sample = (curr + frac * (next - curr)) / 100000; // xxx 32000; attenuate...
103 | if (this.samplesRemaining < 1000) sample *= (this.samplesRemaining / 1000.0); // relaase phease
104 | this.updateEnvelope();
105 | return this.envelopeValue * sample;
106 | };
107 |
108 | NotePlayer.prototype.rawSample = function(sampleIndex) {
109 | if (sampleIndex >= this.endOffset) {
110 | if (!this.isLooped) return 0;
111 | sampleIndex = this.loopPoint;
112 | }
113 | var byteIndex = 2 * sampleIndex;
114 | var result = (this.soundData[byteIndex + 1] << 8) + this.soundData[byteIndex];
115 | return result <= 32767 ? result : result - 65536;
116 | };
117 |
118 | NotePlayer.prototype.updateEnvelope = function() {
119 | // Compute envelopeValue for the current sample.
120 | this.samplesSinceStart++;
121 | if (this.samplesSinceStart < this.attackEnd) {
122 | this.envelopeValue *= this.attackRate;
123 | } else if (this.samplesSinceStart == this.attackEnd) {
124 | this.envelopeValue = 1;
125 | } else if (this.samplesSinceStart > this.holdEnd) {
126 | if (this.decayRate < 1) this.envelopeValue *= this.decayRate;
127 | }
128 | };
129 |
--------------------------------------------------------------------------------
/js/sound/SoundBank.js:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Massachusetts Institute of Technology
2 | //
3 | // This program is free software; you can redistribute it and/or
4 | // modify it under the terms of the GNU General Public License version 2,
5 | // as published by the Free Software Foundation.
6 | //
7 | // This program is distributed in the hope that it will be useful,
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | // GNU General Public License for more details.
11 | //
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 |
16 | // SoundBank.js
17 | // Tim Mickel, 2013
18 | // Based on the original AS by John Maloney - Scratch 1.4 compatibility removed
19 | //
20 | // A collection of instrument and drum resources to support the note and drum commands.
21 |
22 | var SoundBank = function() {};
23 |
24 | // -----------------------------
25 | // Scratch 2.0 Instrument Definitions
26 | //------------------------------
27 |
28 | // Each instrument is an array of one or more key-span entries of the following form:
29 | //
30 | // top key of key span, sampleName, midiKey, loopStart, loopEnd, [attack, hold, decay]
31 | //
32 | // The loop points are -1 if the sound is unlooped (e.g. Marimba).
33 | // The three-element envelop array may be omitted if the instrument has no envelope.
34 | SoundBank.instruments = [
35 | [
36 | [38, 'AcousticPiano_As3', 58, 10266, 17053, [0, 100, 22]],
37 | [44, 'AcousticPiano_C4', 60, 13968, 18975, [0, 100, 20]],
38 | [51, 'AcousticPiano_G4', 67, 12200, 12370, [0, 80, 18]],
39 | [62, 'AcousticPiano_C6', 84, 13042, 13276, [0, 80, 16]],
40 | [70, 'AcousticPiano_F5', 77, 12425, 12965, [0, 40, 14]],
41 | [77, 'AcousticPiano_Ds6', 87, 12368, 12869, [0, 20, 10]],
42 | [85, 'AcousticPiano_Ds6', 87, 12368, 12869, [0, 0, 8]],
43 | [90, 'AcousticPiano_Ds6', 87, 12368, 12869, [0, 0, 6]],
44 | [96, 'AcousticPiano_D7', 98, 7454, 7606, [0, 0, 3]],
45 | [128, 'AcousticPiano_D7', 98, 7454, 7606, [0, 0, 2]]
46 | ],
47 | [
48 | [48, 'ElectricPiano_C2', 36, 15338, 17360, [0, 80, 10]],
49 | [74, 'ElectricPiano_C4', 60, 11426, 12016, [0, 40, 8]],
50 | [128, 'ElectricPiano_C4', 60, 11426, 12016, [0, 0, 6]]
51 | ],
52 | [
53 | [128, 'Organ_G2', 43, 1306, 3330]
54 | ],
55 | [
56 | [40, 'AcousticGuitar_F3', 53, 36665, 36791, [0, 0, 15]],
57 | [56, 'AcousticGuitar_F3', 53, 36665, 36791, [0, 0, 13.5]],
58 | [60, 'AcousticGuitar_F3', 53, 36665, 36791, [0, 0, 12]],
59 | [67, 'AcousticGuitar_F3', 53, 36665, 36791, [0, 0, 8.5]],
60 | [72, 'AcousticGuitar_F3', 53, 36665, 36791, [0, 0, 7]],
61 | [83, 'AcousticGuitar_F3', 53, 36665, 36791, [0, 0, 5.5]],
62 | [128, 'AcousticGuitar_F3', 53, 36665, 36791, [0, 0, 4.5]]
63 | ],
64 | [
65 | [40, 'ElectricGuitar_F3', 53, 34692, 34945, [0, 0, 15]],
66 | [56, 'ElectricGuitar_F3', 53, 34692, 34945, [0, 0, 13.5]],
67 | [60, 'ElectricGuitar_F3', 53, 34692, 34945, [0, 0, 12]],
68 | [67, 'ElectricGuitar_F3', 53, 34692, 34945, [0, 0, 8.5]],
69 | [72, 'ElectricGuitar_F3', 53, 34692, 34945, [0, 0, 7]],
70 | [83, 'ElectricGuitar_F3', 53, 34692, 34945, [0, 0, 5.5]],
71 | [128, 'ElectricGuitar_F3', 53, 34692, 34945, [0, 0, 4.5]]
72 | ],
73 | [
74 | [34, 'ElectricBass_G1', 31, 41912, 42363, [0, 0, 17]],
75 | [48, 'ElectricBass_G1', 31, 41912, 42363, [0, 0, 14]],
76 | [64, 'ElectricBass_G1', 31, 41912, 42363, [0, 0, 12]],
77 | [128, 'ElectricBass_G1', 31, 41912, 42363, [0, 0, 10]]
78 | ],
79 | [
80 | [38, 'Pizz_G2', 43, 8554, 8782, [0, 0, 5]],
81 | [45, 'Pizz_G2', 43, 8554, 8782, [0, 12, 4]],
82 | [56, 'Pizz_A3', 57, 11460, 11659, [0, 0, 4]],
83 | [64, 'Pizz_A3', 57, 11460, 11659, [0, 0, 3.2]],
84 | [72, 'Pizz_E4', 64, 17525, 17592, [0, 0, 2.8]],
85 | [80, 'Pizz_E4', 64, 17525, 17592, [0, 0, 2.2]],
86 | [128, 'Pizz_E4', 64, 17525, 17592, [0, 0, 1.5]]
87 | ],
88 | [
89 | [41, 'Cello_C2', 36, 8548, 8885],
90 | [52, 'Cello_As2', 46, 7465, 7845],
91 | [62, 'Violin_D4', 62, 10608, 11360],
92 | [75, 'Violin_A4', 69, 3111, 3314, [70, 0, 0]],
93 | [128, 'Violin_E5', 76, 2383, 2484]
94 | ],
95 | [
96 | [30, 'BassTrombone_A2_3', 45, 1357, 2360],
97 | [40, 'BassTrombone_A2_2', 45, 1893, 2896],
98 | [55, 'Trombone_B3', 59, 2646, 3897],
99 | [88, 'Trombone_B3', 59, 2646, 3897, [50, 0, 0]],
100 | [128, 'Trumpet_E5', 76, 2884, 3152]
101 | ],
102 | [
103 | [128, 'Clarinet_C4', 60, 14540, 15468]
104 | ],
105 | [
106 | [40, 'TenorSax_C3', 48, 8939, 10794],
107 | [50, 'TenorSax_C3', 48, 8939, 10794, [20, 0, 0]],
108 | [59, 'TenorSax_C3', 48, 8939, 10794, [40, 0, 0]],
109 | [67, 'AltoSax_A3', 57, 8546, 9049],
110 | [75, 'AltoSax_A3', 57, 8546, 9049, [20, 0, 0]],
111 | [80, 'AltoSax_A3', 57, 8546, 9049, [20, 0, 0]],
112 | [128, 'AltoSax_C6', 84, 1258, 1848]
113 | ],
114 | [
115 | [61, 'Flute_B5_2', 83, 1859, 2259],
116 | [128, 'Flute_B5_1', 83, 2418, 2818]
117 | ],
118 | [
119 | [128, 'WoodenFlute_C5', 72, 11426, 15724]
120 | ],
121 | [
122 | [57, 'Bassoon_C3', 48, 2428, 4284],
123 | [67, 'Bassoon_C3', 48, 2428, 4284, [40, 0, 0]],
124 | [76, 'Bassoon_C3', 48, 2428, 4284, [80, 0, 0]],
125 | [84, 'EnglishHorn_F3', 53, 7538, 8930, [40, 0, 0]],
126 | [128, 'EnglishHorn_D4', 62, 4857, 5231]
127 | ],
128 | [
129 | [39, 'Choir_F3', 53, 14007, 41281],
130 | [50, 'Choir_F3', 53, 14007, 41281, [40, 0, 0]],
131 | [61, 'Choir_F3', 53, 14007, 41281, [60, 0, 0]],
132 | [72, 'Choir_F4', 65, 16351, 46436],
133 | [128, 'Choir_F5', 77, 18440, 45391]
134 | ],
135 | [
136 | [38, 'Vibraphone_C3', 48, 6202, 6370, [0, 100, 8]],
137 | [48, 'Vibraphone_C3', 48, 6202, 6370, [0, 100, 7.5]],
138 | [59, 'Vibraphone_C3', 48, 6202, 6370, [0, 60, 7]],
139 | [70, 'Vibraphone_C3', 48, 6202, 6370, [0, 40, 6]],
140 | [78, 'Vibraphone_C3', 48, 6202, 6370, [0, 20, 5]],
141 | [86, 'Vibraphone_C3', 48, 6202, 6370, [0, 0, 4]],
142 | [128, 'Vibraphone_C3', 48, 6202, 6370, [0, 0, 3]]
143 | ],
144 | [
145 | [128, 'MusicBox_C4', 60, 14278, 14700, [0, 0, 2]]
146 | ],
147 | [
148 | [128, 'SteelDrum_D5', 74.4, -1, -1, [0, 0, 2]]
149 | ],
150 | [
151 | [128, 'Marimba_C4', 60, -1, -1]
152 | ],
153 | [
154 | [80, 'SynthLead_C4', 60, 135, 1400],
155 | [128, 'SynthLead_C6', 84, 124, 356]
156 | ],
157 | [
158 | [38, 'SynthPad_A3', 57, 4212, 88017, [50, 0, 0]],
159 | [50, 'SynthPad_A3', 57, 4212, 88017, [80, 0, 0]],
160 | [62, 'SynthPad_A3', 57, 4212, 88017, [110, 0, 0]],
161 | [74, 'SynthPad_A3', 57, 4212, 88017, [150, 0, 0]],
162 | [86, 'SynthPad_A3', 57, 4212, 88017, [200, 0, 0]],
163 | [128, 'SynthPad_C6', 84, 2575, 9202]
164 | ]
165 | ];
166 |
167 | // -----------------------------
168 | // Scratch 2.0 Drum Definitions
169 | //------------------------------
170 |
171 | // Each drum entry is an array of of the form:
172 | //
173 | // sampleName, pitchAdjust, [loopStart, loopEnd, decay]
174 | //
175 | // pitchAdjust (pitch shift in semitones) adjusts the original pitch.
176 | // The loop points and decay parameter may be omitted if the drum is unlooped.
177 | // (A few drums are looped to create several different pitched drums from one sample.)
178 | SoundBank.drums = [
179 | ['SnareDrum', 0],
180 | ['Tom', 0],
181 | ['SideStick', 0],
182 | ['Crash', -7],
183 | ['HiHatOpen', -8],
184 | ['HiHatClosed', 0],
185 | ['Tambourine', 0],
186 | ['Clap', 0],
187 | ['Claves', 0],
188 | ['WoodBlock', -4],
189 | ['Cowbell', 0],
190 | ['Triangle', -6, 16843, 17255, 2],
191 | ['Bongo', 2],
192 | ['Conga', -7, 4247, 4499, 2], // jhm decay
193 | ['Cabasa', 0],
194 | ['GuiroLong', 0],
195 | ['Vibraslap', -6],
196 | ['Cuica', -5],
197 | ];
198 |
199 | SoundBank.getNotePlayer = function(instNum, midiKey) {
200 | // Return a NotePlayer for the given Scratch 2.0 instrument number (1..21)
201 | // and MIDI key (0..127). If the instrument is out of range, use 1.
202 | var r = SoundBank.getNoteRecord(instNum - 1, midiKey);
203 | var env = r.length > 5 ? r[5] : null;
204 | return new NotePlayer(Instr.samples[r[1]], SoundBank.pitchForKey(r[2]), r[3], r[4], env);
205 | };
206 |
207 | SoundBank.getNoteRecord = function(instNum, midiKey) {
208 | // Get a note record for the given instrument number.
209 | if (instNum < 0 || instNum >= SoundBank.instruments.length) instNum = 0;
210 | var keyRanges = SoundBank.instruments[instNum];
211 | for (var r = 0; r < keyRanges.length; r++) {
212 | var topOfKeyRange = keyRanges[r][0];
213 | if (midiKey <= topOfKeyRange) return keyRanges[r];
214 | }
215 | return keyRanges[keyRanges.length - 1]; // return the note record for the top key range.
216 | };
217 |
218 | SoundBank.pitchForKey = function(midiKey) {
219 | return 440 * Math.pow(2, (midiKey - 69) / 12); // midi key 69 is A=440 Hz
220 | };
221 |
222 | SoundBank.getDrumPlayer = function(drumNum, secs) {
223 | // Return a NotePlayer for the given drum number.
224 | var entry = SoundBank.drums[drumNum - 1];
225 | if (entry == null) entry = SoundBank.drums[2];
226 | var loopStart = -1, loopEnd = -1, env = null;
227 | if (entry.length >= 4) {
228 | loopStart = entry[2];
229 | loopEnd = entry[3];
230 | }
231 | if (entry.length >= 5) env = [0, 0, entry[4]];
232 | var player = new NotePlayer(Instr.samples[entry[0]], SoundBank.pitchForKey(60), loopStart, loopEnd, env);
233 | player.setNoteAndDuration(60 + entry[1], 0);
234 | return player;
235 | };
236 |
--------------------------------------------------------------------------------
/js/sound/SoundDecoder.js:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Massachusetts Institute of Technology
2 | //
3 | // This program is free software; you can redistribute it and/or
4 | // modify it under the terms of the GNU General Public License version 2,
5 | // as published by the Free Software Foundation.
6 | //
7 | // This program is distributed in the hope that it will be useful,
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | // GNU General Public License for more details.
11 | //
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 |
16 | // SoundDecoder.js
17 | // Decode WAV Files (8-bit, 16-bit, and ADPCM) for playing by Sprites.
18 | // For best performance, this should be run only once per WAV and
19 | // the decoded buffer should be cached.
20 |
21 | // Based almost entirely on John Maloney's AS implementation.
22 |
23 | var SoundDecoder = function(wavFileData) {
24 | this.scratchSound = null;
25 |
26 | this.soundData = null;
27 | this.startOffset = 0;
28 | this.endOffset = 0;
29 | this.stepSize = 0;
30 | this.adpcmBlockSize = 0;
31 | this.bytePosition = 0;
32 | this.soundChannel = null;
33 | this.lastBufferTime = 0;
34 |
35 | this.getSample = null;
36 | this.fraction = 0.0;
37 | this.thisSample = 0;
38 |
39 | // decoder state
40 | this.sample = 0;
41 | this.index = 0;
42 | this.lastByte = -1; // -1 indicates that there is no saved lastByte
43 |
44 | this.nextSample = 0;
45 |
46 | this.info = null;
47 |
48 | getSample = this.getSample16Uncompressed;
49 | if (wavFileData != null) {
50 | var info = WAVFile.decode(wavFileData);
51 | this.info = info;
52 | this.startOffset = info.sampleDataStart;
53 | this.endOffset = this.startOffset + info.sampleDataSize;
54 | this.soundData = new Uint8Array(wavFileData.slice(this.startOffset, this.endOffset));
55 | this.stepSize = info.samplesPerSecond / 44100.0;
56 | if (info.encoding == 17) {
57 | this.adpcmBlockSize = info.adpcmBlockSize;
58 | this.getSample = this.getSampleADPCM;
59 | } else {
60 | if (info.bitsPerSample == 8) this.getSample = this.getSample8Uncompressed;
61 | if (info.bitsPerSample == 16) this.getSample = this.getSample16Uncompressed;
62 | }
63 | }
64 | };
65 |
66 | SoundDecoder.prototype.noteFinished = function() {
67 | // Called by subclasses to force ending condition to be true in writeSampleData()
68 | this.bytePosition = this.endOffset;
69 | };
70 |
71 | // Used for Notes and Drums - Web Audio API ScriptProcessorNodes use this
72 | // as a callback function to fill the buffers with sample data.
73 | SoundDecoder.prototype.writeSampleData = function(evt) {
74 | var i = 0;
75 | var output = evt.outputBuffer.getChannelData(0);
76 | //this.updateVolume();
77 | for (i = 0; i < output.length; i++) {
78 | var n = this.interpolatedSample();
79 | output[i] = n;
80 | }
81 | };
82 |
83 | // For pre-caching the samples of WAV sounds
84 | // Return a full list of samples generated by the decoder.
85 | SoundDecoder.prototype.getAllSamples = function() {
86 | var samples = [], smp = 0;
87 | smp = this.interpolatedSample();
88 | while (smp != null) {
89 | samples.push(smp);
90 | smp = this.interpolatedSample();
91 | }
92 | return samples;
93 | };
94 |
95 | // Provide the next sample for the buffer
96 | SoundDecoder.prototype.interpolatedSample = function() {
97 | this.fraction += this.stepSize;
98 | while (this.fraction >= 1.0) {
99 | this.thisSample = this.nextSample;
100 | this.nextSample = this.getSample();
101 | this.fraction -= 1.0;
102 | }
103 | if (this.nextSample == null) { return null; }
104 | var out = this.fraction == 0 ? this.thisSample : this.thisSample + this.fraction * (this.nextSample - this.thisSample);
105 | return out / 32768.0;
106 | };
107 |
108 | // 16-bit samples, big-endian
109 | SoundDecoder.prototype.getSample16Uncompressed = function() {
110 | var result = 0;
111 | if (this.bytePosition <= (this.info.sampleDataSize - 2)) {
112 | result = (this.soundData[this.bytePosition + 1] << 8) + this.soundData[this.bytePosition];
113 | if (result > 32767) result -= 65536;
114 | this.bytePosition += 2;
115 | } else {
116 | this.bytePosition = this.endOffset;
117 | result = null;
118 | }
119 | return result;
120 | };
121 |
122 | // 8-bit samples, uncompressed
123 | SoundDecoder.prototype.getSample8Uncompressed = function() {
124 | if (this.bytePosition >= this.info.sampleDataSize) return null;
125 | return (this.soundData[this.bytePosition++] - 128) << 8;
126 | };
127 |
128 | /*SoundDecoder.prototype.updateVolume = function() {
129 | if (this.client == null) {
130 | this.volume = 1.0;
131 | return;
132 | }
133 | if (this.client.volume == this.lastClientVolume) return; // optimization
134 | this.volume = Math.max(0.0, Math.min(this.client.volume / 100.0, 1.0));
135 | this.lastClientVolume = this.client.volume;
136 | }*/
137 |
138 | // Decoder for IMA ADPCM compressed sounds
139 | SoundDecoder.indexTable = [-1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8];
140 |
141 | SoundDecoder.stepTable = [
142 | 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
143 | 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230,
144 | 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963,
145 | 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327,
146 | 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487,
147 | 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
148 | ];
149 |
150 | SoundDecoder.prototype.getSampleADPCM = function() {
151 | // Decompress sample data using the IMA ADPCM algorithm.
152 | // Note: Handles only one channel, 4-bits/sample.
153 | var step = 0, code = 0, delta = 0;
154 |
155 | if (this.bytePosition % this.adpcmBlockSize == 0 && this.lastByte < 0) { // read block header
156 | if (this.bytePosition > this.info.sampleDataSize - 4) return null;
157 | this.sample = (this.soundData[this.bytePosition + 1] << 8) + this.soundData[this.bytePosition];
158 | if (this.sample > 32767) this.sample -= 65536;
159 | this.index = this.soundData[this.bytePosition + 2];
160 | this.bytePosition += 4;
161 | if (this.index > 88) this.index = 88;
162 | this.lastByte = -1;
163 | return this.sample;
164 | } else {
165 | // read 4-bit code and compute delta
166 | if (this.lastByte < 0) {
167 | if (this.bytePosition >= this.info.sampleDataSize) return null;
168 | this.lastByte = this.soundData[this.bytePosition++];
169 | code = this.lastByte & 0xF;
170 | } else {
171 | code = (this.lastByte >> 4) & 0xF;
172 | this.lastByte = -1;
173 | }
174 | step = SoundDecoder.stepTable[this.index];
175 | delta = 0;
176 | if (code & 4) delta += step;
177 | if (code & 2) delta += step >> 1;
178 | if (code & 1) delta += step >> 2;
179 | delta += step >> 3;
180 | // compute next index
181 | this.index += SoundDecoder.indexTable[code];
182 | if (this.index > 88) this.index = 88;
183 | if (this.index < 0) this.index = 0;
184 | // compute and output sample
185 | this.sample += code & 8 ? -delta : delta;
186 | if (this.sample > 32767) this.sample = 32767;
187 | if (this.sample < -32768) this.sample = -32768;
188 | return this.sample;
189 | }
190 | }
191 |
192 |
--------------------------------------------------------------------------------
/js/sound/WAVFile.js:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Massachusetts Institute of Technology
2 | //
3 | // This program is free software; you can redistribute it and/or
4 | // modify it under the terms of the GNU General Public License version 2,
5 | // as published by the Free Software Foundation.
6 | //
7 | // This program is distributed in the hope that it will be useful,
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | // GNU General Public License for more details.
11 | //
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 |
16 | // WAVFile.js
17 | // Utility class for reading and decoding WAV file metadata
18 | // Based directly on John Maloney's AS version for the Scratch Flash Player
19 |
20 | var WAVFile = function() {};
21 |
22 | WAVFile.decode = function(waveData) {
23 | // Decode the given WAV file data and return an Object with the format and sample data.
24 | var result = {};
25 |
26 | var data = new OffsetBuffer(waveData);
27 |
28 | // read WAVE File Header
29 | if (data.readString(4) != 'RIFF') { console.log("WAVFile: bad file header"); return; }
30 | var totalSize = data.readInt();
31 | if (data.getLength() != (totalSize + 8)) console.log("WAVFile: bad RIFF size; ignoring");
32 | if (data.readString(4) != 'WAVE') { console.log("WAVFile: not a WAVE file"); return; }
33 |
34 | // read format chunk
35 | var formatChunk = WAVFile.extractChunk('fmt ', data);
36 | if (formatChunk.getLength() < 16) { console.log("WAVFile: format chunk is too small"); return; }
37 |
38 | var encoding = formatChunk.readShort();
39 | result.encoding = encoding;
40 | result.channels = formatChunk.readShort();
41 | result.samplesPerSecond = formatChunk.readInt();
42 | result.bytesPerSecond = formatChunk.readInt();
43 | result.blockAlignment = formatChunk.readShort();
44 | result.bitsPerSample = formatChunk.readShort();
45 |
46 | // get size of data chunk
47 | var sampleDataStartAndSize = WAVFile.dataChunkStartAndSize(data);
48 | result.sampleDataStart = sampleDataStartAndSize[0];
49 | result.sampleDataSize = sampleDataStartAndSize[1];
50 |
51 | // handle various encodings
52 | if (encoding == 1) {
53 | if (!((result.bitsPerSample == 8) || (result.bitsPerSample == 16))) {
54 | console.log("WAVFile: can only handle 8-bit or 16-bit uncompressed PCM data");
55 | return;
56 | }
57 | result.sampleCount = result.sampleDataSize / 2;
58 | } else if (encoding == 17) {
59 | if (formatChunk.length < 20) { console.log("WAVFile: adpcm format chunk is too small"); return; }
60 | if (result.channels != 1) { console.log("WAVFile: adpcm supports only one channel (monophonic)"); return; }
61 | formatChunk.offset += 2; // skip extra header byte count
62 | var samplesPerBlock = formatChunk.readShort();
63 | result.adpcmBlockSize = ((samplesPerBlock - 1) / 2) + 4; // block size in bytes
64 | var factChunk = WAVFile.extractChunk('fact', data);
65 | if ((factChunk != null) && (factChunk.getLength() == 4)) {
66 | result.sampleCount = factChunk.readInt();
67 | } else {
68 | // this should never happen, since there should always be a 'fact' chunk
69 | // slight over-estimate (doesn't take ADPCM headers into account)
70 | result.sampleCount = 2 * result.sampleDataSize;
71 | }
72 | } else {
73 | console.log("WAVFile: unknown encoding " + encoding);
74 | return;
75 | }
76 | return result;
77 | };
78 |
79 | WAVFile.extractChunk = function(desiredType, data) {
80 | // Return the contents of the first chunk of the given type or an empty OffsetBuffer if it is not found.
81 | data.offset = 12;
82 | while (data.bytesAvailable() > 8) {
83 | var chunkType = data.readString(4);
84 | var chunkSize = data.readUint();
85 | if (chunkType == desiredType) {
86 | if (chunkSize > data.bytesAvailable()) return null;
87 | var result = new OffsetBuffer(data.readBytes(chunkSize));
88 | return result;
89 | } else {
90 | data.offset += chunkSize;
91 | }
92 | }
93 | return new OffsetBuffer(new ArrayBuffer());
94 | };
95 |
96 | WAVFile.dataChunkStartAndSize = function(data) {
97 | // Return an array with the starting offset and size of the first chunk of the given type.
98 | data.offset = 12;
99 | while (data.bytesAvailable() >= 8) {
100 | var chunkType = data.readString(4);
101 | var chunkSize = data.readUint();
102 | if (chunkType == 'data') {
103 | if (chunkSize > data.bytesAvailable()) return [0, 0]; // bad wave file
104 | return [data.offset, chunkSize];
105 | } else {
106 | data.offset += chunkSize;
107 | }
108 | }
109 | return [0, 0]; // chunk not found; bad wave file
110 | };
111 |
--------------------------------------------------------------------------------
/js/util/Color.js:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Massachusetts Institute of Technology
2 | //
3 | // This program is free software; you can redistribute it and/or
4 | // modify it under the terms of the GNU General Public License version 2,
5 | // as published by the Free Software Foundation.
6 | //
7 | // This program is distributed in the hope that it will be useful,
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | // GNU General Public License for more details.
11 | //
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 |
16 | // Scratch HTML5 Player
17 | // Color.js
18 | // Based on the original by John Maloney
19 |
20 | Color = function() {};
21 |
22 | Color.fromHSV = function(h, s, v) {
23 | var r, g, b;
24 | h = h % 360;
25 | if (h < 0) h += 360;
26 | s = Math.max(0, Math.min(s, 1));
27 | v = Math.max(0, Math.min(v, 1));
28 |
29 | var i = Math.floor(h / 60);
30 | var f = (h / 60) - i;
31 | var p = v * (1 - s);
32 | var q = v * (1 - s * f);
33 | var t = v * (1 - s * (1 - f));
34 | if (i == 0) { r = v; g = t; b = p; }
35 | else if (i == 1) { r = q; g = v; b = p; }
36 | else if (i == 2) { r = p; g = v; b = t; }
37 | else if (i == 3) { r = p; g = q; b = v; }
38 | else if (i == 4) { r = t; g = p; b = v; }
39 | else if (i == 5) { r = v; g = p; b = q; }
40 | r = Math.floor(r * 255);
41 | g = Math.floor(g * 255);
42 | b = Math.floor(b * 255);
43 | return (r << 16) | (g << 8) | b;
44 | };
45 |
46 | Color.rgb2hsv = function(rgb) {
47 | var h, s, v, x, f, i;
48 | var r = ((rgb >> 16) & 255) / 255;
49 | var g = ((rgb >> 8) & 255) / 255;
50 | var b = (rgb & 255) / 255;
51 | x = Math.min(Math.min(r, g), b);
52 | v = Math.max(Math.max(r, g), b);
53 | if (x == v) return [0, 0, v]; // gray; hue arbitrarily reported as zero
54 | f = r == x ? g - b : g == x ? b - r : r - g;
55 | i = r == x ? 3 : g == x ? 5 : 1;
56 | h = ((i - f / (v - x)) * 60) % 360;
57 | s = (v - x) / v;
58 | return [h, s, v];
59 | };
60 |
61 | Color.scaleBrightness = function(rgb, scale) {
62 | var hsv = Color.rgb2hsv(rgb);
63 | scale = Math.max(0, Math.min(scale, 1));
64 | return Color.fromHSV(hsv[0], hsv[1], scale * hsv[2]);
65 | };
66 |
67 | Color.mixRGB = function(rgb1, rgb2, fraction) {
68 | // Mix rgb1 with rgb2. 0 gives all rgb1, 1 gives rbg2, .5 mixes them 50/50.
69 | if (fraction <= 0) return rgb1;
70 | if (fraction >= 1) return rgb2;
71 | var r1 = (rgb1 >> 16) & 255;
72 | var g1 = (rgb1 >> 8) & 255;
73 | var b1 = rgb1 & 255
74 | var r2 = (rgb2 >> 16) & 255;
75 | var g2 = (rgb2 >> 8) & 255;
76 | var b2 = rgb2 & 255
77 | var r = ((fraction * r2) + ((1.0 - fraction) * r1)) & 255;
78 | var g = ((fraction * g2) + ((1.0 - fraction) * g1)) & 255;
79 | var b = ((fraction * b2) + ((1.0 - fraction) * b1)) & 255;
80 | return (r << 16) | (g << 8) | b;
81 | };
82 |
83 | Color.random = function() {
84 | // return a random color
85 | var h = 360 * Math.random();
86 | var s = 0.7 + (0.3 * Math.random());
87 | var v = 0.6 + (0.4 * Math.random());
88 | return Color.fromHSV(h, s, v);
89 | };
90 |
--------------------------------------------------------------------------------
/js/util/OffsetBuffer.js:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Massachusetts Institute of Technology
2 | //
3 | // This program is free software; you can redistribute it and/or
4 | // modify it under the terms of the GNU General Public License version 2,
5 | // as published by the Free Software Foundation.
6 | //
7 | // This program is distributed in the hope that it will be useful,
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | // GNU General Public License for more details.
11 | //
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 |
16 | // Provides the equivalent functionality of an AS ByteArray
17 | // using JavaScript ArrayBuffers and viewers
18 |
19 | var OffsetBuffer = function(data) {
20 | this.offset = 0;
21 | this.ab = data;
22 | };
23 |
24 | // Read various datatypes from the ArrayBuffer, seeking the offset.
25 | OffsetBuffer.prototype.readString = function(length) {
26 | var str = this.ab2str(this.ab.slice(this.offset, this.offset + length));
27 | this.offset += length;
28 | return str;
29 | };
30 |
31 | OffsetBuffer.prototype.readInt = function() {
32 | var num = this.ab2int(this.ab.slice(this.offset, this.offset + 4));
33 | this.offset += 4;
34 | return num;
35 | };
36 |
37 | OffsetBuffer.prototype.readUint = function() {
38 | var num = this.ab2uint(this.ab.slice(this.offset, this.offset + 4));
39 | this.offset += 4;
40 | return num;
41 | };
42 |
43 | OffsetBuffer.prototype.readShort = function() {
44 | var num = this.ab2short(this.ab.slice(this.offset, this.offset + 2));
45 | this.offset += 2;
46 | return num;
47 | };
48 |
49 | OffsetBuffer.prototype.readBytes = function(length) {
50 | var bytes = this.ab.slice(this.offset, this.offset + length);
51 | this.offset += length;
52 | return bytes;
53 | };
54 |
55 | // Length of the internal buffer
56 | OffsetBuffer.prototype.getLength = function() {
57 | return this.ab.byteLength;
58 | };
59 |
60 | // Number of bytes remaining from the current offset
61 | OffsetBuffer.prototype.bytesAvailable = function() {
62 | return this.getLength() - this.offset;
63 | };
64 |
65 | // ArrayBuffer -> JS type conversion methods
66 | OffsetBuffer.prototype.ab2str = function(buf) {
67 | return String.fromCharCode.apply(null, new Uint8Array(buf));
68 | };
69 |
70 | // These create Javascript Numbers
71 | OffsetBuffer.prototype.ab2int = function(buf) {
72 | return new Int32Array(buf)[0];
73 | };
74 |
75 | OffsetBuffer.prototype.ab2uint = function(buf) {
76 | return new Uint32Array(buf)[0];
77 | };
78 |
79 | OffsetBuffer.prototype.ab2short = function(buf) {
80 | return new Int16Array(buf)[0];
81 | };
82 |
--------------------------------------------------------------------------------
/js/util/Rectangle.js:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Massachusetts Institute of Technology
2 | //
3 | // This program is free software; you can redistribute it and/or
4 | // modify it under the terms of the GNU General Public License version 2,
5 | // as published by the Free Software Foundation.
6 | //
7 | // This program is distributed in the hope that it will be useful,
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | // GNU General Public License for more details.
11 | //
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 |
16 | var Point = function(x, y) {
17 | this.x = x;
18 | this.y = y;
19 | };
20 |
21 | var Rectangle = function(x, y, width, height) {
22 | this.x = x;
23 | this.y = y;
24 | this.width = width;
25 | this.height = height;
26 | this.left = x;
27 | this.right = x + width;
28 | this.top = y;
29 | this.bottom = y + height;
30 | };
31 |
32 | Rectangle.prototype.intersects = function(other) {
33 | return !(this.left > other.right || this.right < other.left || this.top > other.bottom || this.bottom < other.top);
34 | };
35 |
--------------------------------------------------------------------------------
/js/util/Timer.js:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Massachusetts Institute of Technology
2 | //
3 | // This program is free software; you can redistribute it and/or
4 | // modify it under the terms of the GNU General Public License version 2,
5 | // as published by the Free Software Foundation.
6 | //
7 | // This program is distributed in the hope that it will be useful,
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | // GNU General Public License for more details.
11 | //
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 |
16 | /*
17 | * Timer for the interpeter and performance testing
18 | * Tim Mickel, July 2011
19 | */
20 | var Timer = function() {
21 | var trials = [];
22 | var last_trial = 0;
23 | var start_time = 0;
24 | };
25 |
26 | Timer.prototype.time = function() {
27 | return Date.now();
28 | };
29 |
30 | Timer.prototype.start = function() {
31 | start_time = this.time();
32 | };
33 |
34 | Timer.prototype.stop = function() {
35 | end = this.time();
36 | last_trial = end - start_time;
37 | trials.push(last_trial);
38 | };
39 |
40 | Timer.prototype.count = function() {
41 | return trials.length;
42 | };
43 |
44 | Timer.prototype.average = function() {
45 | sum = 0;
46 | for (i = 0; i < this.count(); i++) {
47 | sum += trials[i];
48 | }
49 | return sum / this.count();
50 | };
51 |
52 | Timer.prototype.print = function(element) {
53 | text = "Trial: " + last_trial + "ms" +
54 | "
\nTrials: " + this.count() + ", Avg: " + this.average() + "ms";
55 | if (element) {
56 | $(element).html(text);
57 | } else {
58 | console.log(text);
59 | }
60 | };
61 |
--------------------------------------------------------------------------------
/makefile:
--------------------------------------------------------------------------------
1 | JSCS_PATH = ./node_modules/.bin/jscs
2 | KARMA_PATH = ./node_modules/.bin/karma
3 | KARMA_CONFIG = ./test/fixtures/karma.conf.js
4 |
5 | # Performs code governance (lint + style) test
6 | lint:
7 | @$(JSCS_PATH) ./js/*
8 | @$(JSCS_PATH) ./test/unit/*
9 |
10 | # Performs unit tests
11 | unit:
12 | @$(KARMA_PATH) start $(KARMA_CONFIG) $*
13 |
14 | # Run all test targets
15 | test:
16 | @make lint
17 | @make unit
18 |
19 | # Ignore directory structure
20 | .PHONY: lint unit test
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "scratch-html5",
3 | "description": "HTML 5 based Scratch project player",
4 | "repository": "https://github.com/LLK/scratch-html5",
5 | "scripts": {
6 | "postinstall": "curl http://code.jquery.com/jquery-1.11.0.min.js > ./test/lib/jquery-1.11.0.min.js",
7 | "test": "make test"
8 | },
9 | "engines": {
10 | "node": ">=0.10"
11 | },
12 | "dependencies": {},
13 | "devDependencies": {
14 | "jasmine-jquery": "~1.3.3",
15 | "jscs": "~1.3.0",
16 | "karma": "~0.10",
17 | "karma-html2js-preprocessor": "~0.1.0",
18 | "underscore": "~1.6.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/player.css:
--------------------------------------------------------------------------------
1 | /* Wrapper wraps the entire player, and the text below and above. Ideally,
2 | all CSS selectors would be descendants of #wrapper so that the player
3 | is embeddable.
4 | */
5 | #player-container {
6 | position: relative;
7 | width: 482px;
8 | padding: 48px;
9 | margin: 0 auto;
10 | font-family: sans-serif;
11 | /* Before, we accomplished this with e.preventDefault
12 | * on the context div. But, we'd like to use those click events
13 | * for some things like reporter sliders.
14 | */
15 | -webkit-touch-callout: none;
16 | -webkit-user-select: none;
17 | -khtml-user-select: none;
18 | -moz-user-select: none;
19 | -ms-user-select: none;
20 | user-select: none;
21 | }
22 |
23 | /* Control bar above the stage */
24 | #player-header {
25 | width: 480px;
26 | height: 38px;
27 | position: relative;
28 | background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e6e6e6));
29 | background: -webkit-linear-gradient(#fff, #e6e6e6);
30 | background: -moz-linear-gradient(#fff, #e6e6e6);
31 | background: -ms-linear-gradient(#fff, #e6e6e6);
32 | background: -o-linear-gradient(#fff, #e6e6e6);
33 | background: linear-gradient(#fff, #e6e6e6);
34 | border: 1px solid #d1d1d1;
35 | border-bottom: 0;
36 | border-radius: 7px 7px 0 0;
37 | }
38 |
39 | /* Button styling */
40 | #player-header button {
41 | border: 0;
42 | background: center center no-repeat;
43 | width: 33px;
44 | height: 38px;
45 | float: right;
46 | -moz-box-sizing: border-box;
47 | box-sizing: border-box;
48 | }
49 | #player-header-preload {
50 | width: 1px;
51 | height: 1px;
52 | position: absolute;
53 | top: -1px;
54 | left: -1px;
55 | background: url(img/fullScreenOn.png), url(img/greenFlagOn.png), url(img/stopOn.png);
56 | }
57 | button#toggle-fullscreen {
58 | background-image: url(img/fullScreenOff.png);
59 | background-position: 10px 8px;
60 | width: 40px;
61 | position: absolute;
62 | top: 0;
63 | left: 0;
64 | }
65 | button#trigger-green-flag {
66 | background-image: url(img/greenFlagOff.png);
67 | }
68 | button#trigger-green-flag:hover {
69 | background-image: url(img/greenFlagOn.png);
70 | }
71 | button#trigger-stop {
72 | background-image: url(img/stopOff.png);
73 | }
74 | button#trigger-stop:hover {
75 | background-image: url(img/stopOn.png);
76 | }
77 |
78 | /* Version number */
79 | #player-header-version {
80 | position: absolute;
81 | top: 28px;
82 | left: 0;
83 | width: 45px;
84 | text-align: center;
85 | color: rgba(0, 0, 0, .4);
86 | font: 9px sans-serif;
87 | }
88 |
89 | /* Wrapper for the Stage */
90 | #player-content {
91 | position: relative;
92 | border: 1px solid #d1d1d1;
93 | border-top: 0;
94 | width: 480px;
95 | height: 360px;
96 | }
97 |
98 | /* The Stage */
99 | #container {
100 | position: absolute;
101 | top: 0;
102 | left: 0;
103 | width: 480px;
104 | height: 360px;
105 | cursor: default;
106 | overflow: hidden;
107 | }
108 |
109 | /* Pane over the Stage with the green flag on startup */
110 | #overlay {
111 | position: absolute;
112 | top: 0;
113 | left: 0;
114 | width: 480px;
115 | height: 360px;
116 | z-index: 10000;
117 | background: url(img/playerStartFlag.png) rgba(0, 0, 0, .26) center center no-repeat;
118 | }
119 |
120 | /* Preloader */
121 | #preloader {
122 | position: absolute;
123 | z-index: 10001;
124 | top: 50%;
125 | left: 50%;
126 | margin-left: -156px;
127 | margin-top: -61px;
128 | width: 309px;
129 | height: 119px;
130 | border: 1px solid rgb(208, 209, 210);
131 | background: #fff;
132 | border-radius: 12px;
133 | -webkit-box-shadow: 4px 4px 6px rgba(0, 0, 0, .5);
134 | box-shadow: 4px 4px 6px rgba(0, 0, 0, .5);
135 | cursor: default;
136 | }
137 | #preloader-progress {
138 | margin: 24px 30px 14px 29px;
139 | background: rgb(185, 187, 189);
140 | border-radius: 5px;
141 | height: 22px;
142 | }
143 | #preloader-progress-bar {
144 | height: 22px;
145 | background: rgb(0, 161, 216);
146 | border-radius: 5px;
147 | width: 0;
148 | }
149 | #preloader-caption,
150 | #preloader-details {
151 | margin: 14px 0 0 8px;
152 | text-align: center;
153 | font-size: 18px;
154 | color: rgba(0, 0, 0, .65);
155 | }
156 | #preloader-details {
157 | margin: 6px 0 0 4px;
158 | font-size: 12px;
159 | }
160 |
161 | /* iPad arrow key frame */
162 | .arrow {
163 | position: absolute;
164 | top: 0;
165 | left: 0;
166 | width: 578px;
167 | height: 400px;
168 | text-align: center;
169 | font: 24px/48px sans-serif;
170 | color: rgba(0, 0, 0, .2);
171 | cursor: default;
172 | }
173 | .arrow.vertical {
174 | height: 48px;
175 | }
176 | .arrow.horizontal {
177 | width: 48px;
178 | top: 48px;
179 | line-height: 400px;
180 | }
181 | #right {
182 | left: 530px;
183 | }
184 | #down {
185 | top: 448px;
186 | }
187 | #up::before {
188 | content: '\25b2';
189 | }
190 | #down::before {
191 | content: '\25bc';
192 | }
193 | #left::before {
194 | content: '\25c0';
195 | }
196 | #right::before {
197 | content: '\25b6';
198 | }
199 |
200 | /* Panel for project ID input */
201 | #project-picker {
202 | margin: 16px 0;
203 | text-align: center;
204 | font-size: 16px;
205 | line-height: 18px;
206 | }
207 | #project-id {
208 | border: 1px solid #aaa;
209 | -webkit-border-radius: 0;
210 | padding: 6px;
211 | -webkit-box-shadow: inset 3px 3px 3px -3px rgba(0, 0, 0, .3);
212 | box-shadow: inset 3px 3px 3px -3px rgba(0, 0, 0, .3);
213 | background: 0;
214 | margin: 0;
215 | font: inherit;
216 | width: 6em;
217 | position: relative;
218 | }
219 | #project-id.error {
220 | background: #fee;
221 | -webkit-box-shadow: inset 3px 3px 3px -3px rgba(100, 0, 0, .3);
222 | box-shadow: inset 3px 3px 3px -3px rgba(100, 0, 0, .3);
223 | }
224 | #project-id:focus {
225 | z-index: 1;
226 | }
227 | #address-hint {
228 | padding-left: 6px;
229 | vertical-align: middle;
230 | }
231 | #go-project {
232 | padding: 6px;
233 | margin: 0;
234 | border: 1px solid #aaa;
235 | border-left: 0;
236 | font: inherit;
237 | background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#ddd));
238 | background: -webkit-linear-gradient(#fff, #ddd);
239 | background: -moz-linear-gradient(#fff, #ddd);
240 | background: -ms-linear-gradient(#fff, #ddd);
241 | background: -o-linear-gradient(#fff, #ddd);
242 | background: linear-gradient(#fff, #ddd);
243 | -webkit-box-shadow: inset 0 -1px rgba(255, 255, 255, .2);
244 | box-shadow: inset 0 -1px rgba(255, 255, 255, .2);
245 | cursor: pointer;
246 | position: relative;
247 | }
248 | #go-project:active {
249 | background: -webkit-gradient(linear, left top, left bottom, from(#ddd), to(#eee));
250 | background: -webkit-linear-gradient(#ddd, #eee);
251 | background: -moz-linear-gradient(#ddd, #eee);
252 | background: -ms-linear-gradient(#ddd, #eee);
253 | background: -o-linear-gradient(#ddd, #eee);
254 | background: linear-gradient(#ddd, #eee);
255 | -webkit-box-shadow: inset 0 1px rgba(255, 255, 255, .3), inset 0 2px 5px rgba(0, 0, 0, .1);
256 | box-shadow: inset 0 1px rgba(255, 255, 255, .3), inset 0 2px 5px rgba(0, 0, 0, .1);
257 | }
258 |
259 | #project-id,
260 | #go-project {
261 | -moz-box-sizing: content-box;
262 | box-sizing: content-box;
263 | height: 18px;
264 | vertical-align: middle;
265 | }
266 | #go-project::-moz-focus-inner {
267 | border: 0;
268 | padding: 0;
269 | }
270 |
271 |
272 | /* Reporter styles */
273 | .reporter-normal {
274 | display: inline-block;
275 | padding: 2px 7px 3px 5px;
276 | background-color: rgb(193, 196, 199);
277 | border: 1px solid rgb(148, 145, 145);
278 | border-radius: 6px;
279 | font: bold 11px/15px sans-serif;
280 | color: #000;
281 | position: absolute;
282 | }
283 | .reporter-inset {
284 | display: inline-block;
285 | vertical-align: top;
286 | min-width: 38px;
287 | margin-left: 6px;
288 | padding: 1px;
289 | border: 1px solid #fff;
290 | border-radius: 6px;
291 | -webkit-box-shadow: inset -1px -1px 3px rgba(255, 255, 255, 0.7), inset 1px 1px 2px rgba(0, 0, 0, 0.35);
292 | box-shadow: inset -1px -1px 3px rgba(255, 255, 255, 0.7), inset 1px 1px 2px rgba(0, 0, 0, 0.35);
293 | text-align: center;
294 | font: bold 9px/11px sans-serif;
295 | height: 11px;
296 | color: #fff;
297 | }
298 | .reporter-large {
299 | display: inline-block;
300 | min-width: 40px;
301 | padding: 2px 5px 1px 5px;
302 | border-radius: 4px;
303 | -webkit-box-shadow: 0 -3px 3px -3px #fff inset, -3px 0 3px -3px #fff inset, 0 3px 3px -3px #000 inset, 3px 0 3px -3px #000 inset;
304 | box-shadow: 0 -3px 3px -3px #fff inset, -3px 0 3px -3px #fff inset, 0 3px 3px -3px #000 inset, 3px 0 3px -3px #000 inset;
305 | font-family: bold 15px sans-serif;
306 | text-align: center;
307 | color: #fff;
308 | position: absolute;
309 | }
310 |
311 | /* List watcher styles */
312 | .list {
313 | float: left;
314 | border-radius: 7px;
315 | -webkit-border-radius: 7px;
316 | -moz-border-radius: 7px;
317 | border-style: solid;
318 | border-color: gray;
319 | border-width: 2px;
320 | background-color: #C1C4C7;
321 | padding-left: 3px;
322 | font: bold 11px sans-serif;
323 | position: absolute;
324 | }
325 |
326 | .list .list-title {
327 | padding-top: 2px;
328 | text-align: center;
329 | color: black;
330 | font-weight: bold;
331 | margin-bottom: 4px;
332 | }
333 |
334 | .list .list-scrollbar-container {
335 | float: right;
336 | width: 10px;
337 | position: relative;
338 | }
339 |
340 | .list .list-scrollbar {
341 | position: absolute;
342 | top: 0px;
343 | width: 10px;
344 | background-color: #a8aaad;
345 | border-radius: 10px;
346 | }
347 |
348 | .list .list-index {
349 | color: rgb(81, 81, 81);
350 | float: left;
351 | padding: 2px;
352 | margin-top: 0px;
353 | text-align: right;
354 | font: bold 11px;
355 | }
356 |
357 | .list .list-item {
358 | float: right;
359 | height: 16px;
360 | overflow: hidden;
361 | margin-bottom: 0px;
362 | margin-right: 2px;
363 | padding-top: 2px;
364 | padding-left: 5px;
365 | border-color: white;
366 | border-style: solid;
367 | border-width: 1px;
368 | border-radius: 4px;
369 | background-color: #cc5b22;
370 | color: white;
371 | word-wrap: break-word;
372 | font: bolder 11px sans-serif;
373 | }
374 |
375 | .list .list-add {
376 | clear: both;
377 | background-color: #dedede;
378 | width: 12px;
379 | height: 12px;
380 | border-radius: 12px;
381 | color: #707070;
382 | font: bold 10px sans-serif;
383 | text-align: center;
384 | position: absolute;
385 | bottom: 2px;
386 | left: 2px;
387 | }
388 |
389 | .list .list-length {
390 | font-size: 10px;
391 | font-weight: normal;
392 | text-align: center;
393 | color: black;
394 | position: absolute;
395 | bottom: 2px;
396 | left: 0px;
397 | right: 0px;
398 | }
399 |
400 | /* Say/think bubble styles */
401 | .bubble-container {
402 | position: absolute;
403 | }
404 | .bubble {
405 | position: relative;
406 | display: inline-block;
407 | max-width: 120px;
408 | min-width: 40px;
409 | padding: 6px 11px 6px 11px;
410 | border-radius: 10px;
411 | background: #fff;
412 | font-family: sans-serif;
413 | font-weight: bold;
414 | font-size: 14px;
415 | color: #000;
416 | text-align: center;
417 | }
418 | .bubble.say-think-border {
419 | border: 3px solid rgb(160, 160, 160);
420 | }
421 | .bubble.ask-border, .ask-container {
422 | border: 3px solid rgb(74, 173, 222);
423 | }
424 | .bubble-ask {
425 | position: absolute;
426 | margin-top: -3px;
427 | margin-left: 8px;
428 | width: 44px;
429 | height: 18px;
430 | background: url(img/ask-bottom.png) transparent no-repeat;
431 | }
432 | .bubble-say {
433 | position: absolute;
434 | margin-top: -3px;
435 | margin-left: 8px;
436 | width: 44px;
437 | height: 18px;
438 | background: url(img/say-bottom.png) transparent no-repeat;
439 | }
440 | .bubble-think {
441 | position: absolute;
442 | margin-top: 0px;
443 | margin-left: 8px;
444 | width: 44px;
445 | height: 19px;
446 | background: url(img/think-bottom.png) transparent no-repeat;
447 | }
448 | .ask-container {
449 | position: absolute;
450 | display: inline-block;
451 | padding: 5px 0px 0px 5px;
452 | border-radius: 5px;
453 | background: #fff;
454 | font-family: sans-serif;
455 | font-weight: bold;
456 | font-size: 14px;
457 | color: #000;
458 | }
459 | .ask-container .ask-field .ask-text-field {
460 | width: 405px;
461 | height: 16px;
462 | font-family: sans-serif;
463 | font-weight: light;
464 | font-size: 12px;
465 | background: #EAEAEA;
466 | }
467 |
468 | .ask-container .ask-button {
469 | position: absolute;
470 | right: 2px;
471 | top: 2px;
472 | width: 25px;
473 | height: 25px;
474 | background: url(img/ask-button.png) transparent no-repeat;
475 | }
476 |
477 |
478 |
--------------------------------------------------------------------------------
/soundbank/Instr.js:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Massachusetts Institute of Technology
2 | //
3 | // This program is free software; you can redistribute it and/or
4 | // modify it under the terms of the GNU General Public License version 2,
5 | // as published by the Free Software Foundation.
6 | //
7 | // This program is distributed in the hope that it will be useful,
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | // GNU General Public License for more details.
11 | //
12 | // You should have received a copy of the GNU General Public License
13 | // along with this program; if not, write to the Free Software
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 |
16 | // Instr.js
17 | // Tim Mickel, 2013
18 | // Based entirely on the AS by John Maloney, April 2012
19 | //
20 | // This class interacts with IO to load Scratch instruments and drums.
21 | // The variable 'samples' is a dictionary of named sound buffers.
22 | // Call initSamples() to initialize 'samples' before using (during load).
23 | //
24 | // All instrument and drum samples were created for Scratch by:
25 | //
26 | // Paul Madden, paulmatthewmadden@yahoo.com
27 | //
28 | // Paul is an excellent sound designer and we appreciate all the effort
29 | // he put into this project.
30 |
31 | var Instr = function() {}
32 |
33 | Instr.samples = {};
34 | Instr.wavsLoaded = 0;
35 |
36 | Instr.wavs = {
37 | 'AcousticGuitar_F3': 'instruments/AcousticGuitar_F3_22k.wav',
38 | 'AcousticPiano_As3': 'instruments/AcousticPiano(5)_A#3_22k.wav',
39 | 'AcousticPiano_C4': 'instruments/AcousticPiano(5)_C4_22k.wav',
40 | 'AcousticPiano_G4': 'instruments/AcousticPiano(5)_G4_22k.wav',
41 | 'AcousticPiano_F5': 'instruments/AcousticPiano(5)_F5_22k.wav',
42 | 'AcousticPiano_C6': 'instruments/AcousticPiano(5)_C6_22k.wav',
43 | 'AcousticPiano_Ds6': 'instruments/AcousticPiano(5)_D#6_22k.wav',
44 | 'AcousticPiano_D7': 'instruments/AcousticPiano(5)_D7_22k.wav',
45 | 'AltoSax_A3': 'instruments/AltoSax_A3_22K.wav',
46 | 'AltoSax_C6': 'instruments/AltoSax(3)_C6_22k.wav',
47 | 'Bassoon_C3': 'instruments/Bassoon_C3_22k.wav',
48 | 'BassTrombone_A2_2': 'instruments/BassTrombone_A2(2)_22k.wav',
49 | 'BassTrombone_A2_3': 'instruments/BassTrombone_A2(3)_22k.wav',
50 | 'Cello_C2': 'instruments/Cello(3b)_C2_22k.wav',
51 | 'Cello_As2': 'instruments/Cello(3)_A#2_22k.wav',
52 | 'Choir_F3': 'instruments/Choir(4)_F3_22k.wav',
53 | 'Choir_F4': 'instruments/Choir(4)_F4_22k.wav',
54 | 'Choir_F5': 'instruments/Choir(4)_F5_22k.wav',
55 | 'Clarinet_C4': 'instruments/Clarinet_C4_22k.wav',
56 | 'ElectricBass_G1': 'instruments/ElectricBass(2)_G1_22k.wav',
57 | 'ElectricGuitar_F3': 'instruments/ElectricGuitar(2)_F3(1)_22k.wav',
58 | 'ElectricPiano_C2': 'instruments/ElectricPiano_C2_22k.wav',
59 | 'ElectricPiano_C4': 'instruments/ElectricPiano_C4_22k.wav',
60 | 'EnglishHorn_D4': 'instruments/EnglishHorn(1)_D4_22k.wav',
61 | 'EnglishHorn_F3': 'instruments/EnglishHorn(1)_F3_22k.wav',
62 | 'Flute_B5_1': 'instruments/Flute(3)_B5(1)_22k.wav',
63 | 'Flute_B5_2': 'instruments/Flute(3)_B5(2)_22k.wav',
64 | 'Marimba_C4': 'instruments/Marimba_C4_22k.wav',
65 | 'MusicBox_C4': 'instruments/MusicBox_C4_22k.wav',
66 | 'Organ_G2': 'instruments/Organ(2)_G2_22k.wav',
67 | 'Pizz_A3': 'instruments/Pizz(2)_A3_22k.wav',
68 | 'Pizz_E4': 'instruments/Pizz(2)_E4_22k.wav',
69 | 'Pizz_G2': 'instruments/Pizz(2)_G2_22k.wav',
70 | 'SteelDrum_D5': 'instruments/SteelDrum_D5_22k.wav',
71 | 'SynthLead_C4': 'instruments/SynthLead(6)_C4_22k.wav',
72 | 'SynthLead_C6': 'instruments/SynthLead(6)_C6_22k.wav',
73 | 'SynthPad_A3': 'instruments/SynthPad(2)_A3_22k.wav',
74 | 'SynthPad_C6': 'instruments/SynthPad(2)_C6_22k.wav',
75 | 'TenorSax_C3': 'instruments/TenorSax(1)_C3_22k.wav',
76 | 'Trombone_B3': 'instruments/Trombone_B3_22k.wav',
77 | 'Trumpet_E5': 'instruments/Trumpet_E5_22k.wav',
78 | 'Vibraphone_C3': 'instruments/Vibraphone_C3_22k.wav',
79 | 'Violin_D4': 'instruments/Violin(2)_D4_22K.wav',
80 | 'Violin_A4': 'instruments/Violin(3)_A4_22k.wav',
81 | 'Violin_E5': 'instruments/Violin(3b)_E5_22k.wav',
82 | 'WoodenFlute_C5': 'instruments/WoodenFlute_C5_22k.wav',
83 | // Drums
84 | 'BassDrum': 'drums/BassDrum(1b)_22k.wav',
85 | 'Bongo': 'drums/Bongo_22k.wav',
86 | 'Cabasa': 'drums/Cabasa(1)_22k.wav',
87 | 'Clap': 'drums/Clap(1)_22k.wav',
88 | 'Claves': 'drums/Claves(1)_22k.wav',
89 | 'Conga': 'drums/Conga(1)_22k.wav',
90 | 'Cowbell': 'drums/Cowbell(3)_22k.wav',
91 | 'Crash': 'drums/Crash(2)_22k.wav',
92 | 'Cuica': 'drums/Cuica(2)_22k.wav',
93 | 'GuiroLong': 'drums/GuiroLong(1)_22k.wav',
94 | 'GuiroShort': 'drums/GuiroShort(1)_22k.wav',
95 | 'HiHatClosed': 'drums/HiHatClosed(1)_22k.wav',
96 | 'HiHatOpen': 'drums/HiHatOpen(2)_22k.wav',
97 | 'HiHatPedal': 'drums/HiHatPedal(1)_22k.wav',
98 | 'Maracas': 'drums/Maracas(1)_22k.wav',
99 | 'SideStick': 'drums/SideStick(1)_22k.wav',
100 | 'SnareDrum': 'drums/SnareDrum(1)_22k.wav',
101 | 'Tambourine': 'drums/Tambourine(3)_22k.wav',
102 | 'Tom': 'drums/Tom(1)_22k.wav',
103 | 'Triangle': 'drums/Triangle(1)_22k.wav',
104 | 'Vibraslap': 'drums/Vibraslap(1)_22k.wav',
105 | 'WoodBlock': 'drums/WoodBlock(1)_22k.wav'
106 | };
107 |
108 | Instr.wavCount = Object.keys(Instr.wavs).length;
109 |
--------------------------------------------------------------------------------
/soundbank/drums/BassDrum(1b)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/BassDrum(1b)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/Bongo_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/Bongo_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/Cabasa(1)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/Cabasa(1)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/Clap(1)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/Clap(1)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/Claves(1)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/Claves(1)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/Conga(1)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/Conga(1)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/Cowbell(3)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/Cowbell(3)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/Crash(2)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/Crash(2)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/Cuica(2)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/Cuica(2)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/GuiroLong(1)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/GuiroLong(1)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/GuiroShort(1)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/GuiroShort(1)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/HiHatClosed(1)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/HiHatClosed(1)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/HiHatOpen(2)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/HiHatOpen(2)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/HiHatPedal(1)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/HiHatPedal(1)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/Maracas(1)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/Maracas(1)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/SideStick(1)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/SideStick(1)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/SnareDrum(1)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/SnareDrum(1)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/Tambourine(3)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/Tambourine(3)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/Tom(1)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/Tom(1)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/Triangle(1)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/Triangle(1)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/Vibraslap(1)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/Vibraslap(1)_22k.wav
--------------------------------------------------------------------------------
/soundbank/drums/WoodBlock(1)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/drums/WoodBlock(1)_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/AcousticGuitar_F3_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/AcousticGuitar_F3_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/AcousticPiano(5)_A#3_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/AcousticPiano(5)_A#3_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/AcousticPiano(5)_C4_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/AcousticPiano(5)_C4_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/AcousticPiano(5)_C6_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/AcousticPiano(5)_C6_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/AcousticPiano(5)_D#6_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/AcousticPiano(5)_D#6_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/AcousticPiano(5)_D7_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/AcousticPiano(5)_D7_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/AcousticPiano(5)_F5_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/AcousticPiano(5)_F5_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/AcousticPiano(5)_G4_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/AcousticPiano(5)_G4_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/AltoSax(3)_C6_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/AltoSax(3)_C6_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/AltoSax_A3_22K.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/AltoSax_A3_22K.wav
--------------------------------------------------------------------------------
/soundbank/instruments/BassTrombone_A2(2)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/BassTrombone_A2(2)_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/BassTrombone_A2(3)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/BassTrombone_A2(3)_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Bassoon_C3_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Bassoon_C3_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Cello(3)_A#2_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Cello(3)_A#2_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Cello(3b)_C2_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Cello(3b)_C2_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Choir(4)_F3_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Choir(4)_F3_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Choir(4)_F4_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Choir(4)_F4_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Choir(4)_F5_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Choir(4)_F5_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Clarinet_C4_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Clarinet_C4_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/ElectricBass(2)_G1_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/ElectricBass(2)_G1_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/ElectricGuitar(2)_F3(1)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/ElectricGuitar(2)_F3(1)_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/ElectricPiano_C2_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/ElectricPiano_C2_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/ElectricPiano_C4_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/ElectricPiano_C4_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/EnglishHorn(1)_D4_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/EnglishHorn(1)_D4_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/EnglishHorn(1)_F3_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/EnglishHorn(1)_F3_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Flute(3)_B5(1)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Flute(3)_B5(1)_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Flute(3)_B5(2)_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Flute(3)_B5(2)_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Marimba_C4_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Marimba_C4_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/MusicBox_C4_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/MusicBox_C4_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Organ(2)_G2_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Organ(2)_G2_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Pizz(2)_A3_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Pizz(2)_A3_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Pizz(2)_E4_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Pizz(2)_E4_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Pizz(2)_G2_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Pizz(2)_G2_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/SteelDrum_D5_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/SteelDrum_D5_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/SynthLead(6)_C4_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/SynthLead(6)_C4_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/SynthLead(6)_C6_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/SynthLead(6)_C6_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/SynthPad(2)_A3_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/SynthPad(2)_A3_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/SynthPad(2)_C6_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/SynthPad(2)_C6_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/TenorSax(1)_C3_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/TenorSax(1)_C3_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Trombone_B3_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Trombone_B3_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Trumpet_E5_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Trumpet_E5_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Vibraphone_C3_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Vibraphone_C3_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Violin(2)_D4_22K.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Violin(2)_D4_22K.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Violin(3)_A4_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Violin(3)_A4_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/Violin(3b)_E5_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/Violin(3b)_E5_22k.wav
--------------------------------------------------------------------------------
/soundbank/instruments/WoodenFlute_C5_22k.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/soundbank/instruments/WoodenFlute_C5_22k.wav
--------------------------------------------------------------------------------
/test/artifacts/IOMock.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var ioMock = function() {
4 | var args = createArgs(arguments);
5 |
6 | function getArgs(argKey) {
7 | return ((argKey in args) ? args[argKey] : null);
8 | }
9 |
10 | function createArgs(methodArgs) {
11 | var args = {};
12 | if (methodArgs.length) {
13 | _.each(methodArgs, function(newObject) {
14 | _.each(newObject, function(value, key) {
15 | args[key] = value;
16 | });
17 | });
18 | }
19 | return args;
20 | }
21 |
22 | return {
23 | 'getCount' : function() { return getArgs('getCount'); }
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/test/artifacts/InterpreterMock.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var interpreterMock = function() {
4 | var args = createArgs(arguments);
5 |
6 | function getArgs(argKey) {
7 | return ((argKey in args) ? args[argKey] : null);
8 | }
9 |
10 | function createArgs(methodArgs) {
11 | var args = {};
12 | if (methodArgs.length) {
13 | _.each(methodArgs, function(newObject) {
14 | _.each(newObject, function(value, key) {
15 | args[key] = value;
16 | });
17 | });
18 | }
19 | return args;
20 | }
21 |
22 | return {
23 | 'targetSprite' : function() { return getArgs('targetSprite'); },
24 | 'arg': function(block, index) { return getArgs('arg');},
25 | 'activeThread': new threadMock(),
26 | 'targetStage': function() { var rtMock = new runtimeMock(); return rtMock.stage}
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/test/artifacts/RuntimeMock.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var runtimeMock = function() {
4 | var args = createArgs(arguments);
5 |
6 | function getArgs(argKey) {
7 | return ((argKey in args) ? args[argKey] : null);
8 | }
9 |
10 | function createArgs(methodArgs) {
11 | var args = {};
12 | if (methodArgs.length) {
13 | _.each(methodArgs, function(newObject) {
14 | _.each(newObject, function(value, key) {
15 | args[key] = value;
16 | });
17 | });
18 | }
19 | return args;
20 | }
21 |
22 | return {
23 | 'sprites' : [
24 | new spriteMock()
25 | ],
26 | 'stage': new stageMock()
27 |
28 | }
29 | };
30 |
--------------------------------------------------------------------------------
/test/artifacts/SpriteMock.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var spriteMock = function() {
4 | var args = createArgs(arguments);
5 |
6 | function getArgs(argKey) {
7 | return ((argKey in args) ? args[argKey] : null);
8 | }
9 |
10 | function createArgs(methodArgs) {
11 | var args = {};
12 | if (methodArgs.length) {
13 | _.each(methodArgs, function(newObject) {
14 | _.each(newObject, function(value, key) {
15 | args[key] = value;
16 | });
17 | });
18 | }
19 | return args;
20 | }
21 |
22 | return {
23 | 'hideBubble' : function() { return getArgs('hideBubble'); },
24 | 'hideAsk': function() { return getArgs('hideAsk');},
25 | 'resetFilters': function() { return getArgs('resetFilters');}
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/test/artifacts/StageMock.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var stageMock = function() {
4 | var args = createArgs(arguments);
5 |
6 | function getArgs(argKey) {
7 | return ((argKey in args) ? args[argKey] : null);
8 | }
9 |
10 | function createArgs(methodArgs) {
11 | var args = {};
12 | if (methodArgs.length) {
13 | _.each(methodArgs, function(newObject) {
14 | _.each(newObject, function(value, key) {
15 | args[key] = value;
16 | });
17 | });
18 | }
19 | return args;
20 | }
21 |
22 | return {
23 | 'resetFilters' : function() { return getArgs('resetFilters'); },
24 | 'askAnswer' : 12
25 | }
26 | };
27 |
--------------------------------------------------------------------------------
/test/artifacts/TargetMock.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var targetMock = function() {
4 | return {
5 | 'showBubble' : function() {},
6 | 'showAsk' : function() {},
7 | 'askAnswer' : 22
8 | };
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/test/artifacts/TestHelpers.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var deepCopy = function(object) {
4 | return jQuery.extend(true, {}, object);
5 | }
6 |
--------------------------------------------------------------------------------
/test/artifacts/ThreadMock.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var threadMock = function() {
4 | var args = createArgs(arguments);
5 |
6 | function getArgs(argKey) {
7 | return ((argKey in args) ? args[argKey] : null);
8 | }
9 |
10 | function createArgs(methodArgs) {
11 | var args = {};
12 | if (methodArgs.length) {
13 | _.each(methodArgs, function(newObject) {
14 | _.each(newObject, function(value, key) {
15 | args[key] = value;
16 | });
17 | });
18 | }
19 | return args;
20 | }
21 |
22 | return {
23 | 'paused' : getArgs('paused')
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/test/artifacts/ask-artifact.js:
--------------------------------------------------------------------------------
1 | var sensingData = {
2 | "objName": "Stage",
3 | "variables": [{
4 | "name": "myAnswer",
5 | "value": 0,
6 | "isPersistent": false
7 | }],
8 | "costumes": [{
9 | "costumeName": "backdrop1",
10 | "baseLayerID": -1,
11 | "baseLayerMD5": "b61b1077b0ea1931abee9dbbfa7903ff.png",
12 | "bitmapResolution": 2,
13 | "rotationCenterX": 480,
14 | "rotationCenterY": 360
15 | }],
16 | "currentCostumeIndex": 0,
17 | "penLayerMD5": "5c81a336fab8be57adc039a8a2b33ca9.png",
18 | "tempoBPM": 60,
19 | "videoAlpha": 0.5,
20 | "children": [{
21 | "objName": "Sprite1",
22 | "variables": [{
23 | "name": "myAnswer2",
24 | "value": 0,
25 | "isPersistent": false
26 | }, {
27 | "name": "answer",
28 | "value": 0,
29 | "isPersistent": false
30 | }],
31 | "scripts": [[42, 40.5, [["whenGreenFlag"], ["doAsk", "What's your name?"]]],
32 | [44.5,
33 | 155.5,
34 | [["whenGreenFlag"],
35 | ["say:", "Hello!"],
36 | ["doIf", ["=", ["timeAndDate", "minute"], "60"], [["say:", ["timestamp"]]]]]]],
37 | "costumes": [{
38 | "costumeName": "costume1",
39 | "baseLayerID": -1,
40 | "baseLayerMD5": "f9a1c175dbe2e5dee472858dd30d16bb.svg",
41 | "bitmapResolution": 1,
42 | "rotationCenterX": 47,
43 | "rotationCenterY": 55
44 | }],
45 | "currentCostumeIndex": 0,
46 | "scratchX": 0,
47 | "scratchY": 0,
48 | "scale": 1,
49 | "direction": 90,
50 | "rotationStyle": "normal",
51 | "isDraggable": false,
52 | "indexInLibrary": 1,
53 | "visible": true,
54 | "spriteInfo": {
55 | }
56 | }],
57 | "info": {
58 | "projectID": "18926654",
59 | "spriteCount": 1,
60 | "flashVersion": "MAC 12,0,0,70",
61 | "swfVersion": "v396",
62 | "userAgent": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10.9; rv:27.0) Gecko\/20100101 Firefox\/27.0",
63 | "videoOn": false,
64 | "scriptCount": 2,
65 | "hasCloudData": false
66 | }
67 | };
68 |
--------------------------------------------------------------------------------
/test/artifacts/io-artifact.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var io_base = 'proxy.php?resource=internalapi/';
4 | var project_base = 'http://cdn.projects.scratch.mit.edu/internalapi/project/';
5 | var project_suffix = '/get/';
6 | var asset_base = 'http://cdn.assets.scratch.mit.edu/internalapi/asset/';
7 | var asset_suffix = '/get/';
8 | var soundbank_base = 'soundbank/';
9 | var spriteLayerCount = 0;
10 |
--------------------------------------------------------------------------------
/test/artifacts/reporterValues.js:
--------------------------------------------------------------------------------
1 | 'use Strict;'
2 |
3 | var ReporterValues = function() {
4 | return {
5 | 'getStageVariables': function() {
6 | return {
7 | 'cmd' : "getVar:",
8 | 'color' : 15629590,
9 | 'isDiscrete' : true,
10 | 'mode' : 1,
11 | 'param' : "myAnswer",
12 | 'sliderMax' : 100,
13 | 'sliderMin' : 0,
14 | 'target' : "Stage",
15 | 'visible' : true,
16 | 'x' : 5,
17 | 'y' : 5,
18 | };
19 | }
20 | }
21 | };
22 | /*
23 | Additional Reporter examples
24 | cmd : "getVar:"
25 | color : 15629590
26 | isDiscrete : true
27 | mode : 1
28 | param : "myAnswer2"
29 | sliderMax : 100
30 | sliderMin : 0
31 | target : "Sprite1"
32 | visible : true
33 | x : 5
34 | y : 32
35 |
36 | cmd : "getVar:"
37 | color : 15629590
38 | isDiscrete : true
39 | mode : 1
40 | param : "answer"
41 | sliderMax : 100
42 | sliderMin : 0
43 | target : "Sprite1"
44 | visible : true
45 | x : 5
46 | y : 59
47 | */
48 |
--------------------------------------------------------------------------------
/test/artifacts/scratch-artifact.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var project_id = 123456789;
4 |
5 | var returnData = {
6 | "objName": "Stage",
7 | "sounds": [{
8 | "soundName": "pop",
9 | "soundID": -1,
10 | "md5": "83a9787d4cb6f3b7632b4ddfebf74367.wav",
11 | "sampleCount": 258,
12 | "rate": 11025,
13 | "format": ""
14 | }],
15 | "costumes": [{
16 | "costumeName": "Scene 1",
17 | "baseLayerID": -1,
18 | "baseLayerMD5": "510da64cf172d53750dffd23fbf73563.png",
19 | "rotationCenterX": 240,
20 | "rotationCenterY": 180,
21 | "spritesHiddenInScene": null
22 | }],
23 | "currentCostumeIndex": 0,
24 | "penLayerMD5": "279467d0d49e152706ed66539b577c00.png",
25 | "tempoBPM": 60,
26 | "children": [{
27 | "objName": "Sprite1",
28 | "scripts": [[283,
29 | 151,
30 | [["whenClicked"],
31 | ["clearPenTrails"],
32 | ["penColor:", 10485886],
33 | ["putPenDown"],
34 | ["doForever",
35 | [["gotoX:y:", ["randomFrom:to:", -240, 240], ["randomFrom:to:", -180, 180]], ["changePenShadeBy:", 10]]]]]],
36 | "sounds": [{
37 | "soundName": "pop",
38 | "soundID": -1,
39 | "md5": "83a9787d4cb6f3b7632b4ddfebf74367.wav",
40 | "sampleCount": 258,
41 | "rate": 11025,
42 | "format": ""
43 | }],
44 | "costumes": [{
45 | "costumeName": "Costume1",
46 | "baseLayerID": -1,
47 | "baseLayerMD5": "cce61b6e9ad98ea8c8c2e9556a94b7ab.png",
48 | "rotationCenterX": 47,
49 | "rotationCenterY": 55,
50 | "spritesHiddenInScene": null
51 | },
52 | {
53 | "costumeName": "Costume2",
54 | "baseLayerID": -1,
55 | "baseLayerMD5": "51f6fa1871f17de1a21cdfead7aad574.png",
56 | "rotationCenterX": 47,
57 | "rotationCenterY": 55,
58 | "spritesHiddenInScene": null
59 | }],
60 | "currentCostumeIndex": 0,
61 | "scratchX": 120,
62 | "scratchY": -101,
63 | "scale": 1,
64 | "direction": 90,
65 | "rotationStyle": "normal",
66 | "isDraggable": false,
67 | "indexInLibrary": 0,
68 | "visible": true
69 | },
70 | {
71 | "objName": "fish31",
72 | "scripts": [[181, 138, [["whenClicked"], ["nextCostume"]]]],
73 | "sounds": [{
74 | "soundName": "pop",
75 | "soundID": -1,
76 | "md5": "83a9787d4cb6f3b7632b4ddfebf74367.wav",
77 | "sampleCount": 258,
78 | "rate": 11025,
79 | "format": ""
80 | }],
81 | "costumes": [{
82 | "costumeName": "fish3",
83 | "baseLayerID": -1,
84 | "baseLayerMD5": "5ab571cf8c6e6bcf0ee2443b5df17dcb.png",
85 | "rotationCenterX": 90,
86 | "rotationCenterY": 79,
87 | "spritesHiddenInScene": null
88 | },
89 | {
90 | "costumeName": "crab1-a",
91 | "baseLayerID": -1,
92 | "baseLayerMD5": "110bf75ed212eb072acec2fa2c39456d.png",
93 | "rotationCenterX": 92,
94 | "rotationCenterY": 62,
95 | "spritesHiddenInScene": null
96 | },
97 | {
98 | "costumeName": "ballerina-a",
99 | "baseLayerID": -1,
100 | "baseLayerMD5": "4c789664cc6f69d1ef4678ac8b4cb812.png",
101 | "rotationCenterX": 51,
102 | "rotationCenterY": 84,
103 | "spritesHiddenInScene": null
104 | }],
105 | "currentCostumeIndex": 2,
106 | "scratchX": 108,
107 | "scratchY": -28,
108 | "scale": 1,
109 | "direction": 90,
110 | "rotationStyle": "normal",
111 | "isDraggable": false,
112 | "indexInLibrary": 0,
113 | "visible": true
114 | }],
115 | "info": {
116 | }
117 | };
118 |
--------------------------------------------------------------------------------
/test/fixtures/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config){
2 | config.set({
3 | basePath : '../../',
4 |
5 | files : [
6 | 'test/artifacts/**/*.js',
7 | 'test/lib/**/*.js',
8 | 'test/unit/**/*.js',
9 | 'js/sound/SoundDecoder.js',
10 | 'js/sound/**/*.js',
11 | 'js/util/**/*.js',
12 | 'js/**/*.js',
13 | 'node_modules/jasmine-jquery/lib/jasmine-jquery.js',
14 | 'node_modules/underscore/underscore.js'
15 | ],
16 |
17 | exclude : [
18 | ],
19 |
20 | preprocessors: {
21 | '*.html': ['html2js']
22 | },
23 |
24 | autoWatch : true,
25 |
26 | frameworks: ['jasmine'],
27 |
28 | browsers : ['Chrome'],
29 |
30 | plugins : [
31 | 'karma-jasmine',
32 | 'jasmine-jquery',
33 | 'karma-html2js-preprocessor',
34 | 'karma-chrome-launcher',
35 | 'karma-firefox-launcher'
36 | ]
37 | })}
38 |
--------------------------------------------------------------------------------
/test/lib/.git-keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchfoundation/scratch-html5/e7c3b48a2ae903d46d27b1936baf32da20a7b93b/test/lib/.git-keep
--------------------------------------------------------------------------------
/test/unit/interpreterSpec.js:
--------------------------------------------------------------------------------
1 | /* jasmine specs for Interpreter.js go here */
2 |
3 | describe('Interpreter', function() {
4 | var interp;
5 |
6 | beforeEach(function() {
7 | interp = Interpreter;
8 | });
9 |
10 | describe('Initialized variables', function() {
11 | var initInterp, realThread, realTimer;
12 | beforeEach(function() {
13 | realThread = Thread;
14 | realTimer = Timer;
15 | Thread = threadMock;
16 | Timer = function() {};
17 | initInterp = new interp();
18 | });
19 |
20 | afterEach(function() {
21 | Thread = realThread;
22 | Timer = realTimer;
23 | });
24 |
25 | describe('Interpreter Variables', function() {
26 | it('should have a primitiveTable collection', function() {
27 | expect(initInterp.primitiveTable).toEqual({});
28 | });
29 |
30 | it('should have a variables collection', function() {
31 | expect(initInterp.variables).toEqual({});
32 | });
33 |
34 | it('should have a threads array', function() {
35 | expect(initInterp.threads).toEqual([]);
36 | });
37 |
38 | it('should have an activeThread variable', function() {
39 | expect(initInterp.activeThread).toEqual(threadMock());
40 | });
41 |
42 | it('should have a WorkTime variable', function() {
43 | expect(initInterp.WorkTime).toBe(30);
44 | });
45 |
46 | it('should have a currentMSecs variable', function() {
47 | expect(initInterp.currentMSecs).toBe(null);
48 | });
49 |
50 | it('should have a timer variable', function() {
51 | expect(initInterp.timer).toEqual({});
52 | });
53 |
54 | it('should have a yield variable', function() {
55 | expect(initInterp.yield).toBe(false);
56 | });
57 |
58 | it('should have a doRedraw variable', function() {
59 | expect(initInterp.doRedraw).toBe(false);
60 | });
61 |
62 | it('should have an opCount variable', function() {
63 | expect(initInterp.opCount).toBe(0);
64 | });
65 |
66 | it('should have a debugOps variable', function() {
67 | expect(initInterp.debugOps).toBe(false);
68 | });
69 |
70 | it('should have a debugFunc variable', function() {
71 | expect(initInterp.debugFunc).toBe(null);
72 | });
73 |
74 | it('should have an opCount2 variable', function() {
75 | expect(initInterp.opCount2).toBe(0);
76 | });
77 | });
78 | });
79 |
80 | describe('TargetStage', function() {
81 | it('should return the target.stage object', function() {
82 | runtime = new runtimeMock();
83 | expect(interp.prototype.targetStage()).toEqual(runtime.stage);
84 | });
85 | });
86 | });
87 |
--------------------------------------------------------------------------------
/test/unit/ioSpec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* jasmine specs for IO.js go here */
4 |
5 | describe('IO', function() {
6 | var io;
7 |
8 | beforeEach(function() {
9 | io = new IO();
10 | });
11 |
12 | it('should have "null" data', function() {
13 | expect(io.data).toBe(null);
14 | });
15 |
16 | it('should have a project_base', function() {
17 | expect(io.project_base).toBe(project_base);
18 | });
19 |
20 | it('should have a project_suffix', function() {
21 | expect(io.project_suffix).toBe(project_suffix);
22 | });
23 |
24 | it('should have an asset_base', function() {
25 | expect(io.asset_base).toBe(asset_base);
26 | });
27 |
28 | it('should have an asset_suffix', function() {
29 | expect(io.asset_suffix).toBe(asset_suffix);
30 | });
31 |
32 | it('should have an soundbank_base', function() {
33 | expect(io.soundbank_base).toBe(soundbank_base);
34 | });
35 |
36 | it('should have a spriteLayerCount', function() {
37 | expect(io.spriteLayerCount).toBe(spriteLayerCount);
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/test/unit/looksPrimitiveSpec.js:
--------------------------------------------------------------------------------
1 | /* jasmine specs for primitives/LooksPrims.js go here */
2 |
3 | describe('LooksPrims', function() {
4 | var looksPrims, targetSpriteMock;
5 | beforeEach(function() {
6 | looksPrims = LooksPrims;
7 | targetSpriteMock = targetMock();
8 | });
9 |
10 | describe('showBubble for say', function() {
11 | var sayBlock;
12 | beforeEach(function() {
13 | sayBlock = {'args': ['what to say']};
14 | interp = interpreterMock({'targetSprite': targetSpriteMock }, {'arg': sayBlock});
15 | });
16 |
17 | it('should call the showBubble method on the targetedSprite', function() {
18 | spyOn(targetSpriteMock, "showBubble");
19 | showBubble(sayBlock, "say");
20 | expect(targetSpriteMock.showBubble).toHaveBeenCalledWith({args:['what to say']}, 'say');
21 | });
22 | });
23 |
24 | describe('showBubble for think', function() {
25 | var thinkBlock;
26 | beforeEach(function() {
27 | thinkBlock = {'args': ['what to think']};
28 | interp = interpreterMock({'targetSprite': targetSpriteMock }, {'arg': thinkBlock});
29 | });
30 |
31 | it('should call the showBubble method on the targetedSprite', function() {
32 | spyOn(targetSpriteMock, "showBubble");
33 | showBubble(thinkBlock, "think");
34 | expect(targetSpriteMock.showBubble).toHaveBeenCalledWith({args:['what to think']}, 'think');
35 | });
36 | });
37 |
38 | describe('showBubble for ask', function() {
39 | var askBlock;
40 | beforeEach(function() {
41 | askBlock = {'args': ['what to ask']};
42 | interp = interpreterMock({'targetSprite': targetSpriteMock }, {'arg': askBlock});
43 | });
44 |
45 | it('should call the showBubble method on the targetedSprite', function() {
46 | spyOn(targetSpriteMock, "showBubble");
47 | showBubble(askBlock, "ask");
48 | expect(targetSpriteMock.showBubble).toHaveBeenCalledWith({args:['what to ask']}, 'ask');
49 | });
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/test/unit/reporterSpec.js:
--------------------------------------------------------------------------------
1 | /* jasmine specs for Reporter.js go here */
2 |
3 | describe('Reporter', function() {
4 | var reporter, reporterValues;
5 |
6 | beforeEach(function() {
7 | reporter = Reporter;
8 | reporterValues = new ReporterValues();
9 | });
10 |
11 | describe('Initialized variables', function() {
12 | var initReporter;
13 | beforeEach(function() {
14 | io = new ioMock({'getCount': 4});
15 | initReporter = new reporter(reporterValues.getStageVariables());
16 | });
17 |
18 | describe('Reporter Variables', function() {
19 | it('should have a cmd variable', function() {
20 | expect(initReporter.cmd).toBe('getVar:');
21 | });
22 |
23 | it('should have a color variable', function() {
24 | expect(initReporter.color).toBe(15629590);
25 | });
26 |
27 | it('should have a isDiscrete variable', function() {
28 | expect(initReporter.isDiscrete).toBe(true);
29 | });
30 |
31 | it('should have a mode variable', function() {
32 | expect(initReporter.mode).toBe(1);
33 | });
34 |
35 | it('should have a param variable', function() {
36 | expect(initReporter.param).toBe('myAnswer');
37 | });
38 |
39 | it('should have a sliderMax variable', function() {
40 | expect(initReporter.sliderMax).toBe(100);
41 | });
42 |
43 | it('should have a sliderMin variable', function() {
44 | expect(initReporter.sliderMin).toBe(0);
45 | });
46 |
47 | it('should have a target variable', function() {
48 | expect(initReporter.target).toBe('Stage');
49 | });
50 |
51 | it('should have a visible variable', function() {
52 | expect(initReporter.visible).toBe(true);
53 | });
54 |
55 | it('should have a x variable', function() {
56 | expect(initReporter.x).toBe(5);
57 | });
58 |
59 | it('should have a y variable', function() {
60 | expect(initReporter.y).toBe(5);
61 | });
62 |
63 | it('should have a z variable', function() {
64 | expect(initReporter.z).toBe(4);
65 | });
66 |
67 | it('should have a label variable', function() {
68 | expect(initReporter.label).toBe('myAnswer');
69 | });
70 |
71 | it('should have an el variable', function() {
72 | expect(initReporter.el).toBe(null);
73 | });
74 |
75 | it('should have an valueEl variable', function() {
76 | expect(initReporter.valueEl).toBe(null);
77 | });
78 |
79 | it('should have an slider variable', function() {
80 | expect(initReporter.slider).toBe(null);
81 | });
82 | });
83 | });
84 |
85 | describe('determineReporterLabel', function() {
86 | it('should return a stage variable', function() {
87 | reporter.prototype.target = "Stage";
88 | reporter.prototype.param = "myAnswer";
89 | reporter.prototype.cmd = "getVar:";
90 | expect(reporter.prototype.determineReporterLabel()).toBe('myAnswer');
91 | });
92 |
93 | it('should return a sprite variable', function() {
94 | reporter.prototype.target = "Sprite 1";
95 | reporter.prototype.param = "localAnswer";
96 | reporter.prototype.cmd = "getVar:";
97 | expect(reporter.prototype.determineReporterLabel()).toBe('Sprite 1: localAnswer');
98 | });
99 |
100 | it('should return a stage answer variable', function() {
101 | reporter.prototype.target = "Stage";
102 | reporter.prototype.param = null;
103 | reporter.prototype.cmd = "answer";
104 | expect(reporter.prototype.determineReporterLabel()).toBe('answer');
105 | });
106 |
107 | });
108 | });
109 |
--------------------------------------------------------------------------------
/test/unit/runTimeSpec.js:
--------------------------------------------------------------------------------
1 | /* jasmine specs for Runtime.js go here */
2 |
3 | describe('Runtime', function() {
4 | var runtimeObj;
5 |
6 | beforeEach(function() {
7 | runtimeObj = Runtime;
8 | });
9 |
10 | describe('Initialized variables', function() {
11 | var initRuntime, lineCanvas;
12 | beforeEach(function() {
13 | initRuntime = new runtimeObj();
14 | });
15 |
16 | describe('Runtime Variables', function() {
17 | it('should have a scene variable', function() {
18 | expect(initRuntime.scene).toBe(null);
19 | });
20 |
21 | it('should have a sprites array', function() {
22 | expect(initRuntime.sprites).toEqual([]);
23 | });
24 |
25 | it('should have a reporters array', function() {
26 | expect(initRuntime.reporters).toEqual([]);
27 | });
28 |
29 | it('should have a keysDown array', function() {
30 | expect(initRuntime.keysDown).toEqual([]);
31 | });
32 |
33 | it('should have a mouseDown variable', function() {
34 | expect(initRuntime.mouseDown).toBe(false);
35 | });
36 |
37 | it('should have a mousePos array', function() {
38 | expect(initRuntime.mousePos).toEqual([0,0]);
39 | });
40 |
41 | it('should have an audioContext variable', function() {
42 | expect(initRuntime.audioContext).toBe(null);
43 | });
44 |
45 | it('should have an audoGain variable', function() {
46 | expect(initRuntime.audioGain).toBe(null);
47 | });
48 |
49 | it('should have an audioPlaying array', function() {
50 | expect(initRuntime.audioPlaying).toEqual([]);
51 | });
52 |
53 | it('should have a notesPlaying array', function() {
54 | expect(initRuntime.notesPlaying).toEqual([]);
55 | });
56 |
57 | it('should have a projectLoaded variable', function() {
58 | expect(initRuntime.projectLoaded).toBe(false);
59 | });
60 | });
61 | });
62 |
63 | describe('Stop All', function() {
64 | var realThread;
65 | beforeEach(function() {
66 | runtime = new runtimeMock
67 | spyOn(window, "stopAllSounds");
68 | spyOn(runtime.stage, "resetFilters");
69 | spyOn(runtime.sprites[0], "hideBubble");
70 | spyOn(runtime.sprites[0], "resetFilters");
71 | spyOn(runtime.sprites[0], "hideAsk");
72 | realThread = Thread;
73 | Thread = threadMock;
74 | interp = new interpreterMock();
75 | });
76 |
77 | afterEach(function() {
78 | Thread = realThread;
79 | });
80 |
81 | it('should call a new Thread Object', function() {
82 | runtimeObj.prototype.stopAll();
83 | expect(interp.activeThread).toEqual(new threadMock());
84 | });
85 |
86 | it('should intitialize an empty threads array', function() {
87 | runtimeObj.prototype.stopAll();
88 | expect(interp.threads).toEqual([]);
89 | });
90 |
91 | it('should call stopAllSounds', function() {
92 | runtimeObj.prototype.stopAll();
93 | expect(window.stopAllSounds).toHaveBeenCalled();
94 | });
95 |
96 | it('should call sprites.hideBubble', function() {
97 | runtimeObj.prototype.stopAll();
98 | expect(runtime.sprites[0].hideBubble).toHaveBeenCalled();
99 | });
100 |
101 | it('should call sprites.resetFilters', function() {
102 | runtimeObj.prototype.stopAll();
103 | expect(runtime.sprites[0].resetFilters).toHaveBeenCalled();
104 | });
105 |
106 | it('should call sprites.hideAsk', function() {
107 | runtimeObj.prototype.stopAll();
108 | expect(runtime.sprites[0].hideAsk).toHaveBeenCalled();
109 | });
110 |
111 | });
112 |
113 | });
114 |
--------------------------------------------------------------------------------
/test/unit/scratchSpec.js:
--------------------------------------------------------------------------------
1 | /* jasmine specs for Scratch.js go here */
2 |
3 | describe('Scratch', function() {
4 | var scratch;
5 |
6 | beforeEach(function() {
7 | spyOn(IO.prototype, "loadProject");
8 | spyOn(Runtime.prototype, "init");
9 | spyOn(Interpreter.prototype, "initPrims");
10 | scratch = Scratch;
11 | });
12 |
13 | describe('Scratch - Load Project', function() {
14 | beforeEach(function() {
15 | scratch(project_id);
16 | });
17 |
18 | it('should call the IO loadProject Method', function() {
19 | expect(IO.prototype.loadProject).toHaveBeenCalled();
20 | });
21 |
22 | it('should call the Runtime init method', function() {
23 | expect(Runtime.prototype.init).toHaveBeenCalled();
24 | });
25 |
26 | it('should call the Interpreter initPrims method', function() {
27 | expect(Interpreter.prototype.initPrims).toHaveBeenCalled();
28 | });
29 | });
30 |
31 | describe('Scratch - Click Green Flag', function() {
32 | beforeEach(function() {
33 | setFixtures('
');
34 | scratch(project_id);
35 | });
36 |
37 | it('should not click on the green flag if the project is loading', function() {
38 | runtime.projectLoaded = false;
39 | spyOn(runtime, 'greenFlag');
40 | $('#trigger-green-flag').click();
41 | expect(runtime.greenFlag).not.toHaveBeenCalled();
42 | expect($('#overlay').css('display')).toBe('block');
43 | });
44 |
45 | it('should click on the green flag if the project is loaded', function() {
46 | runtime.projectLoaded = true;
47 | spyOn(runtime, 'greenFlag');
48 | $('#trigger-green-flag').click();
49 | expect(runtime.greenFlag).toHaveBeenCalled();
50 | expect($('#overlay').css('display')).toBe('none');
51 | });
52 | });
53 |
54 | describe('Scratch - Click Stop', function() {
55 | beforeEach(function() {
56 | setFixtures('
');
57 | scratch(project_id);
58 | });
59 |
60 | it('should not click on the green flag if the project is loading', function() {
61 | spyOn(runtime, 'stopAll');
62 | $('#trigger-stop').click();
63 | expect(runtime.stopAll).toHaveBeenCalled();
64 | });
65 | });
66 | });
67 |
--------------------------------------------------------------------------------
/test/unit/sensingPrimitiveSpec.js:
--------------------------------------------------------------------------------
1 | /* jasmine specs for primitives/SensingPrims.js go here */
2 |
3 | describe('SensingPrims', function() {
4 | var sensingPrims;
5 | beforeEach(function() {
6 | sensingPrims = SensingPrims;
7 | realDate = Date;
8 | });
9 |
10 | afterEach(function() {
11 | Date = realDate;
12 | });
13 |
14 | describe('primTimestamp', function() {
15 | beforeEach(function() {
16 | /* MonkeyPatching the built-in Javascript Date */
17 | var epochDate = new Date(2000, 0, 1);
18 | var nowDate = new Date(2014, 5, 16);
19 | Date = function() {
20 | return (arguments.length ? epochDate : nowDate);
21 | };
22 | });
23 |
24 | it('should return the days since 2000', function() {
25 | expect(sensingPrims.prototype.primTimestamp()).toBeCloseTo(5280);
26 | });
27 | });
28 |
29 | describe('primTimeDate', function() {
30 | beforeEach(function() {
31 | /* MonkeyPatching the built-in Javascript Date */
32 | Date = function() {
33 | return {
34 | 'getFullYear': function() { return 2014;},
35 | 'getMonth': function() { return 4;},
36 | 'getDate': function() { return 16;},
37 | 'getDay': function() { return 4;},
38 | 'getHours': function() { return 9;},
39 | 'getMinutes': function() { return 18;},
40 | 'getSeconds': function() { return 36;},
41 | 'getTime': function() {}
42 | };
43 | };
44 | });
45 |
46 | it('should return the year', function() {
47 | var block = {'args': ['year']};
48 | expect(sensingPrims.prototype.primTimeDate(block)).toEqual(2014);
49 | });
50 |
51 | it('should return the month of the year', function() {
52 | var block = {'args': ['month']};
53 | expect(sensingPrims.prototype.primTimeDate(block)).toEqual(5);
54 | });
55 |
56 | it('should return the day of the week', function() {
57 | var block = {'args': ['day of week']};
58 | expect(sensingPrims.prototype.primTimeDate(block)).toEqual(5);
59 | });
60 |
61 | it('should return the hour of the day', function() {
62 | var block = {'args': ['hour']};
63 | expect(sensingPrims.prototype.primTimeDate(block)).toEqual(9);
64 | });
65 |
66 | it('should return the minute of the hour', function() {
67 | var block = {'args': ['minute']};
68 | expect(sensingPrims.prototype.primTimeDate(block)).toEqual(18);
69 | });
70 |
71 | it('should return the second of the minute', function() {
72 | var block = {'args': ['second']};
73 | expect(sensingPrims.prototype.primTimeDate(block)).toEqual(36);
74 | });
75 |
76 | it('should return the 0 on year', function() {
77 | var block = {'args': ['anythingElse']};
78 | expect(sensingPrims.prototype.primTimeDate(block)).toEqual(0);
79 | });
80 | });
81 |
82 | describe('primAnswer', function() {
83 | beforeEach(function() {
84 | interp = interpreterMock({'targetSprite': new targetMock()});
85 | });
86 |
87 | it('should return the answer variable from the targetedSprite', function() {
88 | expect(sensingPrims.prototype.primAnswer()).toBe(12);
89 | });
90 | });
91 |
92 | describe('primDoAsk', function() {
93 | var askBlock, targetSpriteMock;
94 | beforeEach(function() {
95 | targetSpriteMock = targetMock();
96 | askBlock = {'args': 'what to ask'};
97 | interp = interpreterMock({'targetSprite': targetSpriteMock}, {'arg': askBlock});
98 | });
99 |
100 | it('should call the showBubble method on the targetedSprite', function() {
101 | spyOn(window, "showBubble");
102 | spyOn(targetSpriteMock, "showAsk");
103 | sensingPrims.prototype.primDoAsk(askBlock);
104 | expect(window.showBubble).toHaveBeenCalledWith({args:'what to ask'}, 'doAsk');
105 | expect(targetSpriteMock.showAsk).toHaveBeenCalled;
106 | expect(interp.activeThread.paused).toBe(true);
107 | });
108 | });
109 | });
110 |
--------------------------------------------------------------------------------
/test/unit/stageSpec.js:
--------------------------------------------------------------------------------
1 | /* jasmine specs for Stage.js go here */
2 |
3 | describe('Stage', function() {
4 | var stage;
5 |
6 | beforeEach(function() {
7 | stage = Stage;
8 | });
9 |
10 | describe('Initialized variables', function() {
11 | var initStage, lineCanvas;
12 | beforeEach(function() {
13 | spyOn(Sprite, "call");
14 | initStage = new stage(sensingData);
15 | });
16 |
17 | describe('Stage Variables', function() {
18 | it('should have a z variable', function() {
19 | expect(initStage.z).toBe(-2);
20 | });
21 |
22 | it('should have a penLayerLoaded variable', function() {
23 | expect(initStage.penLayerLoaded).toBe(false);
24 | });
25 |
26 | it('should have a lineCanvas element', function() {
27 | expect(initStage.lineCanvas).toBeDefined();
28 | });
29 |
30 | it('should have a lineCanvas width', function() {
31 | expect(initStage.lineCanvas.width).toBe(480);
32 | });
33 |
34 | it('should have a lineCanvas height', function() {
35 | expect(initStage.lineCanvas.height).toBe(360);
36 | });
37 |
38 | it('should have a lineCache variable', function() {
39 | expect(initStage.lineCache).toBeDefined();
40 | });
41 |
42 | it('should have a isStage variable', function() {
43 | expect(initStage.isStage).toBe(true);
44 | });
45 |
46 | it('should have an askAnswer variable', function() {
47 | expect(initStage.askAnswer).toBe("");
48 | });
49 |
50 | it('should have called Sprite.call', function() {
51 | expect(Sprite.call).toHaveBeenCalled();
52 | });
53 | });
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/test/unit/threadSpec.js:
--------------------------------------------------------------------------------
1 | /* jasmine specs for Interpreter.js -> Thread go here */
2 |
3 | describe('Thread', function() {
4 | var thread;
5 |
6 | beforeEach(function() {
7 | thread = Thread;
8 | });
9 |
10 | describe('Initialized variables', function() {
11 | var initThread;
12 | beforeEach(function() {
13 | initThread = new thread('block', 'target');
14 | });
15 |
16 | describe('Thread Variables', function() {
17 | it('should have a nextBlock variable', function() {
18 | expect(initThread.nextBlock).toBe('block');
19 | });
20 |
21 | it('should have a firstBlock variable', function() {
22 | expect(initThread.firstBlock).toBe('block');
23 | });
24 |
25 | it('should have a stack variable', function() {
26 | expect(initThread.stack).toEqual([]);
27 | });
28 |
29 | it('should have a target variable', function() {
30 | expect(initThread.target).toBe('target');
31 | });
32 |
33 | it('should have a tmp variable', function() {
34 | expect(initThread.tmp).toBe(null);
35 | });
36 |
37 | it('should have a tmpObj variable', function() {
38 | expect(initThread.tmpObj).toEqual([]);
39 | });
40 |
41 | it('should have a firstTime variable', function() {
42 | expect(initThread.firstTime).toBe(true);
43 | });
44 |
45 | it('should have a paused variable', function() {
46 | expect(initThread.paused).toBe(false);
47 | });
48 | });
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/todo.txt:
--------------------------------------------------------------------------------
1 | To-do and notes: HTML5 Player
2 | Last updated: June 2013, Tim Mickel
3 |
4 | To do:
5 | -Implement volume control for playSound (it's working for notes and drums,
6 | but the way I'm preloading the sample data for playSound means changing the volume
7 | mid-play is a little more tricky. Perhaps use a GainNode per sprite?)
8 | Also need volume/tempo watchers.
9 |
10 | -Cloning
11 | This is potentially easy; duplicate the object using deep copy on clone.
12 | Then remove the object from runtime.sprites on self-destruct - be careful of mem. leak
13 |
14 | -Procedures
15 |
16 | -Trigger hats (e.g. When Scene Starts?)
17 |
18 | -SVGs/Clicks/Correct collisions and bounce
19 | (see Chromium bug https://code.google.com/p/chromium/issues/detail?id=249037).
20 |
21 | -Cloud variables/Scratch 2.0 API calls
22 |
23 | -List watchers
24 |
25 | -Implement sliders via drawing instead of
26 |
27 | -Graphic effects
28 | Some are simple to implement in canvas (ghost, brightness, etc.)
29 | Potentially use CSS filters (CSS3 hue-rotate looks promising)
30 |
31 | -Sound input, camera input
32 |
33 |
34 | Performance considerations:
35 | -Touching, touching color, color touching color can definitely be improved
36 |
37 | Known bugs:
38 | -Touching color, color touching color is currently finicky
39 | -"Keep on stage" / "Bounce on edge" do not match flash player; need to consider transparency
40 | -Experimental touch support for iPad was put in - this needs to be played with especially for non-iOS.
41 |
--------------------------------------------------------------------------------