Merge pull request #498 from cwillisf/use-scratch-storage

Load projects & costumes through scratch-storage
This commit is contained in:
Chris Willis-Ford 2017-03-23 22:10:25 -07:00 committed by GitHub
commit c8b4871b19
21 changed files with 305 additions and 65 deletions

View file

@ -40,6 +40,7 @@
"scratch-audio": "latest", "scratch-audio": "latest",
"scratch-blocks": "latest", "scratch-blocks": "latest",
"scratch-render": "latest", "scratch-render": "latest",
"scratch-storage": "latest",
"script-loader": "0.7.0", "script-loader": "0.7.0",
"stats.js": "^0.17.0", "stats.js": "^0.17.0",
"tap": "^10.2.0", "tap": "^10.2.0",

View file

@ -311,6 +311,14 @@ Runtime.prototype.clearEdgeActivatedValues = function () {
this._edgeActivatedHatValues = {}; this._edgeActivatedHatValues = {};
}; };
/**
* Attach the audio engine
* @param {!AudioEngine} audioEngine The audio engine to attach
*/
Runtime.prototype.attachAudioEngine = function (audioEngine) {
this.audioEngine = audioEngine;
};
/** /**
* Attach the renderer * Attach the renderer
* @param {!RenderWebGL} renderer The renderer to attach * @param {!RenderWebGL} renderer The renderer to attach
@ -320,11 +328,11 @@ Runtime.prototype.attachRenderer = function (renderer) {
}; };
/** /**
* Attach the audio engine * Attach the storage module
* @param {!AudioEngine} audioEngine The audio engine to attach * @param {!ScratchStorage} storage The storage module to attach
*/ */
Runtime.prototype.attachAudioEngine = function (audioEngine) { Runtime.prototype.attachStorage = function (storage) {
this.audioEngine = audioEngine; this.storage = storage;
}; };
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View file

@ -5,10 +5,13 @@
* scratch-vm runtime structures. * scratch-vm runtime structures.
*/ */
var ScratchStorage = require('scratch-storage');
var AssetType = ScratchStorage.AssetType;
var Blocks = require('../engine/blocks'); var Blocks = require('../engine/blocks');
var RenderedTarget = require('../sprites/rendered-target'); var RenderedTarget = require('../sprites/rendered-target');
var Sprite = require('../sprites/sprite'); var Sprite = require('../sprites/sprite');
var Color = require('../util/color.js'); var Color = require('../util/color');
var log = require('../util/log'); var log = require('../util/log');
var uid = require('../util/uid'); var uid = require('../util/uid');
var specMap = require('./sb2specmap'); var specMap = require('./sb2specmap');
@ -26,7 +29,7 @@ var parseScratchObject = function (object, runtime, topLevel) {
if (!object.hasOwnProperty('objName')) { if (!object.hasOwnProperty('objName')) {
// Watcher/monitor - skip this object until those are implemented in VM. // Watcher/monitor - skip this object until those are implemented in VM.
// @todo // @todo
return; return null;
} }
// Blocks container for this object. // Blocks container for this object.
var blocks = new Blocks(); var blocks = new Blocks();
@ -37,33 +40,39 @@ var parseScratchObject = function (object, runtime, topLevel) {
sprite.name = object.objName; sprite.name = object.objName;
} }
// Costumes from JSON. // Costumes from JSON.
var costumePromises = [];
if (object.hasOwnProperty('costumes')) { if (object.hasOwnProperty('costumes')) {
for (var i = 0; i < object.costumes.length; i++) { for (var i = 0; i < object.costumes.length; i++) {
var costume = object.costumes[i]; var costumeSource = object.costumes[i];
// @todo: Make sure all the relevant metadata is being pulled out. var costume = {
sprite.costumes.push({ name: costumeSource.costumeName,
skin: 'https://cdn.assets.scratch.mit.edu/internalapi/asset/' + bitmapResolution: costumeSource.bitmapResolution || 1,
costume.baseLayerMD5 + '/get/', rotationCenterX: costumeSource.rotationCenterX,
name: costume.costumeName, rotationCenterY: costumeSource.rotationCenterY,
bitmapResolution: costume.bitmapResolution, skinId: null
rotationCenterX: costume.rotationCenterX, };
rotationCenterY: costume.rotationCenterY var costumePromise = loadCostume(costumeSource.baseLayerMD5, costume, runtime);
}); if (costumePromise) {
costumePromises.push(costumePromise);
}
sprite.costumes.push(costume);
} }
} }
// Sounds from JSON // Sounds from JSON
if (object.hasOwnProperty('sounds')) { if (object.hasOwnProperty('sounds')) {
for (var s = 0; s < object.sounds.length; s++) { for (var s = 0; s < object.sounds.length; s++) {
var sound = object.sounds[s]; var soundSource = object.sounds[s];
sprite.sounds.push({ var sound = {
format: sound.format, name: soundSource.soundName,
fileUrl: 'https://cdn.assets.scratch.mit.edu/internalapi/asset/' + sound.md5 + '/get/', format: soundSource.format,
rate: sound.rate, rate: soundSource.rate,
sampleCount: sound.sampleCount, sampleCount: soundSource.sampleCount,
soundID: sound.soundID, soundID: soundSource.soundID,
name: sound.soundName, md5: soundSource.md5,
md5: sound.md5 data: null
}); };
loadSound(sound, runtime);
sprite.sounds.push(sound);
} }
} }
// If included, parse any and all scripts/blocks on the object. // If included, parse any and all scripts/blocks on the object.
@ -127,7 +136,9 @@ var parseScratchObject = function (object, runtime, topLevel) {
} }
} }
target.isStage = topLevel; target.isStage = topLevel;
target.updateAllDrawableProperties(); Promise.all(costumePromises).then(function () {
target.updateAllDrawableProperties();
});
// The stage will have child objects; recursively process them. // The stage will have child objects; recursively process them.
if (object.children) { if (object.children) {
for (var m = 0; m < object.children.length; m++) { for (var m = 0; m < object.children.length; m++) {
@ -137,6 +148,92 @@ var parseScratchObject = function (object, runtime, topLevel) {
return target; return target;
}; };
/**
* Load a costume's asset into memory asynchronously.
* Do not call this unless there is a renderer attached.
* @param {string} md5ext - the MD5 and extension of the costume to be loaded.
* @param {!object} costume - the Scratch costume object.
* @property {int} skinId - the ID of the costume's render skin, once installed.
* @property {number} rotationCenterX - the X component of the costume's origin.
* @property {number} rotationCenterY - the Y component of the costume's origin.
* @property {number} [bitmapResolution] - the resolution scale for a bitmap costume.
* @param {!Runtime} runtime - Scratch runtime, used to access the storage module.
* @returns {?Promise} - a promise which will resolve after skinId is set, or null on error.
*/
var loadCostume = function (md5ext, costume, runtime) {
if (!runtime.storage) {
log.error('No storage module present; cannot load costume asset: ', md5ext);
return null;
}
if (!runtime.renderer) {
log.error('No rendering module present; cannot load costume asset: ', md5ext);
return null;
}
var idParts = md5ext.split('.');
var md5 = idParts[0];
var ext = idParts[1].toUpperCase();
var assetType = (ext === 'SVG') ? AssetType.ImageVector : AssetType.ImageBitmap;
var rotationCenter = [
costume.rotationCenterX / costume.bitmapResolution,
costume.rotationCenterY / costume.bitmapResolution
];
var promise = runtime.storage.load(assetType, md5);
if (assetType === AssetType.ImageVector) {
promise = promise.then(function (costumeAsset) {
costume.skinId = runtime.renderer.createSVGSkin(costumeAsset.decodeText(), rotationCenter);
});
} else {
promise = promise.then(function (costumeAsset) {
return new Promise(function (resolve, reject) {
var imageElement = new Image();
var removeEventListeners; // fix no-use-before-define
var onError = function () {
removeEventListeners();
reject();
};
var onLoad = function () {
removeEventListeners();
resolve(imageElement);
};
removeEventListeners = function () {
imageElement.removeEventListener('error', onError);
imageElement.removeEventListener('load', onLoad);
};
imageElement.addEventListener('error', onError);
imageElement.addEventListener('load', onLoad);
imageElement.src = costumeAsset.encodeDataURI();
});
}).then(function (imageElement) {
costume.skinId = runtime.renderer.createBitmapSkin(imageElement, costume.bitmapResolution, rotationCenter);
});
}
return promise;
};
/**
* Load a sound's asset into memory asynchronously.
* @param {!object} sound - the Scratch sound object.
* @property {string} md5 - the MD5 and extension of the sound to be loaded.
* @property {Buffer} data - sound data will be written here once loaded.
* @param {!Runtime} runtime - Scratch runtime, used to access the storage module.
*/
var loadSound = function (sound, runtime) {
if (!runtime.storage) {
log.error('No storage module present; cannot load sound asset: ', sound.md5);
return;
}
var idParts = sound.md5.split('.');
var md5 = idParts[0];
runtime.storage.load(AssetType.Sound, md5).then(function (soundAsset) {
sound.data = soundAsset.data;
// @todo register sound.data with scratch-audio
});
};
/** /**
* Top-level handler. Parse provided JSON, * Top-level handler. Parse provided JSON,
* and process the top-level object (the stage object). * and process the top-level object (the stage object).

View file

@ -1,27 +1,56 @@
var Scratch = window.Scratch = window.Scratch || {};
var ASSET_SERVER = 'https://cdn.assets.scratch.mit.edu/';
var PROJECT_SERVER = 'https://cdn.projects.scratch.mit.edu/';
var loadProject = function () { var loadProject = function () {
var id = location.hash.substring(1); var id = location.hash.substring(1);
if (id.length < 1 || !isFinite(id)) { if (id.length < 1 || !isFinite(id)) {
id = '119615668'; id = '119615668';
} }
var url = 'https://projects.scratch.mit.edu/internalapi/project/' + Scratch.vm.downloadProjectId(id);
id + '/get/'; };
var r = new XMLHttpRequest();
r.onreadystatechange = function () { /**
if (this.readyState === 4) { * @param {Asset} asset - calculate a URL for this asset.
if (r.status === 200) { * @returns {string} a URL to download a project file.
window.vm.loadProject(this.responseText); */
} var getProjectUrl = function (asset) {
} var assetIdParts = asset.assetId.split('.');
}; var assetUrlParts = [PROJECT_SERVER, 'internalapi/project/', assetIdParts[0], '/get/'];
r.open('GET', url); if (assetIdParts[1]) {
r.send(); assetUrlParts.push(assetIdParts[1]);
}
return assetUrlParts.join('');
};
/**
* @param {Asset} asset - calculate a URL for this asset.
* @returns {string} a URL to download a project asset (PNG, WAV, etc.)
*/
var getAssetUrl = function (asset) {
var assetUrlParts = [
ASSET_SERVER,
'internalapi/asset/',
asset.assetId,
'.',
asset.assetType.runtimeFormat,
'/get/'
];
return assetUrlParts.join('');
}; };
window.onload = function () { window.onload = function () {
// Lots of global variables to make debugging easier // Lots of global variables to make debugging easier
// Instantiate the VM. // Instantiate the VM.
var vm = new window.VirtualMachine(); var vm = new window.VirtualMachine();
window.vm = vm; Scratch.vm = vm;
var storage = new Scratch.Storage();
var AssetType = Scratch.Storage.AssetType;
storage.addWebSource([AssetType.Project], getProjectUrl);
storage.addWebSource([AssetType.ImageVector, AssetType.ImageBitmap, AssetType.Sound], getAssetUrl);
vm.attachStorage(storage);
// Loading projects from the server. // Loading projects from the server.
document.getElementById('projectLoadButton').onclick = function () { document.getElementById('projectLoadButton').onclick = function () {
@ -33,7 +62,7 @@ window.onload = function () {
// Instantiate the renderer and connect it to the VM. // Instantiate the renderer and connect it to the VM.
var canvas = document.getElementById('scratch-stage'); var canvas = document.getElementById('scratch-stage');
var renderer = new window.RenderWebGL(canvas); var renderer = new window.RenderWebGL(canvas);
window.renderer = renderer; Scratch.renderer = renderer;
vm.attachRenderer(renderer); vm.attachRenderer(renderer);
var audioEngine = new window.AudioEngine(); var audioEngine = new window.AudioEngine();
vm.attachAudioEngine(audioEngine); vm.attachAudioEngine(audioEngine);
@ -57,7 +86,7 @@ window.onload = function () {
dragShadowOpacity: 0.6 dragShadowOpacity: 0.6
} }
}); });
window.workspace = workspace; Scratch.workspace = workspace;
// Attach scratch-blocks events to VM. // Attach scratch-blocks events to VM.
workspace.addChangeListener(vm.blockListener); workspace.addChangeListener(vm.blockListener);
@ -90,10 +119,10 @@ window.onload = function () {
}; };
// Only request data from the VM thread if the appropriate tab is open. // Only request data from the VM thread if the appropriate tab is open.
window.exploreTabOpen = false; Scratch.exploreTabOpen = false;
var getPlaygroundData = function () { var getPlaygroundData = function () {
vm.getPlaygroundData(); vm.getPlaygroundData();
if (window.exploreTabOpen) { if (Scratch.exploreTabOpen) {
window.requestAnimationFrame(getPlaygroundData); window.requestAnimationFrame(getPlaygroundData);
} }
}; };
@ -182,7 +211,7 @@ window.onload = function () {
canvasWidth: rect.width, canvasWidth: rect.width,
canvasHeight: rect.height canvasHeight: rect.height
}; };
window.vm.postIOData('mouse', coordinates); Scratch.vm.postIOData('mouse', coordinates);
}); });
canvas.addEventListener('mousedown', function (e) { canvas.addEventListener('mousedown', function (e) {
var rect = canvas.getBoundingClientRect(); var rect = canvas.getBoundingClientRect();
@ -193,7 +222,7 @@ window.onload = function () {
canvasWidth: rect.width, canvasWidth: rect.width,
canvasHeight: rect.height canvasHeight: rect.height
}; };
window.vm.postIOData('mouse', data); Scratch.vm.postIOData('mouse', data);
e.preventDefault(); e.preventDefault();
}); });
canvas.addEventListener('mouseup', function (e) { canvas.addEventListener('mouseup', function (e) {
@ -205,7 +234,7 @@ window.onload = function () {
canvasWidth: rect.width, canvasWidth: rect.width,
canvasHeight: rect.height canvasHeight: rect.height
}; };
window.vm.postIOData('mouse', data); Scratch.vm.postIOData('mouse', data);
e.preventDefault(); e.preventDefault();
}); });
@ -215,7 +244,7 @@ window.onload = function () {
if (e.target !== document && e.target !== document.body) { if (e.target !== document && e.target !== document.body) {
return; return;
} }
window.vm.postIOData('keyboard', { Scratch.vm.postIOData('keyboard', {
keyCode: e.keyCode, keyCode: e.keyCode,
isDown: true isDown: true
}); });
@ -224,7 +253,7 @@ window.onload = function () {
document.addEventListener('keyup', function (e) { document.addEventListener('keyup', function (e) {
// Always capture up events, // Always capture up events,
// even those that have switched to other targets. // even those that have switched to other targets.
window.vm.postIOData('keyboard', { Scratch.vm.postIOData('keyboard', {
keyCode: e.keyCode, keyCode: e.keyCode,
isDown: false isDown: false
}); });
@ -268,7 +297,7 @@ window.onload = function () {
// Handlers to show different explorers. // Handlers to show different explorers.
document.getElementById('threadexplorer-link').addEventListener('click', document.getElementById('threadexplorer-link').addEventListener('click',
function () { function () {
window.exploreTabOpen = true; Scratch.exploreTabOpen = true;
getPlaygroundData(); getPlaygroundData();
tabBlockExplorer.style.display = 'none'; tabBlockExplorer.style.display = 'none';
tabRenderExplorer.style.display = 'none'; tabRenderExplorer.style.display = 'none';
@ -277,7 +306,7 @@ window.onload = function () {
}); });
document.getElementById('blockexplorer-link').addEventListener('click', document.getElementById('blockexplorer-link').addEventListener('click',
function () { function () {
window.exploreTabOpen = true; Scratch.exploreTabOpen = true;
getPlaygroundData(); getPlaygroundData();
tabBlockExplorer.style.display = 'block'; tabBlockExplorer.style.display = 'block';
tabRenderExplorer.style.display = 'none'; tabRenderExplorer.style.display = 'none';
@ -286,7 +315,7 @@ window.onload = function () {
}); });
document.getElementById('renderexplorer-link').addEventListener('click', document.getElementById('renderexplorer-link').addEventListener('click',
function () { function () {
window.exploreTabOpen = false; Scratch.exploreTabOpen = false;
tabBlockExplorer.style.display = 'none'; tabBlockExplorer.style.display = 'none';
tabRenderExplorer.style.display = 'block'; tabRenderExplorer.style.display = 'block';
tabThreadExplorer.style.display = 'none'; tabThreadExplorer.style.display = 'none';
@ -294,7 +323,7 @@ window.onload = function () {
}); });
document.getElementById('importexport-link').addEventListener('click', document.getElementById('importexport-link').addEventListener('click',
function () { function () {
window.exploreTabOpen = false; Scratch.exploreTabOpen = false;
tabBlockExplorer.style.display = 'none'; tabBlockExplorer.style.display = 'none';
tabRenderExplorer.style.display = 'none'; tabRenderExplorer.style.display = 'none';
tabThreadExplorer.style.display = 'none'; tabThreadExplorer.style.display = 'none';

View file

@ -367,7 +367,7 @@ RenderedTarget.prototype.setCostume = function (index) {
if (this.renderer) { if (this.renderer) {
var costume = this.sprite.costumes[this.currentCostume]; var costume = this.sprite.costumes[this.currentCostume];
var drawableProperties = { var drawableProperties = {
skin: costume.skin, skinId: costume.skinId,
costumeResolution: costume.bitmapResolution costumeResolution: costume.bitmapResolution
}; };
if ( if (
@ -458,7 +458,7 @@ RenderedTarget.prototype.updateAllDrawableProperties = function () {
draggable: this.draggable, draggable: this.draggable,
scale: renderedDirectionScale.scale, scale: renderedDirectionScale.scale,
visible: this.visible, visible: this.visible,
skin: costume.skin, skinId: costume.skinId,
costumeResolution: bitmapResolution, costumeResolution: bitmapResolution,
rotationCenter: [ rotationCenter: [
costume.rotationCenterX / bitmapResolution, costume.rotationCenterX / bitmapResolution,

View file

@ -24,7 +24,7 @@ var Sprite = function (blocks, runtime) {
* List of costumes for this sprite. * List of costumes for this sprite.
* Each entry is an object, e.g., * Each entry is an object, e.g.,
* { * {
* skin: "costume.svg", * skinId: 1,
* name: "Costume Name", * name: "Costume Name",
* bitmapResolution: 2, * bitmapResolution: 2,
* rotationCenterX: 0, * rotationCenterX: 0,

View file

@ -1,12 +1,16 @@
var EventEmitter = require('events'); var EventEmitter = require('events');
var util = require('util'); var util = require('util');
var log = require('./util/log');
var Runtime = require('./engine/runtime'); var Runtime = require('./engine/runtime');
var ScratchStorage = require('scratch-storage');
var sb2import = require('./import/sb2import'); var sb2import = require('./import/sb2import');
var StringUtil = require('./util/string-util'); var StringUtil = require('./util/string-util');
var RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_']; var RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_'];
var AssetType = ScratchStorage.AssetType;
/** /**
* Handles connections between blocks, stage, and extensions. * Handles connections between blocks, stage, and extensions.
* @constructor * @constructor
@ -156,9 +160,25 @@ VirtualMachine.prototype.loadProject = function (json) {
this.runtime.setEditingTarget(this.editingTarget); this.runtime.setEditingTarget(this.editingTarget);
}; };
/**
* Load a project from the Scratch web site, by ID.
* @param {string} id - the ID of the project to download, as a string.
*/
VirtualMachine.prototype.downloadProjectId = function (id) {
if (!this.runtime.storage) {
log.error('No storage module present; cannot load project: ', id);
return;
}
var vm = this;
var promise = this.runtime.storage.load(AssetType.Project, id);
promise.then(function (projectAsset) {
vm.loadProject(projectAsset.decodeText());
});
};
/** /**
* Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format. * Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format.
* @param {?string} json JSON string representing the sprite. * @param {string} json JSON string representing the sprite.
*/ */
VirtualMachine.prototype.addSprite2 = function (json) { VirtualMachine.prototype.addSprite2 = function (json) {
// Select new sprite. // Select new sprite.
@ -253,6 +273,14 @@ VirtualMachine.prototype.deleteSprite = function (targetId) {
} }
}; };
/**
* Set the audio engine for the VM/runtime
* @param {!AudioEngine} audioEngine The audio engine to attach
*/
VirtualMachine.prototype.attachAudioEngine = function (audioEngine) {
this.runtime.attachAudioEngine(audioEngine);
};
/** /**
* Set the renderer for the VM/runtime * Set the renderer for the VM/runtime
* @param {!RenderWebGL} renderer The renderer to attach * @param {!RenderWebGL} renderer The renderer to attach
@ -262,11 +290,11 @@ VirtualMachine.prototype.attachRenderer = function (renderer) {
}; };
/** /**
* Set the audio engine for the VM/runtime * Set the storage module for the VM/runtime
* @param {!AudioEngine} audioEngine The audio engine to attach * @param {!ScratchStorage} storage The storage module to attach
*/ */
VirtualMachine.prototype.attachAudioEngine = function (audioEngine) { VirtualMachine.prototype.attachStorage = function (storage) {
this.runtime.attachAudioEngine(audioEngine); this.runtime.attachStorage(storage);
}; };
/** /**

47
test/fixtures/attach-test-storage.js vendored Normal file
View file

@ -0,0 +1,47 @@
var ScratchStorage = require('scratch-storage');
var ASSET_SERVER = 'https://cdn.assets.scratch.mit.edu/';
var PROJECT_SERVER = 'https://cdn.projects.scratch.mit.edu/';
/**
* @param {Asset} asset - calculate a URL for this asset.
* @returns {string} a URL to download a project file.
*/
var getProjectUrl = function (asset) {
var assetIdParts = asset.assetId.split('.');
var assetUrlParts = [PROJECT_SERVER, 'internalapi/project/', assetIdParts[0], '/get/'];
if (assetIdParts[1]) {
assetUrlParts.push(assetIdParts[1]);
}
return assetUrlParts.join('');
};
/**
* @param {Asset} asset - calculate a URL for this asset.
* @returns {string} a URL to download a project asset (PNG, WAV, etc.)
*/
var getAssetUrl = function (asset) {
var assetUrlParts = [
ASSET_SERVER,
'internalapi/asset/',
asset.assetId,
'.',
asset.assetType.runtimeFormat,
'/get/'
];
return assetUrlParts.join('');
};
/**
* Construct a new instance of ScratchStorage, provide it with default web sources, and attach it to the provided VM.
* @param {VirtualMachine} vm - the VM which will own the new ScratchStorage instance.
*/
var attachTestStorage = function (vm) {
var storage = new ScratchStorage();
var AssetType = ScratchStorage.AssetType;
storage.addWebSource([AssetType.Project], getProjectUrl);
storage.addWebSource([AssetType.ImageVector, AssetType.ImageBitmap, AssetType.Sound], getAssetUrl);
vm.attachStorage(storage);
};
module.exports = attachTestStorage;

View file

@ -1,6 +1,7 @@
var fs = require('fs'); var fs = require('fs');
var path = require('path'); var path = require('path');
var test = require('tap').test; var test = require('tap').test;
var attachTestStorage = require('../fixtures/attach-test-storage');
var extract = require('../fixtures/extract'); var extract = require('../fixtures/extract');
var VirtualMachine = require('../../src/index'); var VirtualMachine = require('../../src/index');
@ -12,6 +13,7 @@ var sprite = fs.readFileSync(spriteUri, 'utf8');
test('complex', function (t) { test('complex', function (t) {
var vm = new VirtualMachine(); var vm = new VirtualMachine();
attachTestStorage(vm);
// Evaluate playground data and exit // Evaluate playground data and exit
vm.on('playgroundData', function (e) { vm.on('playgroundData', function (e) {

View file

@ -1,5 +1,6 @@
var path = require('path'); var path = require('path');
var test = require('tap').test; var test = require('tap').test;
var attachTestStorage = require('../fixtures/attach-test-storage');
var extract = require('../fixtures/extract'); var extract = require('../fixtures/extract');
var VirtualMachine = require('../../src/index'); var VirtualMachine = require('../../src/index');
@ -8,6 +9,7 @@ var project = extract(uri);
test('control', function (t) { test('control', function (t) {
var vm = new VirtualMachine(); var vm = new VirtualMachine();
attachTestStorage(vm);
// Evaluate playground data and exit // Evaluate playground data and exit
vm.on('playgroundData', function (e) { vm.on('playgroundData', function (e) {

View file

@ -1,5 +1,6 @@
var path = require('path'); var path = require('path');
var test = require('tap').test; var test = require('tap').test;
var attachTestStorage = require('../fixtures/attach-test-storage');
var extract = require('../fixtures/extract'); var extract = require('../fixtures/extract');
var VirtualMachine = require('../../src/index'); var VirtualMachine = require('../../src/index');
@ -8,6 +9,7 @@ var project = extract(uri);
test('data', function (t) { test('data', function (t) {
var vm = new VirtualMachine(); var vm = new VirtualMachine();
attachTestStorage(vm);
// Evaluate playground data and exit // Evaluate playground data and exit
vm.on('playgroundData', function () { vm.on('playgroundData', function () {

View file

@ -1,5 +1,6 @@
var path = require('path'); var path = require('path');
var test = require('tap').test; var test = require('tap').test;
var attachTestStorage = require('../fixtures/attach-test-storage');
var extract = require('../fixtures/extract'); var extract = require('../fixtures/extract');
var VirtualMachine = require('../../src/index'); var VirtualMachine = require('../../src/index');
@ -8,6 +9,7 @@ var project = extract(uri);
test('event', function (t) { test('event', function (t) {
var vm = new VirtualMachine(); var vm = new VirtualMachine();
attachTestStorage(vm);
// Evaluate playground data and exit // Evaluate playground data and exit
vm.on('playgroundData', function (e) { vm.on('playgroundData', function (e) {

View file

@ -1,5 +1,6 @@
var path = require('path'); var path = require('path');
var test = require('tap').test; var test = require('tap').test;
var attachTestStorage = require('../fixtures/attach-test-storage');
var extract = require('../fixtures/extract'); var extract = require('../fixtures/extract');
var VirtualMachine = require('../../src/index'); var VirtualMachine = require('../../src/index');
@ -8,6 +9,7 @@ var project = extract(projectUri);
test('complex', function (t) { test('complex', function (t) {
var vm = new VirtualMachine(); var vm = new VirtualMachine();
attachTestStorage(vm);
// Evaluate playground data and exit // Evaluate playground data and exit
vm.on('playgroundData', function (e) { vm.on('playgroundData', function (e) {

View file

@ -1,5 +1,6 @@
var path = require('path'); var path = require('path');
var test = require('tap').test; var test = require('tap').test;
var attachTestStorage = require('../fixtures/attach-test-storage');
var extract = require('../fixtures/extract'); var extract = require('../fixtures/extract');
var renderedTarget = require('../../src/sprites/rendered-target'); var renderedTarget = require('../../src/sprites/rendered-target');
@ -18,6 +19,7 @@ test('default', function (t) {
// Create runtime instance & load SB2 into it // Create runtime instance & load SB2 into it
var rt = new runtime(); var rt = new runtime();
attachTestStorage(rt);
sb2(file, rt); sb2(file, rt);
// Test // Test

View file

@ -1,5 +1,6 @@
var path = require('path'); var path = require('path');
var test = require('tap').test; var test = require('tap').test;
var attachTestStorage = require('../fixtures/attach-test-storage');
var extract = require('../fixtures/extract'); var extract = require('../fixtures/extract');
var VirtualMachine = require('../../src/index'); var VirtualMachine = require('../../src/index');
@ -8,6 +9,7 @@ var project = extract(uri);
test('looks', function (t) { test('looks', function (t) {
var vm = new VirtualMachine(); var vm = new VirtualMachine();
attachTestStorage(vm);
// Evaluate playground data and exit // Evaluate playground data and exit
vm.on('playgroundData', function (e) { vm.on('playgroundData', function (e) {

View file

@ -1,5 +1,6 @@
var path = require('path'); var path = require('path');
var test = require('tap').test; var test = require('tap').test;
var attachTestStorage = require('../fixtures/attach-test-storage');
var extract = require('../fixtures/extract'); var extract = require('../fixtures/extract');
var VirtualMachine = require('../../src/index'); var VirtualMachine = require('../../src/index');
@ -8,6 +9,7 @@ var project = extract(uri);
test('motion', function (t) { test('motion', function (t) {
var vm = new VirtualMachine(); var vm = new VirtualMachine();
attachTestStorage(vm);
// Evaluate playground data and exit // Evaluate playground data and exit
vm.on('playgroundData', function (e) { vm.on('playgroundData', function (e) {

View file

@ -1,5 +1,6 @@
var path = require('path'); var path = require('path');
var test = require('tap').test; var test = require('tap').test;
var attachTestStorage = require('../fixtures/attach-test-storage');
var extract = require('../fixtures/extract'); var extract = require('../fixtures/extract');
var VirtualMachine = require('../../src/index'); var VirtualMachine = require('../../src/index');
@ -8,6 +9,7 @@ var project = extract(uri);
test('pen', function (t) { test('pen', function (t) {
var vm = new VirtualMachine(); var vm = new VirtualMachine();
attachTestStorage(vm);
// Evaluate playground data and exit // Evaluate playground data and exit
vm.on('playgroundData', function () { vm.on('playgroundData', function () {

View file

@ -1,5 +1,6 @@
var path = require('path'); var path = require('path');
var test = require('tap').test; var test = require('tap').test;
var attachTestStorage = require('../fixtures/attach-test-storage');
var extract = require('../fixtures/extract'); var extract = require('../fixtures/extract');
var VirtualMachine = require('../../src/index'); var VirtualMachine = require('../../src/index');
@ -8,6 +9,7 @@ var project = extract(uri);
test('procedure', function (t) { test('procedure', function (t) {
var vm = new VirtualMachine(); var vm = new VirtualMachine();
attachTestStorage(vm);
// Evaluate playground data and exit // Evaluate playground data and exit
vm.on('playgroundData', function (e) { vm.on('playgroundData', function (e) {

View file

@ -1,5 +1,6 @@
var path = require('path'); var path = require('path');
var test = require('tap').test; var test = require('tap').test;
var attachTestStorage = require('../fixtures/attach-test-storage');
var extract = require('../fixtures/extract'); var extract = require('../fixtures/extract');
var VirtualMachine = require('../../src/index'); var VirtualMachine = require('../../src/index');
@ -8,6 +9,7 @@ var project = extract(uri);
test('sensing', function (t) { test('sensing', function (t) {
var vm = new VirtualMachine(); var vm = new VirtualMachine();
attachTestStorage(vm);
// Evaluate playground data and exit // Evaluate playground data and exit
vm.on('playgroundData', function (e) { vm.on('playgroundData', function (e) {

View file

@ -1,5 +1,6 @@
var path = require('path'); var path = require('path');
var test = require('tap').test; var test = require('tap').test;
var attachTestStorage = require('../fixtures/attach-test-storage');
var extract = require('../fixtures/extract'); var extract = require('../fixtures/extract');
var VirtualMachine = require('../../src/index'); var VirtualMachine = require('../../src/index');
@ -8,6 +9,7 @@ var project = extract(uri);
test('sound', function (t) { test('sound', function (t) {
var vm = new VirtualMachine(); var vm = new VirtualMachine();
attachTestStorage(vm);
// Evaluate playground data and exit // Evaluate playground data and exit
vm.on('playgroundData', function (e) { vm.on('playgroundData', function (e) {

View file

@ -64,10 +64,12 @@ module.exports = [
'highlightjs/highlight.pack.min.js', 'highlightjs/highlight.pack.min.js',
// Scratch Blocks // Scratch Blocks
'scratch-blocks/dist/vertical.js', 'scratch-blocks/dist/vertical.js',
// Audio
'scratch-audio',
// Renderer // Renderer
'scratch-render', 'scratch-render',
// Audio // Storage
'scratch-audio' 'scratch-storage'
] ]
}, },
output: { output: {
@ -92,13 +94,17 @@ module.exports = [
test: require.resolve('scratch-blocks/dist/vertical.js'), test: require.resolve('scratch-blocks/dist/vertical.js'),
loader: 'expose-loader?Blockly' loader: 'expose-loader?Blockly'
}, },
{
test: require.resolve('scratch-audio'),
loader: 'expose-loader?AudioEngine'
},
{ {
test: require.resolve('scratch-render'), test: require.resolve('scratch-render'),
loader: 'expose-loader?RenderWebGL' loader: 'expose-loader?RenderWebGL'
}, },
{ {
test: require.resolve('scratch-audio'), test: require.resolve('scratch-storage'),
loader: 'expose-loader?AudioEngine' loader: 'expose-loader?Scratch.Storage'
} }
] ]
}, },