Add very basic local file loading

- needs better UI
- the lifecycle of loading/unloading a project should be revisited
This commit is contained in:
Chad Walker 2014-09-08 22:58:44 -05:00
parent 318f5833bf
commit 4bbfa31b07
5 changed files with 146 additions and 24 deletions

View file

@ -12,11 +12,16 @@
<script src=//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js></script>
<script type="text/javascript">
$(document).ready(function() {
var project_id = location.hash && parseInt(location.hash.substr(1)) || 10000160;
var scratch = new Scratch(project_id);
if (location.hash) {
var project_id = parseInt(location.hash.substr(1)) || 10000160;
var scratch = new Scratch(project_id);
}
$("#file-picker").on('change', function (evnt) {
scratch = new Scratch(evnt.target.files[0]);
console.log('new scratch');
});
});
</script>
@ -44,7 +49,8 @@
</div>
<div id=project-picker>
<span id=address-hint>http://scratch.mit.edu/projects/</span><input id=project-id placeholder=10000160><button id=go-project>&rarr;</button>
<span id=address-hint>http://scratch.mit.edu/projects/</span><input id=project-id placeholder=10000160><button id=go-project>&rarr;</button><br/>
<input type="file" id="file-picker" name="file-picker" />
</div>
<h1>Scratch HTML5 player</h1>
@ -72,4 +78,5 @@
<script src=js/Interpreter.js></script>
<script src=js/Runtime.js></script>
<!-- <script src=js/Scratch.js></script> -->
<script type="text/JavaScript" src="js/Scratch.js"></script>
<script type="text/javascript" src="js/util/jszip.min.js"></script>
<script type="text/JavaScript" src="js/Scratch.js"></script>

117
js/IO.js
View file

@ -34,34 +34,93 @@ var IO = function() {
this.asset_suffix = '/get/';
this.soundbank_base = 'soundbank/';
this.spriteLayerCount = 0;
this.zip = null; // if loaded locally from a sb2 file
};
IO.prototype.initProject = function() {
this.makeObjects();
this.loadThreads();
this.loadNotesDrums();
runtime.loadStart(); // Try to run the project.
};
IO.prototype.loadProject = function(project_id) {
var self = this;
$.getJSON(this.project_base + project_id + this.project_suffix, function(data) {
self.data = data;
self.makeObjects();
self.loadThreads();
self.loadNotesDrums();
runtime.loadStart(); // Try to run the project.
self.initProject();
});
};
IO.prototype.loadProjectFromFile = function(file_obj) {
var self = this;
var reader = new FileReader();
reader.onload = function (load_event) {
var loaded = false;
try {
self.zip = new JSZip(load_event.target.result);
$.each(self.zip.files, function (index, zip_entry) {
if (loaded) {
return;
}
if (zip_entry.name === 'project.json') {
try {
self.data = JSON.parse(zip_entry.asText());
} catch (err) {
console.log('invalid JSON in package.json', err);
return;
}
loaded = true;
self.initProject();
}
});
} catch (err) {
console.log('error loading file', file_obj.name, err);
return;
}
};
reader.readAsArrayBuffer(file_obj);
};
IO.prototype.processSoundData = function (sound, soundData, sprite) {
// Decode the waveData and populate a buffer channel with the samples
var snd = new SoundDecoder(soundData);
var samples = snd.getAllSamples();
sound.buffer = runtime.audioContext.createBuffer(1, samples.length, runtime.audioContext.sampleRate);
var data = sound.buffer.getChannelData(0);
for (var i = 0; i < data.length; i++) {
data[i] = samples[i];
}
sprite.soundsLoaded++;
};
IO.prototype.loadSoundFromZip = function (sound, sprite) {
var self = this;
var extension = sound.md5.substr(-4);
var name = sound.soundID.toString() + extension;
var found = false;
$.each(self.zip.files, function (index, zip_entry) {
if (found) {
return;
}
if (zip_entry.name === name) {
found = true;
self.processSoundData(sound, zip_entry.asArrayBuffer(), sprite);
}
});
};
IO.prototype.soundRequest = function(sound, sprite) {
var self = this;
if (self.zip) {
return self.loadSoundFromZip(sound, sprite);
}
var request = new XMLHttpRequest();
request.open('GET', this.asset_base + sound['md5'] + this.asset_suffix, true);
request.open('GET', self.asset_base + sound['md5'] + self.asset_suffix, true);
request.responseType = 'arraybuffer';
request.onload = function() {
var waveData = request.response;
// Decode the waveData and populate a buffer channel with the samples
var snd = new SoundDecoder(waveData);
var samples = snd.getAllSamples();
sound.buffer = runtime.audioContext.createBuffer(1, samples.length, runtime.audioContext.sampleRate);
var data = sound.buffer.getChannelData(0);
for (var i = 0; i < data.length; i++) {
data[i] = samples[i];
}
sprite.soundsLoaded++;
self.processSoundData(sound, waveData, sprite);
};
request.send();
};
@ -158,3 +217,33 @@ IO.prototype.getCount = function() {
this.spriteLayerCount++;
return rv;
};
IO.prototype.getCostumeUrl = function (costume) {
if (!this.zip) {
return this.asset_base + costume.baseLayerMD5 + this.asset_suffix;
}
// get the file as a data url
var base64;
var extension = costume.baseLayerMD5.substr(-4);
var file = costume.baseLayerID.toString() + extension;
$.each(this.zip.files, function (index, zip_entry) {
if (base64) {
return;
}
if (zip_entry.name === file) {
base64 = JSZip.base64.encode(zip_entry.asBinary());
}
});
if (!base64) {
console.log('failed to convert costume to base64');
return '';
}
if (extension === '.png') {
return 'data:image/png;base64,' + base64;
}
if (extension === '.svg') {
return 'data:image/svg+xml;base64,' + base64;
}
console.log('unknown extension:', extension);
return '';
};

View file

@ -23,7 +23,7 @@
'use strict';
var runtime, interp, io, iosAudioActive = false;
function Scratch(project_id) {
function Scratch(project_id_or_file) {
runtime = new Runtime();
runtime.init();
@ -36,11 +36,18 @@ function Scratch(project_id) {
delete runtime.keysDown[e.which];
});
var project_id;
var address = $('#address-hint');
var project = $('#project-id');
// Update the project ID field
project.val(project_id);
if (typeof project_id_or_file === 'string') {
project_id = project_id_or_file;
// Update the project ID field
project.val(project_id);
} else {
project.val(project_id_or_file.name);
}
// Validate project ID field
project.keyup(function() {
@ -150,5 +157,10 @@ function Scratch(project_id) {
// Load the requested project and go!
io = new IO();
io.loadProject(project_id);
if (project_id) {
io.loadProject(project_id);
} else {
console.log('loading', project_id_or_file.name);
io.loadProjectFromFile(project_id_or_file);
}
};

View file

@ -142,7 +142,7 @@ Sprite.prototype.attach = function(scene) {
})
.attr({
'crossOrigin': 'anonymous',
'src': io.asset_base + this.costumes[c].baseLayerMD5 + io.asset_suffix
'src': io.getCostumeUrl(this.costumes[c]) // this will either be a remote
});
}

14
js/util/jszip.min.js vendored Normal file

File diff suppressed because one or more lines are too long