Add very basic local file loading
- needs better UI - the lifecycle of loading/unloading a project should be revisited
This commit is contained in:
parent
318f5833bf
commit
4bbfa31b07
5 changed files with 146 additions and 24 deletions
17
index.html
17
index.html
|
@ -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>→</button>
|
||||
<span id=address-hint>http://scratch.mit.edu/projects/</span><input id=project-id placeholder=10000160><button id=go-project>→</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
117
js/IO.js
|
@ -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 '';
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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
14
js/util/jszip.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in a new issue