Add GPS Recorder with companion code in Bangle App Loader that allows the data to be read back as a KML
parent
2bca5d4747
commit
a703e15455
79
README.md
79
README.md
|
|
@ -192,10 +192,12 @@ about the app.
|
||||||
"custom": "custom.html", // if supplied, apps/custom.html is loaded in an
|
"custom": "custom.html", // if supplied, apps/custom.html is loaded in an
|
||||||
// iframe, and it must post back an 'app' structure
|
// iframe, and it must post back an 'app' structure
|
||||||
// like this one with 'storage','name' and 'id' set up
|
// like this one with 'storage','name' and 'id' set up
|
||||||
|
// see below for more info
|
||||||
|
|
||||||
"interface": "interface.html", // if supplied, apps/interface.html is loaded in an
|
"interface": "interface.html", // if supplied, apps/interface.html is loaded in an
|
||||||
// iframe, and it may interact with the connected Bangle
|
// iframe, and it may interact with the connected Bangle
|
||||||
// to retrieve information from it
|
// to retrieve information from it
|
||||||
|
// see below for more info
|
||||||
|
|
||||||
"allow_emulator":true, // if 'app.js' will run in the emulator, set to true to
|
"allow_emulator":true, // if 'app.js' will run in the emulator, set to true to
|
||||||
// add an icon to allow your app to be tested
|
// add an icon to allow your app to be tested
|
||||||
|
|
@ -218,6 +220,83 @@ about the app.
|
||||||
* tags is used for grouping apps in the library, separate multiple entries by comma. Known tags are `tool`, `system`, `clock`, `game`, `sound`, `gps`, `widget`, `launcher` or empty.
|
* tags is used for grouping apps in the library, separate multiple entries by comma. Known tags are `tool`, `system`, `clock`, `game`, `sound`, `gps`, `widget`, `launcher` or empty.
|
||||||
* storage is used to identify the app files and how to handle them
|
* storage is used to identify the app files and how to handle them
|
||||||
|
|
||||||
|
### `apps.json`: `custom` element
|
||||||
|
|
||||||
|
Apps that can be customised need to define a `custom` element in `apps.json`,
|
||||||
|
which names an HTML file in that app's folder.
|
||||||
|
|
||||||
|
When `custom` is defined, the 'upload' button is replaced by a customize
|
||||||
|
button, and when clicked it opens the HTML page specified in an iframe.
|
||||||
|
|
||||||
|
In that HTML file you're then responsible for handling a button
|
||||||
|
press and calling `sendCustomizedApp` with your own customised
|
||||||
|
version of what's in `apps.json`:
|
||||||
|
|
||||||
|
```
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p><button id="upload" class="btn btn-primary">Upload</button></p>
|
||||||
|
<script src="../../lib/customize.js"></script>
|
||||||
|
<script>
|
||||||
|
document.getElementById("upload").addEventListener("click", function() {
|
||||||
|
sendCustomizedApp({
|
||||||
|
id : "7chname",
|
||||||
|
storage:[
|
||||||
|
{name:"-7chname", content:app_source_code},
|
||||||
|
{name:"+7chname", content:JSON.stringify({
|
||||||
|
name:"My app's name",
|
||||||
|
icon:"*7chname",
|
||||||
|
src:"-7chname"
|
||||||
|
})},
|
||||||
|
{name:"*7chname", content:'require("heatshrink").decompress(atob("mEwg...4"))', evaluate:true},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
This'll then be loaded in to the watch. See [apps/qrcode/grcode.html](the QR Code app)
|
||||||
|
for a clean example.
|
||||||
|
|
||||||
|
### `apps.json`: `interface` element
|
||||||
|
|
||||||
|
Apps that create data that can be read back can define a `interface` element in `apps.json`,
|
||||||
|
which names an HTML file in that app's folder.
|
||||||
|
|
||||||
|
When `interface` is defined, a `Download from App` button is added to
|
||||||
|
the app's description, and when clicked it opens the HTML page specified
|
||||||
|
in an iframe.
|
||||||
|
|
||||||
|
```
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script src="../../lib/interface.js"></script>
|
||||||
|
<div id="t">Loading...</div>
|
||||||
|
<script>
|
||||||
|
function onInit() {
|
||||||
|
Puck.eval("E.getTemperature()", temp=> {
|
||||||
|
document.getElementById("t").innerHTML = temp;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
When the page is ready a function called `onInit` is called,
|
||||||
|
and in that you can call `Puck.write` and `Puck.eval` to get
|
||||||
|
the data you require from Bangle.js.
|
||||||
|
|
||||||
|
See [apps/gpsrec/interface.html](the GPS Recorder) for a full example.
|
||||||
|
|
||||||
## Coding hints
|
## Coding hints
|
||||||
|
|
||||||
- Need to save state? Use the `E.on('kill',...)` event to save JSON to a file called `@7chname`, then load it at startup.
|
- Need to save state? Use the `E.on('kill',...)` event to save JSON to a file called `@7chname`, then load it at startup.
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@
|
||||||
<p>If ok, Click <button id="upload" class="btn btn-primary">Upload</button></p>
|
<p>If ok, Click <button id="upload" class="btn btn-primary">Upload</button></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="../../lib/customize.js"></script>
|
||||||
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet.js"></script>
|
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet.js"></script>
|
||||||
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
|
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
|
||||||
<script src="https://unpkg.com/osmtogeojson@2.2.12/osmtogeojson.js"></script>
|
<script src="https://unpkg.com/osmtogeojson@2.2.12/osmtogeojson.js"></script>
|
||||||
|
|
@ -202,8 +203,7 @@ g.clear();`;
|
||||||
});
|
});
|
||||||
var icon = `require("heatshrink").decompress(atob("mEwghC/AB0O/4AG8AXNgYXHmAXl94XH+AXNn4XH/wXW+YX/C6oWHAAIXN7sz9vdAAoXN9sznvuAAXf/vuC53jC4Xd7wXQ93jn3u9vv9vt7wXT/4tBAgIXQ7wvCC4PgC5sO6czIQJfBC6PumaPDC6wwCC50NYAJcBVgIDBCxrAFbgYXP7yoDF6TADL4YXPVAIXCRyAXC7wXW9zwBC6cNC9zABC4gWQC653CR4fQC6x3TF6gXXI4M9d6wAEC9EN73dAAZfQgczAAkwC/4XXAH4"))`;
|
var icon = `require("heatshrink").decompress(atob("mEwghC/AB0O/4AG8AXNgYXHmAXl94XH+AXNn4XH/wXW+YX/C6oWHAAIXN7sz9vdAAoXN9sznvuAAXf/vuC53jC4Xd7wXQ93jn3u9vv9vt7wXT/4tBAgIXQ7wvCC4PgC5sO6czIQJfBC6PumaPDC6wwCC50NYAJcBVgIDBCxrAFbgYXP7yoDF6TADL4YXPVAIXCRyAXC7wXW9zwBC6cNC9zABC4gWQC653CR4fQC6x3TF6gXXI4M9d6wAEC9EN73dAAZfQgczAAkwC/4XXAH4"))`;
|
||||||
|
|
||||||
|
sendCustomizedApp({
|
||||||
window.postMessage({
|
|
||||||
id : "beer",
|
id : "beer",
|
||||||
|
|
||||||
storage:[
|
storage:[
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,3 @@ function viewTrack(n) {
|
||||||
}
|
}
|
||||||
|
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
|
|
||||||
|
|
||||||
// f = require("Storage").open(".gpsrc"+n,"r");
|
|
||||||
// f.readLine()...
|
|
||||||
|
|
|
||||||
|
|
@ -3,34 +3,202 @@
|
||||||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div id="tracks"></div>
|
||||||
|
|
||||||
<p>Hello!</p>
|
<div class="modal active" id="status-modal">
|
||||||
|
<div class="modal-overlay"></div>
|
||||||
|
<div class="modal-container">
|
||||||
|
<div class="modal-header">
|
||||||
|
<div class="modal-title h5">Please wait</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="content">
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="../../lib/interface.js"></script>
|
||||||
<script>
|
<script>
|
||||||
var __id = 0, __idlookup = [];
|
var domTracks = document.getElementById("tracks");
|
||||||
var Puck = {
|
var domModal = document.getElementById("status-modal");
|
||||||
eval : function(data,callback) {
|
|
||||||
__id++;
|
|
||||||
__idlookup[__id] = callback;
|
|
||||||
window.postMessage({type:"eval",data:data,id:__id});
|
|
||||||
},write : function(data,callback) {
|
|
||||||
__id++;
|
|
||||||
__idlookup[__id] = callback;
|
|
||||||
window.postMessage({type:"write",data:data,id:__id});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
window.addEventListener("message", function(event) {
|
|
||||||
var msg = event.data;
|
|
||||||
if (msg.type=="evalrsp" || msg.type=="writersp") {
|
|
||||||
var cb = __idlookup[msg.id];
|
|
||||||
delete __idlookup[msg.id];
|
|
||||||
cb(msg.data);
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
Puck.eval("E.getTemperature()",function(d) {
|
function showModal(title) {
|
||||||
console.log("GOT: "+d);
|
domModal.querySelector(".content").innerHTML = title;
|
||||||
|
domModal.classList.add("active");
|
||||||
|
}
|
||||||
|
function hideModal(title) {
|
||||||
|
domModal.classList.remove("active");
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveKML(track,title) {
|
||||||
|
var kml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<kml xmlns="http://www.opengis.net/kml/2.2">
|
||||||
|
<Document>
|
||||||
|
<Placemark>
|
||||||
|
<name>${title}</name>
|
||||||
|
<LineString>
|
||||||
|
<coordinates>
|
||||||
|
${track.map(pt=>[pt.lon, pt.lat, pt.alt].join(",")).join(" \n")}
|
||||||
|
</coordinates>
|
||||||
|
</LineString>
|
||||||
|
</Placemark>
|
||||||
|
</Document>
|
||||||
|
</kml>`;
|
||||||
|
var a = document.createElement("a"),
|
||||||
|
file = new Blob([kml], {type: "application/vnd.google-earth.kml+xml"});
|
||||||
|
var url = URL.createObjectURL(file);
|
||||||
|
a.href = url;
|
||||||
|
a.download = title+".kml";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
setTimeout(function() {
|
||||||
|
document.body.removeChild(a);
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveGPX(track, title) {
|
||||||
|
var gpx = `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<gpx creator="Bangle.js" version="1.1">
|
||||||
|
<metadata>
|
||||||
|
<time>${track[0].date.toISOString()}</time>
|
||||||
|
</metadata>
|
||||||
|
<trk>
|
||||||
|
<name>${title}</name>
|
||||||
|
<trkseg>`;
|
||||||
|
track.forEach(pt=>{
|
||||||
|
gpx += `
|
||||||
|
<trkpt lat="${pt.lat}" lon="${pt.lon}">
|
||||||
|
<ele>${pt.alt}</ele>
|
||||||
|
<time>${pt.date.toISOString()}</time>
|
||||||
|
</trkpt>`;
|
||||||
});
|
});
|
||||||
|
gpx += `
|
||||||
|
</trkseg>
|
||||||
|
</trk>
|
||||||
|
</gpx>`;
|
||||||
|
var a = document.createElement("a"),
|
||||||
|
file = new Blob([gpx], {type: "application/gpx+xml"});
|
||||||
|
var url = URL.createObjectURL(file);
|
||||||
|
a.href = url;
|
||||||
|
a.download = title+".gpx";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
setTimeout(function() {
|
||||||
|
document.body.removeChild(a);
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function trackLineToObject(l, hasTrackNumber) {
|
||||||
|
var t = l.trim().split(",");
|
||||||
|
var n = hasTrackNumber ? 1 : 0;
|
||||||
|
var o = {
|
||||||
|
date : new Date(parseInt(t[n+0])),
|
||||||
|
lat : parseFloat(t[n+1]),
|
||||||
|
lon : parseFloat(t[n+2]),
|
||||||
|
alt : parseFloat(t[n+3])
|
||||||
|
};
|
||||||
|
if (hasTrackNumber)
|
||||||
|
o.number = t[0];
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadTrack(trackid, callback) {
|
||||||
|
showModal("Downloading Track...");
|
||||||
|
Puck.write(`\x10(function() {
|
||||||
|
var f = require("Storage").open(".gpsrc${trackid.toString(36)}","r");
|
||||||
|
var l = f.readLine();
|
||||||
|
while (l!==undefined) { Bluetooth.print(l); l = f.readLine(); }
|
||||||
|
})()\n`,tracklist=>{
|
||||||
|
hideModal();
|
||||||
|
var track = tracklist.trim().split("\n").map(l=>trackLineToObject(l,false));
|
||||||
|
callback(track);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function getTrackList() {
|
||||||
|
showModal("Loading Tracks...");
|
||||||
|
domTracks.innerHTML = "";
|
||||||
|
Puck.write(`\x10(function() {
|
||||||
|
for (var n=0;n<36;n++) {
|
||||||
|
var f = require("Storage").open(".gpsrc"+n.toString(36),"r");
|
||||||
|
var l = f.readLine();
|
||||||
|
if (l!==undefined)
|
||||||
|
Bluetooth.println(n+","+l.trim());
|
||||||
|
}
|
||||||
|
})()\n`,tracklist=>{
|
||||||
|
var trackLines = tracklist.trim().split("\n");
|
||||||
|
var html = `<div class="container">
|
||||||
|
<div class="columns">\n`;
|
||||||
|
trackLines.forEach(l => {
|
||||||
|
var track = trackLineToObject(l, true /*has track number*/);
|
||||||
|
html += `
|
||||||
|
<div class="column col-12">
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="card-title h5">Track ${track.number}</div>
|
||||||
|
<div class="card-subtitle text-gray">${track.date.toString().substr(0,24)}</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-image">
|
||||||
|
<iframe
|
||||||
|
width="100%"
|
||||||
|
height="250"
|
||||||
|
frameborder="0" style="border:0"
|
||||||
|
src="https://www.google.com/maps/embed/v1/place?key=AIzaSyBxTcwrrVOh2piz7EmIs1Xn4FsRxJWeVH4&q=${track.lat},${track.lon}&zoom=10" allowfullscreen>
|
||||||
|
</iframe>
|
||||||
|
</div>
|
||||||
|
<div class="card-body"></div>
|
||||||
|
<div class="card-footer">
|
||||||
|
<button class="btn btn-primary" trackid="${track.number}" task="downloadkml">Download KML</button>
|
||||||
|
<button class="btn btn-primary" trackid="${track.number}" task="downloadgpx">Download GPX</button>
|
||||||
|
<button class="btn btn-default" trackid="${track.number}" task="delete">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
if (trackLines.length==0) {
|
||||||
|
html += `
|
||||||
|
<div class="column col-12">
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="card-title h5">No tracks</div>
|
||||||
|
<div class="card-subtitle text-gray">No GPS tracks found</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
html += `
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
domTracks.innerHTML = html;
|
||||||
|
hideModal();
|
||||||
|
var buttons = domTracks.querySelectorAll("button");
|
||||||
|
for (var i=0;i<buttons.length;i++) {
|
||||||
|
buttons[i].addEventListener("click",event => {
|
||||||
|
var button = event.currentTarget;
|
||||||
|
var trackid = button.getAttribute("trackid");
|
||||||
|
var task = button.getAttribute("task");
|
||||||
|
if (task=="delete") {
|
||||||
|
showModal("Deleting Track...");
|
||||||
|
Puck.write(`\x10require("Storage").open(".gpsrc${trackid.toString(36)}","r").erase()\n`,()=>{
|
||||||
|
hideModal();
|
||||||
|
getTrackList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (task=="downloadkml") {
|
||||||
|
downloadTrack(trackid, track => saveKML(track, `Bangle.js Track ${trackid}`));
|
||||||
|
}
|
||||||
|
if (task=="downloadgpx") {
|
||||||
|
downloadTrack(trackid, track => saveGPX(track, `Bangle.js Track ${trackid}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function onInit() {
|
||||||
|
getTrackList();
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
<p>Try your QR Code: <div id="qrcode"></div></p>
|
<p>Try your QR Code: <div id="qrcode"></div></p>
|
||||||
<p>Click <button id="upload" class="btn btn-primary">Upload</button></p>
|
<p>Click <button id="upload" class="btn btn-primary">Upload</button></p>
|
||||||
|
|
||||||
|
<script src="../../lib/customize.js"></script>
|
||||||
<script src="../../lib/qrcode.min.js"></script><!-- https://davidshimjs.github.io/qrcodejs/ -->
|
<script src="../../lib/qrcode.min.js"></script><!-- https://davidshimjs.github.io/qrcodejs/ -->
|
||||||
<script src="https://espruino.github.io/EspruinoWebTools/heatshrink.js"></script>
|
<script src="https://espruino.github.io/EspruinoWebTools/heatshrink.js"></script>
|
||||||
<script src="https://espruino.github.io/EspruinoWebTools/imageconverter.js"></script>
|
<script src="https://espruino.github.io/EspruinoWebTools/imageconverter.js"></script>
|
||||||
|
|
@ -41,15 +41,13 @@ g.setColor(0,0,0);
|
||||||
g.drawString(url,120,230);
|
g.drawString(url,120,230);
|
||||||
g.setColor(1,1,1);
|
g.setColor(1,1,1);
|
||||||
`;
|
`;
|
||||||
console.log(app);
|
|
||||||
var json = JSON.stringify({
|
var json = JSON.stringify({
|
||||||
name:"QR Code",
|
name:"QR Code",
|
||||||
icon:"*qrcode",
|
icon:"*qrcode",
|
||||||
src:"-qrcode"
|
src:"-qrcode"
|
||||||
});
|
});
|
||||||
var icon = `require("heatshrink").decompress(atob("mEwgP/AEX8gE8nkAn4FSngCWF6xfYDgIABHAQFPDQXD4YgDApxNDMooFOAQIdDAqIvWfcYA="))`;
|
var icon = `require("heatshrink").decompress(atob("mEwgP/AEX8gE8nkAn4FSngCWF6xfYDgIABHAQFPDQXD4YgDApxNDMooFOAQIdDAqIvWfcYA="))`;
|
||||||
|
sendCustomizedApp({
|
||||||
window.postMessage({
|
|
||||||
id : "qrcode",
|
id : "qrcode",
|
||||||
|
|
||||||
storage:[
|
storage:[
|
||||||
|
|
@ -58,6 +56,7 @@ g.setColor(1,1,1);
|
||||||
{name:"*qrcode", content:icon, evaluate:true},
|
{name:"*qrcode", content:icon, evaluate:true},
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
<input class="form-input" type="file" id="fileLoader"/></p>
|
<input class="form-input" type="file" id="fileLoader"/></p>
|
||||||
<p><button id="upload" style="display:none" class="btn btn-primary">Upload</button></p>
|
<p><button id="upload" style="display:none" class="btn btn-primary">Upload</button></p>
|
||||||
<pre id="log"></pre>
|
<pre id="log"></pre>
|
||||||
|
<script src="../../lib/customize.js"></script>
|
||||||
<script>
|
<script>
|
||||||
var xmlText = "";
|
var xmlText = "";
|
||||||
var xmlDoc;
|
var xmlDoc;
|
||||||
|
|
@ -246,7 +247,7 @@ src:"-route"
|
||||||
});
|
});
|
||||||
var icon = `require("heatshrink").decompress(atob("mEwgIkhvgFE/wEDgOHAocDgYFEgOAAp4XEEYsB4w1E5hBKnByFKw8/AQNAAQP/4EAAIMB4HggBABHoNwCwUGE4kOgEYBAMAhk+hgIBAoM/hkEAoMIv8MC4QFChARCAoIMCDoQXChkcjA1EAoJBBg5dCJoJHDKYWAsCGD4AJBAAXBDYIlCsYFBGwUzPok+AokcsOOmIUCAogAWA=="))`;
|
var icon = `require("heatshrink").decompress(atob("mEwgIkhvgFE/wEDgOHAocDgYFEgOAAp4XEEYsB4w1E5hBKnByFKw8/AQNAAQP/4EAAIMB4HggBABHoNwCwUGE4kOgEYBAMAhk+hgIBAoM/hkEAoMIv8MC4QFChARCAoIMCDoQXChkcjA1EAoJBBg5dCJoJHDKYWAsCGD4AJBAAXBDYIlCsYFBGwUzPok+AokcsOOmIUCAogAWA=="))`;
|
||||||
|
|
||||||
window.postMessage({
|
sendCustomizedApp({
|
||||||
id : "route",
|
id : "route",
|
||||||
|
|
||||||
storage:[
|
storage:[
|
||||||
|
|
|
||||||
8
index.js
8
index.js
|
|
@ -149,6 +149,7 @@ function handleAppInterface(app) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
var iframe = modal.getElementsByTagName("iframe")[0];
|
var iframe = modal.getElementsByTagName("iframe")[0];
|
||||||
|
iframe.onload = function() {
|
||||||
var iwin = iframe.contentWindow;
|
var iwin = iframe.contentWindow;
|
||||||
iwin.addEventListener("message", function(event) {
|
iwin.addEventListener("message", function(event) {
|
||||||
var msg = event.data;
|
var msg = event.data;
|
||||||
|
|
@ -161,15 +162,18 @@ function handleAppInterface(app) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else if (msg.type=="write") {
|
} else if (msg.type=="write") {
|
||||||
Puck.write(msg.data, function() {
|
Puck.write(msg.data, function(result) {
|
||||||
iwin.postMessage({
|
iwin.postMessage({
|
||||||
type : "writersp",
|
type : "writersp",
|
||||||
|
data : result,
|
||||||
id : msg.id
|
id : msg.id
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, false);
|
}, false);
|
||||||
iframe.src = `apps/${app.id}/${app.interface}`
|
iwin.postMessage({type:"init"});
|
||||||
|
};
|
||||||
|
iframe.src = `apps/${app.id}/${app.interface}`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
/* Library for 'custom' HTML files that are to
|
||||||
|
be used from within BangleApps
|
||||||
|
|
||||||
|
See: README.md / `apps.json`: `custom` element
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Call with a JS object:
|
||||||
|
|
||||||
|
sendCustomizedApp({
|
||||||
|
id : "7chname",
|
||||||
|
|
||||||
|
storage:[
|
||||||
|
{name:"-7chname", content:app_source_code},
|
||||||
|
{name:"+7chname", content:JSON.stringify({
|
||||||
|
name:"My app's name",
|
||||||
|
icon:"*7chname",
|
||||||
|
src:"-7chname"
|
||||||
|
})},
|
||||||
|
{name:"*7chname", content:'require("heatshrink").decompress(atob("mEwg...4"))', evaluate:true},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
function sendCustomizedApp(app) {
|
||||||
|
window.postMessage(app);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* Library for 'interface' HTML files that are to
|
||||||
|
be used from within BangleApps
|
||||||
|
|
||||||
|
See: README.md / `apps.json`: `interface` element
|
||||||
|
|
||||||
|
This exposes a 'Puck' object like the puck.js library,
|
||||||
|
and calls `onInit` when it's ready. `Puck` can be used
|
||||||
|
for sending/receiving data to the correctly connected
|
||||||
|
device with Puck.eval/write.
|
||||||
|
*/
|
||||||
|
var __id = 0, __idlookup = [];
|
||||||
|
var Puck = {
|
||||||
|
eval : function(data,callback) {
|
||||||
|
__id++;
|
||||||
|
__idlookup[__id] = callback;
|
||||||
|
window.postMessage({type:"eval",data:data,id:__id});
|
||||||
|
},write : function(data,callback) {
|
||||||
|
__id++;
|
||||||
|
__idlookup[__id] = callback;
|
||||||
|
window.postMessage({type:"write",data:data,id:__id});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener("message", function(event) {
|
||||||
|
var msg = event.data;
|
||||||
|
if (msg.type=="init") {
|
||||||
|
onInit();
|
||||||
|
} else if (msg.type=="evalrsp" || msg.type=="writersp") {
|
||||||
|
var cb = __idlookup[msg.id];
|
||||||
|
delete __idlookup[msg.id];
|
||||||
|
cb(msg.data);
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
Loading…
Reference in New Issue