use localForage for file store

This commit is contained in:
Steven Hugg 2018-06-26 02:56:36 -04:00
parent 436a476aff
commit beea5c3774
7 changed files with 206 additions and 132 deletions

3
.gitmodules vendored
View File

@ -19,3 +19,6 @@
[submodule "gif.js"]
path = gif.js
url = https://github.com/sehugg/gif.js
[submodule "localForage"]
path = localForage
url = https://github.com/localForage/localForage

View File

@ -205,7 +205,7 @@ ga('send', 'pageview');
<script src="javatari.js/release/javatari/javatari.js"></script>
<script src="src/cpu/z80fast.js"></script>
<script src="src/cpu/6809.js"></script>
<!--<script src="src/cpu/6809.js"></script>-->
<!--
<script src="jsnes/build/jsnes.min.js"></script>
<script src="jsnes/lib/dynamicaudio-min.js" type="text/javascript" charset="utf-8"></script>
@ -222,7 +222,9 @@ ga('send', 'pageview');
<script src="FileSaver.js/FileSaver.min.js"></script>
<script src="octokat.js/dist/octokat.js"></script>
<script src="gif.js/dist/gif.js"></script>
<script src="localForage/dist/localforage.nopromises.js"></script>
<script src="src/store.js"></script>
<script src="src/vlist.js"></script>
<script src="src/emu.js"></script>
<script src="src/audio.js"></script>

1
localForage Submodule

@ -0,0 +1 @@
Subproject commit 7428cfc4a6fd60ac00346619b923bfb8e17e399a

View File

@ -11,7 +11,7 @@ typedef unsigned char byte;
typedef signed char sbyte;
typedef unsigned short word;
const byte** BASL = 0x28;
static byte** BASL = (byte**) 0x28;
byte getchar(byte x, byte y) {
// JSR VTABZ
@ -108,7 +108,7 @@ void move_player(Player* p) {
cputcxy(p->x, p->y, p->tail_attr);
p->x += DIR_X[p->dir];
p->y += DIR_Y[p->dir];
if (getchar(p->x, p->y) & 0x7f != ' ')
if ((getchar(p->x, p->y) & 0x7f) != ' ')
p->collided = 1;
draw_player(p);
}

102
src/store.js Normal file
View File

@ -0,0 +1,102 @@
"use strict";
var OldFileStore = function(storage, prefix) {
var self = this;
this.saveFile = function(name, text) {
storage.setItem(prefix + name, text);
}
this.loadFile = function(name) {
return storage.getItem(prefix + name) || storage.getItem(name);
}
this.getFiles = function(prefix2) {
// iterate over files with <platform>/<dir> prefix
var files = [];
for (var i = 0; i < storage.length; i++) {
var key = storage.key(i);
if (key.startsWith(prefix + prefix2)) {
var name = key.substring(prefix.length + prefix2.length);
files.push(name);
}
}
return files;
}
this.deleteFile = function(name) {
storage.removeItem(prefix + name);
storage.removeItem(prefix + 'local/' + name);
}
}
// localforage-compatible driver for old file store format
var OldFileStoreDriver = {
_driver: 'oldFileStoreDriver',
_initStorage: function(options) {
this._store = new OldFileStore(localStorage, options.name + '/');
},
clear: function(callback) {
// Custom implementation here...
},
getItem: function(key, callback) {
callback(null, this._store.loadFile(key));
},
iterate: function(iteratorCallback, successCallback) {
// Custom implementation here...
},
key: function(n, callback) {
// Custom implementation here...
},
keys: function(callback) {
callback(this._store.getFiles(''));
},
length: function(callback) {
callback(this._store.getFiles('').length);
},
removeItem: function(key, callback) {
this._store.deleteFile(key);
callback();
},
setItem: function(key, value, callback) {
this._store.saveFile(key, value);
callback();
}
}
localforage.defineDriver(OldFileStoreDriver);
// copy localStorage to new driver
function copyFromOldStorageFormat(platform_id, newstore) {
var alreadyMigratedKey = "__migrated_" + platform_id;
//localStorage.removeItem(alreadyMigratedKey);
if (localStorage.getItem(alreadyMigratedKey))
return;
var oldstore = new OldFileStore(localStorage, platform_id + '/');
var keys = oldstore.getFiles('');
// no files to convert?
if (keys.length == 0) {
localStorage.setItem(alreadyMigratedKey, 'true');
return;
}
// convert function
function migrateNext() {
var key = keys.shift();
var value = oldstore.loadFile(key);
newstore.setItem(key, value, function(err, result) {
if (err) {
console.log(err);
} else {
console.log("Converted " + key);
if (keys.length) {
migrateNext();
} else {
newstore.length(function(err, len) {
console.log("Migrated " + len + " local files to new data store");
if (len) {
localStorage.setItem(alreadyMigratedKey, 'true');
window.location.reload();
}
});
}
}
});
}
migrateNext(); // start the conversion
}

218
src/ui.js
View File

@ -41,73 +41,6 @@ var userPaused;
var toolbar = $("#controls_top");
function getBiggestItems(storage) {
var items = [];
for (var i = 0; i < storage.length; i++) {
var key = storage.key(i);
var len = storage.getItem(key).length;
if (len>=100)
items.push([lpad(len+"", 12), key]);
}
items.sort();
return items;
}
/*
var s = "";
for (var i=items.length-5; i<items.length; i++) {
s += items[i] + "\n";
}
return s;
}*/
var FileStore = function(storage, prefix) {
var self = this;
this.saveFile = function(name, text) {
try {
storage.setItem(prefix + name, text);
} catch (e) {
if (e.name == 'NS_ERROR_DOM_QUOTA_REACHED') {
console.log(e);
if (confirm("Sorry, you've reached your local storage quota for this browser.\n\nGo to local storage editor?")) {
window.location = 'editstorage.html';
return;
}
} else {
throw e;
}
}
}
this.loadFile = function(name) {
return storage.getItem(prefix + name) || storage.getItem(name);
}
this.getFiles = function(prefix2) {
// rename items for compatibility
for (var i = 0; i < storage.length; i++) {
var key = storage.key(i);
if (key.startsWith(prefix2) && platform_id == 'vcs') {
this.saveFile(key, storage.getItem(key));
storage.removeItem(key);
console.log("Renamed",key,'to',prefix+key);
i=-1; // reset loop
}
}
// iterate over files with <platform>/<dir> prefix
var files = [];
for (var i = 0; i < storage.length; i++) {
var key = storage.key(i);
if (key.startsWith(prefix + prefix2)) {
var name = key.substring(prefix.length + prefix2.length);
files.push(name);
}
}
return files;
}
this.deleteFile = function(name) {
storage.removeItem(prefix + name);
storage.removeItem(prefix + 'local/' + name);
}
}
var SourceFile = function(lines, text) {
lines = lines || [];
this.text = text;
@ -237,7 +170,7 @@ function updatePreset(current_preset_id, text) {
// TODO: do we have to save all Verilog thingies?
if (text.trim().length &&
(originalFileID != current_preset_id || text != originalText || platform_id=='verilog')) {
store.saveFile(current_preset_id, text);
store.setItem(current_preset_id, text);
}
}
@ -255,34 +188,37 @@ function loadCode(text, fileid) {
function loadFile(fileid, filename, index) {
current_preset_id = fileid;
current_preset_index = index;
var text = store.loadFile(fileid) || "";
if (text) {
loadCode(text, fileid);
} else if (!text && index >= 0) {
if (filename.indexOf('.') <= 0)
filename += ".a";
console.log("Loading preset", fileid, filename, index, PRESETS[index]);
if (text.length == 0) {
console.log("Fetching", filename);
$.get( filename, function( text ) {
console.log("GET",text.length,'bytes');
store.getItem(fileid, function(err, text) {
if (err) console.log(err);
if (!text) text = '';
if (text) {
loadCode(text, fileid);
} else if (!text && index >= 0) {
if (filename.indexOf('.') <= 0)
filename += ".a";
console.log("Loading preset", fileid, filename, index, PRESETS[index]);
if (text.length == 0) {
console.log("Fetching", filename);
$.get( filename, function( text ) {
console.log("GET",text.length,'bytes');
loadCode(text, fileid);
}, 'text')
.fail(function() {
alert("Could not load preset " + fileid);
loadCode("", fileid);
});
}
} else {
var ext = platform.getToolForFilename(fileid);
$.get( "presets/"+platform_id+"/skeleton."+ext, function( text ) {
loadCode(text, fileid);
}, 'text')
.fail(function() {
alert("Could not load preset " + fileid);
alert("Could not load skeleton for " + platform_id + "/" + ext);
loadCode("", fileid);
});
}
} else {
var ext = platform.getToolForFilename(fileid);
$.get( "presets/"+platform_id+"/skeleton."+ext, function( text ) {
loadCode(text, fileid);
}, 'text')
.fail(function() {
alert("Could not load skeleton for " + platform_id + "/" + ext);
loadCode("", fileid);
});
}
});
}
function loadPreset(preset_id) {
@ -378,29 +314,38 @@ function _downloadSourceFile(e) {
}
function populateExamples(sel) {
sel.append($("<option />").text("--------- Examples ---------").attr('disabled',true));
for (var i=0; i<PRESETS.length; i++) {
var preset = PRESETS[i];
var name = preset.chapter ? (preset.chapter + ". " + preset.name) : preset.name;
sel.append($("<option />").val(preset.id).text(name).attr('selected',preset.id==current_preset_id));
}
// make sure to use callback so it follows other sections
store.length(function(err, len) {
sel.append($("<option />").text("--------- Examples ---------").attr('disabled',true));
for (var i=0; i<PRESETS.length; i++) {
var preset = PRESETS[i];
var name = preset.chapter ? (preset.chapter + ". " + preset.name) : preset.name;
sel.append($("<option />").val(preset.id).text(name).attr('selected',preset.id==current_preset_id));
}
});
}
function populateFiles(sel, name, prefix) {
sel.append($("<option />").text("------- " + name + " -------").attr('disabled',true));
var filenames = store.getFiles(prefix);
var foundSelected = false;
for (var i = 0; i < filenames.length; i++) {
var name = filenames[i];
var key = prefix + name;
sel.append($("<option />").val(key).text(name).attr('selected',key==current_preset_id));
if (key == current_preset_id) foundSelected = true;
}
if (!foundSelected && current_preset_id && current_preset_id.startsWith(prefix)) {
var name = current_preset_id.slice(prefix.length);
var key = prefix + name;
sel.append($("<option />").val(key).text(name).attr('selected',true));
}
function populateFiles(sel, category, prefix) {
store.keys(function(err, keys) {
var foundSelected = false;
var numFound = 0;
if (!keys) keys = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (key.startsWith(prefix)) {
if (numFound++ == 0)
sel.append($("<option />").text("------- " + category + " -------").attr('disabled',true));
var name = key.substring(prefix.length);
sel.append($("<option />").val(key).text(name).attr('selected',key==current_preset_id));
if (key == current_preset_id) foundSelected = true;
}
}
if (!foundSelected && current_preset_id && current_preset_id.startsWith(prefix)) {
var name = current_preset_id.slice(prefix.length);
var key = prefix + name;
sel.append($("<option />").val(key).text(name).attr('selected',true));
}
});
}
function updateSelector() {
@ -422,33 +367,47 @@ function updateSelector() {
});
}
function loadFileDependencies(text) {
var arr = [];
function loadFileDependencies(text, callback) {
var filenames = [];
if (platform_id == 'verilog') {
var re = /^(`include|[.]include)\s+"(.+?)"/gm;
var m;
while (m = re.exec(text)) {
arr.push({
filename:m[2],
prefix:platform_id,
text:store.loadFile(m[2]) || store.loadFile('local/'+m[2]) // TODO??
filenames.push(m[2]);
}
}
var result = [];
function loadNextDependency() {
var fn = filenames.shift();
if (!fn) {
callback(result);
} else {
store.getItem(fn, function(err, value) {
result.push({
filename:fn,
prefix:platform_id,
text:value // might be null, that's ok
});
loadNextDependency();
});
}
}
return arr;
loadNextDependency(); // load first dependency
}
function setCode(text) {
if (pendingWorkerMessages++ > 0)
return;
worker.postMessage({
code:text,
dependencies:loadFileDependencies(text),
platform:platform_id,
tool:platform.getToolForFilename(current_preset_id)
});
toolbar.addClass("is-busy");
$('#compile_spinner').css('visibility', 'visible');
loadFileDependencies(text, function(depends) {
worker.postMessage({
code:text,
dependencies:depends,
platform:platform_id,
tool:platform.getToolForFilename(current_preset_id)
});
});
}
function arrayCompare(a,b) {
@ -1427,7 +1386,14 @@ function preloadWorker(fileid) {
}
function initPlatform() {
store = new FileStore(localStorage, platform_id + '/');
//store = new FileStore(localStorage, platform_id + '/');
store = localforage.createInstance({
//driver: 'oldFileStoreDriver', //localforage.LOCALSTORAGE,
name: platform_id,
//storeName: platform_id,
version: "2.0"
});
copyFromOldStorageFormat(platform_id, store);
}
function showBookLink() {
@ -1525,7 +1491,7 @@ function startUI(loadplatform) {
// reset file?
if (qs['file'] && qs['reset']) {
initPlatform();
store.deleteFile(qs['file']);
store.removeItem(qs['file']);
qs['reset'] = '';
gotoNewLocation();
} else {

View File

@ -1034,7 +1034,7 @@ function linkSDLDZ80(step)
var sdcc;
function compileSDCC(step) {
gatherFiles(step, {
mainFilePath:"main.c" // not used
});
@ -1304,7 +1304,7 @@ var jsasm_module_output;
var jsasm_module_key;
function compileJSASM(asmcode, platform, options, is_inline) {
load("assembler");
load("../assembler");
var asm = new Assembler();
var includes = [];
asm.loadJSON = function(filename) {
@ -1401,7 +1401,7 @@ function compileVerilator(step) {
var topmod = detectTopModuleName(code);
var FS = verilator_mod['FS'];
FS.writeFile(topmod+".v", code);
writeDependencies(options.dependencies, FS, errors, function(d, code) {
writeDependencies(step.dependencies, FS, errors, function(d, code) {
return compileInlineASM(code, platform, options, errors, null);
});
starttime();