diff --git a/js/index.js b/js/index.js index 4f1951b..8e7b6d4 100644 --- a/js/index.js +++ b/js/index.js @@ -263,6 +263,14 @@ function iframeSetup(options) { id : msg.id }); }); + } else if (msg.type=="readstoragejson") { + Comms.readFile(msg.filename).then(function(result) { + iframe.contentWindow.postMessage({ + type : "readstoragejsonrsp", + data : Utils.parseRJSON(result), + id : msg.id + }); + }); } else if (msg.type=="writestorage") { Progress.show({title:`Uploading ${JSON.stringify(msg.filename)}`,sticky:true}); Comms.writeFile(msg.filename, msg.data).then(function() { diff --git a/js/utils.js b/js/utils.js index 44fe2f7..d3c97e3 100644 --- a/js/utils.js +++ b/js/utils.js @@ -391,6 +391,76 @@ function atobSafe(input) { return output; } + +// parse relaxed JSON which Espruino's writeJSON uses for settings/etc (returns undefined on failure) +function parseRJSON(str) { + let lex = Espruino.Core.Utils.getLexer(str); + let tok = lex.next(); + function match(s) { + if (tok.str!=s) throw new Error("Expecting "+s+" got "+JSON.stringify(tok.str)); + tok = lex.next(); + } + + function recurse() { + let final = ""; + while (tok!==undefined) { + if (tok.type == "NUMBER") { + let v = parseFloat(tok.str); + tok = lex.next(); + return v; + } + if (tok.str == "-") { + tok = lex.next(); + let v = -parseFloat(tok.str); + tok = lex.next(); + return v; + } + if (tok.type == "STRING") { + let v = tok.value; + tok = lex.next(); + return v; + } + if (tok.type == "ID") switch (tok.str) { + case "true" : tok = lex.next(); return true; + case "false" : tok = lex.next(); return false; + case "null" : tok = lex.next(); return null; + } + if (tok.str == "[") { + tok = lex.next(); + let arr = []; + while (tok.str != ']') { + arr.push(recurse()); + if (tok.str != ']') match(","); + } + match("]"); + return arr; + } + if (tok.str == "{") { + tok = lex.next(); + let obj = {}; + while (tok.str != '}') { + let key = tok.type=="STRING" ? tok.value : tok.str; + tok = lex.next(); + match(":"); + obj[key] = recurse(); + if (tok.str != '}') match(","); + } + match("}"); + return obj; + } + match("EOF"); + } + } + + let json = undefined; + try { + json = recurse(); + } catch (e) { + console.log("RJSON parse error", e); + } + return json; +} + var Utils = { Const : Const, DEVICEINFO : DEVICEINFO, @@ -409,7 +479,8 @@ var Utils = { isAppUpdateable : isAppUpdateable, versionLess : versionLess, debounce : debounce, - atobSafe : atobSafe + atobSafe : atobSafe, // version of 'window.atob' that doesn't fail on 'not correctly encoded' strings + parseRJSON : parseRJSON // parse relaxed JSON which Espruino's writeJSON uses for settings/etc (returns undefined on failure) }; if ("undefined"!=typeof module) diff --git a/lib/customize.js b/lib/customize.js index 60d5cdc..d8a45f4 100644 --- a/lib/customize.js +++ b/lib/customize.js @@ -46,10 +46,17 @@ Puck.eval(data,callback) There is also: -Util.readStorageFile(filename,callback) -Util.eraseStorageFile(filename,callback) -Util.showModal(title) -Util.hideModal() +Util.close() // close this window +Util.readStorage(filename,callback) // read a file from the Bangle, callback with string +Util.readStorageJSON(filename,callback) // read a file from the Bangle and parse JSON, callback with parsed object +Util.writeStorage(filename,data, callback) // write a file to the Bangle, callback when done +Util.eraseStorage(filename,callback) // erase a file on the Bangle +Util.readStorageFile(filename,callback) // read a StorageFile (not just a normal file) +Util.eraseStorageFile(filename,callback) // erase a StorageFile +saveFile(filename, mimeType, dataAsString) // pop up a dialog to save a file (needs it a mimeType like "application/json") +Util.saveCSV(filename, csvData) // pop up a dialog to save a CSV file of data +Util.showModal(title) // show a modal screen over everything in this window +Util.hideModal() // hide the modal from showModal */ function sendCustomizedApp(app) { console.log(" sending app"); @@ -99,6 +106,11 @@ const Util = { __idlookup[__id] = callback; window.postMessage({type:"readstorage",filename:filename,id:__id}); }, + readStorageJSON : function(filename,callback) { + __id++; + __idlookup[__id] = callback; + window.postMessage({type:"readstoragejson",filename:filename,id:__id}); + }, writeStorage : function(filename,data,callback) { __id++; __idlookup[__id] = callback; @@ -167,6 +179,7 @@ window.addEventListener("message", function(event) { msg.type=="writersp" || msg.type=="readstoragefilersp" || msg.type=="readstoragersp" || + msg.type=="readstoragejsonrsp" || msg.type=="writestoragersp") { let cb = __idlookup[msg.id]; delete __idlookup[msg.id]; diff --git a/lib/interface.js b/lib/interface.js index ea892f4..3b74f7a 100644 --- a/lib/interface.js +++ b/lib/interface.js @@ -31,10 +31,17 @@ Puck.eval(data,callback) There is also: -Util.readStorageFile(filename,callback) -Util.eraseStorageFile(filename,callback) -Util.showModal(title) -Util.hideModal() +Util.close() // close this window +Util.readStorage(filename,callback) // read a file from the Bangle, callback with string +Util.readStorageJSON(filename,callback) // read a file from the Bangle and parse JSON, callback with parsed object +Util.writeStorage(filename,data, callback) // write a file to the Bangle, callback when done +Util.eraseStorage(filename,callback) // erase a file on the Bangle +Util.readStorageFile(filename,callback) // read a StorageFile (not just a normal file) +Util.eraseStorageFile(filename,callback) // erase a StorageFile +saveFile(filename, mimeType, dataAsString) // pop up a dialog to save a file (needs it a mimeType like "application/json") +Util.saveCSV(filename, csvData) // pop up a dialog to save a CSV file of data +Util.showModal(title) // show a modal screen over everything in this window +Util.hideModal() // hide the modal from showModal */ let __id = 0, __idlookup = []; const Puck = { @@ -76,6 +83,11 @@ const Util = { __idlookup[__id] = callback; window.postMessage({type:"readstorage",filename:filename,id:__id}); }, + readStorageJSON : function(filename,callback) { + __id++; + __idlookup[__id] = callback; + window.postMessage({type:"readstoragejson",filename:filename,id:__id}); + }, writeStorage : function(filename,data,callback) { __id++; __idlookup[__id] = callback; @@ -144,6 +156,7 @@ window.addEventListener("message", function(event) { msg.type=="writersp" || msg.type=="readstoragefilersp" || msg.type=="readstoragersp" || + msg.type=="readstoragejsonrsp" || msg.type=="writestoragersp") { let cb = __idlookup[msg.id]; delete __idlookup[msg.id];