Added more serious linting for app loader (#451)
parent
2fd2caaacc
commit
324c93de36
|
|
@ -0,0 +1 @@
|
|||
js/espruinotools.js
|
||||
|
|
@ -1 +0,0 @@
|
|||
espruinotools.js
|
||||
|
|
@ -10,10 +10,40 @@
|
|||
{
|
||||
"SwitchCase": 1
|
||||
}
|
||||
]
|
||||
],
|
||||
"no-undef": "warn",
|
||||
"no-redeclare": "warn",
|
||||
"no-var": "warn",
|
||||
"no-unused-vars":"off" // we define stuff to use in other scripts
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"globals": {
|
||||
"btoa": "writable",
|
||||
"Espruino": "writable",
|
||||
|
||||
"htmlElement": "readonly",
|
||||
"Puck": "readonly",
|
||||
"escapeHtml": "readonly",
|
||||
"htmlToArray": "readonly",
|
||||
"heatshrink": "readonly",
|
||||
"Puck": "readonly",
|
||||
"Promise": "readonly",
|
||||
"Comms": "readonly",
|
||||
"Progress": "readonly",
|
||||
"showToast": "readonly",
|
||||
"showPrompt": "readonly",
|
||||
"httpGet": "readonly",
|
||||
"getVersionInfo": "readonly",
|
||||
"AppInfo": "readonly",
|
||||
"marked": "readonly",
|
||||
"appSorter": "readonly",
|
||||
"Uint8Array" : "readonly",
|
||||
"SETTINGS" : "readonly",
|
||||
"globToRegex" : "readonly",
|
||||
"toJS" : "readonly"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,16 +7,16 @@ if (typeof btoa==="undefined") {
|
|||
|
||||
// Converts a string into most efficient way to send to Espruino (either json, base64, or compressed base64)
|
||||
function toJS(txt) {
|
||||
var json = JSON.stringify(txt);
|
||||
var b64 = "atob("+JSON.stringify(btoa(json))+")";
|
||||
var js = b64.length < json.length ? b64 : json;
|
||||
let json = JSON.stringify(txt);
|
||||
let b64 = "atob("+JSON.stringify(btoa(json))+")";
|
||||
let js = b64.length < json.length ? b64 : json;
|
||||
|
||||
if (typeof heatshrink !== "undefined") {
|
||||
var ua = new Uint8Array(txt.length);
|
||||
for (var i=0;i<txt.length;i++) ua[i] = txt.charCodeAt(i);
|
||||
var c = heatshrink.compress(ua);
|
||||
var cs = "";
|
||||
for (var i=0;i<c.length;i++)
|
||||
let ua = new Uint8Array(txt.length);
|
||||
for (let i=0;i<txt.length;i++) ua[i] = txt.charCodeAt(i);
|
||||
let c = heatshrink.compress(ua);
|
||||
let cs = "";
|
||||
for (let i=0;i<c.length;i++)
|
||||
cs += String.fromCharCode(c[i]);
|
||||
cs = 'require("heatshrink").decompress(atob("'+btoa(cs)+'"))';
|
||||
// if it's more than a little smaller, use compressed version
|
||||
|
|
@ -30,7 +30,7 @@ function toJS(txt) {
|
|||
if ("undefined"!=typeof module)
|
||||
Espruino = require("./espruinotools.js");
|
||||
|
||||
var AppInfo = {
|
||||
let AppInfo = {
|
||||
/* Get files needed for app.
|
||||
options = {
|
||||
fileGetter : callback for getting URL,
|
||||
|
|
@ -79,9 +79,9 @@ var AppInfo = {
|
|||
} else {
|
||||
let code = storageFile.content;
|
||||
// write code in chunks, in case it is too big to fit in RAM (fix #157)
|
||||
var CHUNKSIZE = 4096;
|
||||
let CHUNKSIZE = 4096;
|
||||
storageFile.cmd = `\x10require('Storage').write(${JSON.stringify(storageFile.name)},${toJS(code.substr(0,CHUNKSIZE))},0,${code.length});`;
|
||||
for (var i=CHUNKSIZE;i<code.length;i+=CHUNKSIZE)
|
||||
for (let i=CHUNKSIZE;i<code.length;i+=CHUNKSIZE)
|
||||
storageFile.cmd += `\n\x10require('Storage').write(${JSON.stringify(storageFile.name)},${toJS(code.substr(i,CHUNKSIZE))},${i});`;
|
||||
}
|
||||
});
|
||||
|
|
@ -91,12 +91,12 @@ var AppInfo = {
|
|||
},
|
||||
createAppJSON : (app, fileContents) => {
|
||||
return new Promise((resolve,reject) => {
|
||||
var appJSONName = app.id+".info";
|
||||
let appJSONName = app.id+".info";
|
||||
// Check we don't already have a JSON file!
|
||||
var appJSONFile = fileContents.find(f=>f.name==appJSONName);
|
||||
let appJSONFile = fileContents.find(f=>f.name==appJSONName);
|
||||
if (appJSONFile) reject("App JSON file explicitly specified!");
|
||||
// Now actually create the app JSON
|
||||
var json = {
|
||||
let json = {
|
||||
id : app.id
|
||||
};
|
||||
if (app.shortName) json.name = app.shortName;
|
||||
|
|
@ -108,7 +108,7 @@ var AppInfo = {
|
|||
json.icon = app.id+".img";
|
||||
if (app.sortorder) json.sortorder = app.sortorder;
|
||||
if (app.version) json.version = app.version;
|
||||
var fileList = fileContents.map(storageFile=>storageFile.name);
|
||||
let fileList = fileContents.map(storageFile=>storageFile.name);
|
||||
fileList.unshift(appJSONName); // do we want this? makes life easier!
|
||||
json.files = fileList.join(",");
|
||||
if ('data' in app) {
|
||||
|
|
|
|||
44
js/comms.js
44
js/comms.js
|
|
@ -1,7 +1,7 @@
|
|||
Puck.debug=3;
|
||||
|
||||
// FIXME: use UART lib so that we handle errors properly
|
||||
var Comms = {
|
||||
let Comms = {
|
||||
reset : (opt) => new Promise((resolve,reject) => {
|
||||
Puck.write(`\x03\x10reset(${opt=="wipe"?"1":""});\n`, (result) => {
|
||||
if (result===null) return reject("Connection failed");
|
||||
|
|
@ -16,13 +16,13 @@ var Comms = {
|
|||
}).then(fileContents => {
|
||||
return new Promise((resolve,reject) => {
|
||||
console.log("uploadApp",fileContents.map(f=>f.name).join(", "));
|
||||
var maxBytes = fileContents.reduce((b,f)=>b+f.cmd.length, 0)||1;
|
||||
var currentBytes = 0;
|
||||
let maxBytes = fileContents.reduce((b,f)=>b+f.cmd.length, 0)||1;
|
||||
let currentBytes = 0;
|
||||
|
||||
var appInfoFileName = app.id+".info";
|
||||
var appInfoFile = fileContents.find(f=>f.name==appInfoFileName);
|
||||
let appInfoFileName = app.id+".info";
|
||||
let appInfoFile = fileContents.find(f=>f.name==appInfoFileName);
|
||||
if (!appInfoFile) reject(`${appInfoFileName} not found`);
|
||||
var appInfo = JSON.parse(appInfoFile.content);
|
||||
let appInfo = JSON.parse(appInfoFile.content);
|
||||
|
||||
// Upload each file one at a time
|
||||
function doUploadFiles() {
|
||||
|
|
@ -35,14 +35,14 @@ var Comms = {
|
|||
});
|
||||
return;
|
||||
}
|
||||
var f = fileContents.shift();
|
||||
let f = fileContents.shift();
|
||||
console.log(`Upload ${f.name} => ${JSON.stringify(f.content)}`);
|
||||
// Chould check CRC here if needed instead of returning 'OK'...
|
||||
// E.CRC32(require("Storage").read(${JSON.stringify(app.name)}))
|
||||
var cmds = f.cmd.split("\n");
|
||||
let cmds = f.cmd.split("\n");
|
||||
function uploadCmd() {
|
||||
if (!cmds.length) return doUploadFiles();
|
||||
var cmd = cmds.shift();
|
||||
let cmd = cmds.shift();
|
||||
Progress.show({
|
||||
min:currentBytes / maxBytes,
|
||||
max:(currentBytes+cmd.length) / maxBytes});
|
||||
|
|
@ -84,7 +84,7 @@ var Comms = {
|
|||
Progress.hide({sticky:true});
|
||||
return reject("");
|
||||
}
|
||||
Puck.write('\x10Bluetooth.print("[");require("Storage").list(/\.info$/).forEach(f=>{var j=require("Storage").readJSON(f,1)||{};j.id=f.slice(0,-5);Bluetooth.print(JSON.stringify(j)+",")});Bluetooth.println("0]")\n', (appList,err) => {
|
||||
Puck.write('\x10Bluetooth.print("[");require("Storage").list(/\\.info$/).forEach(f=>{var j=require("Storage").readJSON(f,1)||{};j.id=f.slice(0,-5);Bluetooth.print(JSON.stringify(j)+",")});Bluetooth.println("0]")\n', (appList,err) => {
|
||||
Progress.hide({sticky:true});
|
||||
try {
|
||||
appList = JSON.parse(appList);
|
||||
|
|
@ -152,9 +152,9 @@ var Comms = {
|
|||
},
|
||||
setTime : () => {
|
||||
return new Promise((resolve,reject) => {
|
||||
var d = new Date();
|
||||
var tz = d.getTimezoneOffset()/-60
|
||||
var cmd = '\x03\x10setTime('+(d.getTime()/1000)+');';
|
||||
let d = new Date();
|
||||
let tz = d.getTimezoneOffset()/-60
|
||||
let cmd = '\x03\x10setTime('+(d.getTime()/1000)+');';
|
||||
// in 1v93 we have timezones too
|
||||
cmd += 'E.setTimeZone('+tz+');';
|
||||
cmd += "(s=>{s&&(s.timezone="+tz+")&&require('Storage').write('setting.json',s);})(require('Storage').readJSON('setting.json',1))\n";
|
||||
|
|
@ -165,17 +165,17 @@ var Comms = {
|
|||
});
|
||||
},
|
||||
disconnectDevice: () => {
|
||||
var connection = Puck.getConnection();
|
||||
let connection = Puck.getConnection();
|
||||
|
||||
if (!connection) return;
|
||||
|
||||
connection.close();
|
||||
},
|
||||
watchConnectionChange : cb => {
|
||||
var connected = Puck.isConnected();
|
||||
let connected = Puck.isConnected();
|
||||
|
||||
//TODO Switch to an event listener when Puck will support it
|
||||
var interval = setInterval(() => {
|
||||
let interval = setInterval(() => {
|
||||
if (connected === Puck.isConnected()) return;
|
||||
|
||||
connected = Puck.isConnected();
|
||||
|
|
@ -220,20 +220,20 @@ var Comms = {
|
|||
readStorageFile : (filename) => { // StorageFiles are different to normal storage entries
|
||||
return new Promise((resolve,reject) => {
|
||||
// Use "\xFF" to signal end of file (can't occur in files anyway)
|
||||
var fileContent = "";
|
||||
var fileSize = undefined;
|
||||
var connection = Puck.getConnection();
|
||||
let fileContent = "";
|
||||
let fileSize = undefined;
|
||||
let connection = Puck.getConnection();
|
||||
connection.received = "";
|
||||
connection.cb = function(d) {
|
||||
var finished = false;
|
||||
var eofIndex = d.indexOf("\xFF");
|
||||
let finished = false;
|
||||
let eofIndex = d.indexOf("\xFF");
|
||||
if (eofIndex>=0) {
|
||||
finished = true;
|
||||
d = d.substr(0,eofIndex);
|
||||
}
|
||||
fileContent += d;
|
||||
if (fileSize === undefined) {
|
||||
var newLineIdx = fileContent.indexOf("\n");
|
||||
let newLineIdx = fileContent.indexOf("\n");
|
||||
if (newLineIdx>=0) {
|
||||
fileSize = parseInt(fileContent.substr(0,newLineIdx));
|
||||
console.log("File size is "+fileSize);
|
||||
|
|
|
|||
158
js/index.js
158
js/index.js
|
|
@ -1,12 +1,12 @@
|
|||
var appJSON = []; // List of apps and info from apps.json
|
||||
var appsInstalled = []; // list of app JSON
|
||||
var appSortInfo = {}; // list of data to sort by, from appdates.csv { created, modified }
|
||||
var files = []; // list of files on Bangle
|
||||
var DEFAULTSETTINGS = {
|
||||
let appJSON = []; // List of apps and info from apps.json
|
||||
let appsInstalled = []; // list of app JSON
|
||||
let appSortInfo = {}; // list of data to sort by, from appdates.csv { created, modified }
|
||||
let files = []; // list of files on Bangle
|
||||
let DEFAULTSETTINGS = {
|
||||
pretokenise : true,
|
||||
favourites : ["boot","launch","setting"]
|
||||
};
|
||||
var SETTINGS = JSON.parse(JSON.stringify(DEFAULTSETTINGS)); // clone
|
||||
let SETTINGS = JSON.parse(JSON.stringify(DEFAULTSETTINGS)); // clone
|
||||
|
||||
httpGet("apps.json").then(apps=>{
|
||||
try {
|
||||
|
|
@ -23,7 +23,7 @@ httpGet("apps.json").then(apps=>{
|
|||
httpGet("appdates.csv").then(csv=>{
|
||||
document.querySelector(".sort-nav").classList.remove("hidden");
|
||||
csv.split("\n").forEach(line=>{
|
||||
var l = line.split(",");
|
||||
let l = line.split(",");
|
||||
appSortInfo[l[0]] = {
|
||||
created : Date.parse(l[1]),
|
||||
modified : Date.parse(l[2])
|
||||
|
|
@ -35,7 +35,7 @@ httpGet("appdates.csv").then(csv=>{
|
|||
|
||||
// =========================================== Top Navigation
|
||||
function showChangeLog(appid) {
|
||||
var app = appNameToApp(appid);
|
||||
let app = appNameToApp(appid);
|
||||
function show(contents) {
|
||||
showPrompt(app.name+" Change Log",contents,{ok:true}).catch(()=>{});
|
||||
}
|
||||
|
|
@ -43,9 +43,9 @@ function showChangeLog(appid) {
|
|||
then(show).catch(()=>show("No Change Log available"));
|
||||
}
|
||||
function showReadme(appid) {
|
||||
var app = appNameToApp(appid);
|
||||
var appPath = `apps/${appid}/`;
|
||||
var markedOptions = { baseUrl : appPath };
|
||||
let app = appNameToApp(appid);
|
||||
let appPath = `apps/${appid}/`;
|
||||
let markedOptions = { baseUrl : appPath };
|
||||
function show(contents) {
|
||||
if (!contents) return;
|
||||
showPrompt(app.name + " Documentation", marked(contents, markedOptions), {ok: true}, false).catch(() => {});
|
||||
|
|
@ -56,7 +56,7 @@ function handleCustomApp(appTemplate) {
|
|||
// Pops up an IFRAME that allows an app to be customised
|
||||
if (!appTemplate.custom) throw new Error("App doesn't have custom HTML");
|
||||
return new Promise((resolve,reject) => {
|
||||
var modal = htmlElement(`<div class="modal active">
|
||||
let modal = htmlElement(`<div class="modal active">
|
||||
<a href="#close" class="modal-overlay " aria-label="Close"></a>
|
||||
<div class="modal-container" style="height:100%">
|
||||
<div class="modal-header">
|
||||
|
|
@ -79,10 +79,10 @@ function handleCustomApp(appTemplate) {
|
|||
});
|
||||
});
|
||||
|
||||
var iframe = modal.getElementsByTagName("iframe")[0];
|
||||
let iframe = modal.getElementsByTagName("iframe")[0];
|
||||
iframe.contentWindow.addEventListener("message", function(event) {
|
||||
var appFiles = event.data;
|
||||
var app = JSON.parse(JSON.stringify(appTemplate)); // clone template
|
||||
let appFiles = event.data;
|
||||
let app = JSON.parse(JSON.stringify(appTemplate)); // clone template
|
||||
// copy extra keys from appFiles
|
||||
Object.keys(appFiles).forEach(k => {
|
||||
if (k!="storage") app[k] = appFiles[k]
|
||||
|
|
@ -108,7 +108,7 @@ function handleAppInterface(app) {
|
|||
// IFRAME interface window that can be used to get data from the app
|
||||
if (!app.interface) throw new Error("App doesn't have interface HTML");
|
||||
return new Promise((resolve,reject) => {
|
||||
var modal = htmlElement(`<div class="modal active">
|
||||
let modal = htmlElement(`<div class="modal active">
|
||||
<a href="#close" class="modal-overlay " aria-label="Close"></a>
|
||||
<div class="modal-container" style="height:100%">
|
||||
<div class="modal-header">
|
||||
|
|
@ -130,11 +130,11 @@ function handleAppInterface(app) {
|
|||
//reject("Window closed");
|
||||
});
|
||||
});
|
||||
var iframe = modal.getElementsByTagName("iframe")[0];
|
||||
let iframe = modal.getElementsByTagName("iframe")[0];
|
||||
iframe.onload = function() {
|
||||
var iwin = iframe.contentWindow;
|
||||
let iwin = iframe.contentWindow;
|
||||
iwin.addEventListener("message", function(event) {
|
||||
var msg = event.data;
|
||||
let msg = event.data;
|
||||
if (msg.type=="eval") {
|
||||
Puck.eval(msg.data, function(result) {
|
||||
iwin.postMessage({
|
||||
|
|
@ -168,7 +168,7 @@ function handleAppInterface(app) {
|
|||
}
|
||||
|
||||
function changeAppFavourite(favourite, app) {
|
||||
var favourites = SETTINGS.favourites;
|
||||
let favourites = SETTINGS.favourites;
|
||||
if (favourite) {
|
||||
SETTINGS.favourites = SETTINGS.favourites.concat([app.id]);
|
||||
} else {
|
||||
|
|
@ -197,29 +197,29 @@ function showTab(tabname) {
|
|||
// =========================================== Library
|
||||
|
||||
// Can't use chip.attributes.filterid.value here because Safari/Apple's WebView doesn't handle it
|
||||
var chips = Array.from(document.querySelectorAll('.filter-nav .chip')).map(chip => chip.getAttribute("filterid"));
|
||||
var hash = window.location.hash ? window.location.hash.slice(1) : '';
|
||||
let chips = Array.from(document.querySelectorAll('.filter-nav .chip')).map(chip => chip.getAttribute("filterid"));
|
||||
let hash = window.location.hash ? window.location.hash.slice(1) : '';
|
||||
|
||||
var activeFilter = !!~chips.indexOf(hash) ? hash : '';
|
||||
var activeSort = '';
|
||||
var currentSearch = activeFilter ? '' : hash;
|
||||
let activeFilter = ~chips.indexOf(hash) ? hash : '';
|
||||
let activeSort = '';
|
||||
let currentSearch = activeFilter ? '' : hash;
|
||||
|
||||
function refreshFilter(){
|
||||
var filtersContainer = document.querySelector("#librarycontainer .filter-nav");
|
||||
let filtersContainer = document.querySelector("#librarycontainer .filter-nav");
|
||||
filtersContainer.querySelector('.active').classList.remove('active');
|
||||
if(activeFilter) filtersContainer.querySelector('.chip[filterid="'+activeFilter+'"]').classList.add('active');
|
||||
else filtersContainer.querySelector('.chip[filterid]').classList.add('active');
|
||||
}
|
||||
function refreshSort(){
|
||||
var sortContainer = document.querySelector("#librarycontainer .sort-nav");
|
||||
let sortContainer = document.querySelector("#librarycontainer .sort-nav");
|
||||
sortContainer.querySelector('.active').classList.remove('active');
|
||||
if(activeSort) sortContainer.querySelector('.chip[sortid="'+activeSort+'"]').classList.add('active');
|
||||
else sortContainer.querySelector('.chip[sortid]').classList.add('active');
|
||||
}
|
||||
function refreshLibrary() {
|
||||
var panelbody = document.querySelector("#librarycontainer .panel-body");
|
||||
var visibleApps = appJSON;
|
||||
var favourites = SETTINGS.favourites;
|
||||
let panelbody = document.querySelector("#librarycontainer .panel-body");
|
||||
let visibleApps = appJSON;
|
||||
let favourites = SETTINGS.favourites;
|
||||
|
||||
if (activeFilter) {
|
||||
if ( activeFilter == "favourites" ) {
|
||||
|
|
@ -241,17 +241,17 @@ function refreshLibrary() {
|
|||
}
|
||||
|
||||
panelbody.innerHTML = visibleApps.map((app,idx) => {
|
||||
var appInstalled = appsInstalled.find(a=>a.id==app.id);
|
||||
var version = getVersionInfo(app, appInstalled);
|
||||
var versionInfo = version.text;
|
||||
let appInstalled = appsInstalled.find(a=>a.id==app.id);
|
||||
let version = getVersionInfo(app, appInstalled);
|
||||
let versionInfo = version.text;
|
||||
if (versionInfo) versionInfo = " <small>("+versionInfo+")</small>";
|
||||
var readme = `<a class="c-hand" onclick="showReadme('${app.id}')">Read more...</a>`;
|
||||
var favourite = favourites.find(e => e == app.id);
|
||||
let readme = `<a class="c-hand" onclick="showReadme('${app.id}')">Read more...</a>`;
|
||||
let favourite = favourites.find(e => e == app.id);
|
||||
|
||||
var username = "espruino";
|
||||
var githubMatch = window.location.href.match(/\/(\w+)\.github\.io/);
|
||||
let username = "espruino";
|
||||
let githubMatch = window.location.href.match(/\/(\w+)\.github\.io/);
|
||||
if(githubMatch) username = githubMatch[1];
|
||||
var url = `https://github.com/${username}/BangleApps/tree/master/apps/${app.id}`;
|
||||
let url = `https://github.com/${username}/BangleApps/tree/master/apps/${app.id}`;
|
||||
|
||||
return `<div class="tile column col-6 col-sm-12 col-xs-12">
|
||||
<div class="tile-icon">
|
||||
|
|
@ -274,27 +274,27 @@ function refreshLibrary() {
|
|||
</div>
|
||||
`;}).join("");
|
||||
// set badge up top
|
||||
var tab = document.querySelector("#tab-librarycontainer a");
|
||||
let tab = document.querySelector("#tab-librarycontainer a");
|
||||
tab.classList.add("badge");
|
||||
tab.setAttribute("data-badge", appJSON.length);
|
||||
htmlToArray(panelbody.getElementsByTagName("button")).forEach(button => {
|
||||
button.addEventListener("click",event => {
|
||||
var button = event.currentTarget;
|
||||
var icon = button.firstChild;
|
||||
var appid = button.getAttribute("appid");
|
||||
var app = appNameToApp(appid);
|
||||
let button = event.currentTarget;
|
||||
let icon = button.firstChild;
|
||||
let appid = button.getAttribute("appid");
|
||||
let app = appNameToApp(appid);
|
||||
if (!app) throw new Error("App "+appid+" not found");
|
||||
// check icon to figure out what we should do
|
||||
if (icon.classList.contains("icon-share")) {
|
||||
// emulator
|
||||
var file = app.storage.find(f=>f.name.endsWith('.js'));
|
||||
let file = app.storage.find(f=>f.name.endsWith('.js'));
|
||||
if (!file) {
|
||||
console.error("No entrypoint found for "+appid);
|
||||
return;
|
||||
}
|
||||
var baseurl = window.location.href;
|
||||
let baseurl = window.location.href;
|
||||
baseurl = baseurl.substr(0,baseurl.lastIndexOf("/"));
|
||||
var url = baseurl+"/apps/"+app.id+"/"+file.url;
|
||||
let url = baseurl+"/apps/"+app.id+"/"+file.url;
|
||||
window.open(`https://espruino.com/ide/emulator.html?codeurl=${url}&upload`);
|
||||
} else if (icon.classList.contains("icon-upload")) {
|
||||
// upload
|
||||
|
|
@ -421,7 +421,7 @@ function updateApp(app) {
|
|||
|
||||
|
||||
function appNameToApp(appName) {
|
||||
var app = appJSON.find(app=>app.id==appName);
|
||||
let app = appJSON.find(app=>app.id==appName);
|
||||
if (app) return app;
|
||||
/* If app not known, add just one file
|
||||
which is the JSON - so we'll remove it from
|
||||
|
|
@ -436,8 +436,8 @@ function appNameToApp(appName) {
|
|||
}
|
||||
|
||||
function showLoadingIndicator(id) {
|
||||
var panelbody = document.querySelector(`#${id} .panel-body`);
|
||||
var tab = document.querySelector(`#tab-${id} a`);
|
||||
let panelbody = document.querySelector(`#${id} .panel-body`);
|
||||
let tab = document.querySelector(`#tab-${id} a`);
|
||||
// set badge up top
|
||||
tab.classList.add("badge");
|
||||
tab.setAttribute("data-badge", "");
|
||||
|
|
@ -446,9 +446,9 @@ function showLoadingIndicator(id) {
|
|||
}
|
||||
|
||||
function getAppsToUpdate() {
|
||||
var appsToUpdate = [];
|
||||
let appsToUpdate = [];
|
||||
appsInstalled.forEach(appInstalled => {
|
||||
var app = appNameToApp(appInstalled.id);
|
||||
let app = appNameToApp(appInstalled.id);
|
||||
if (app.version != appInstalled.version)
|
||||
appsToUpdate.push(app);
|
||||
});
|
||||
|
|
@ -456,14 +456,14 @@ function getAppsToUpdate() {
|
|||
}
|
||||
|
||||
function refreshMyApps() {
|
||||
var panelbody = document.querySelector("#myappscontainer .panel-body");
|
||||
let panelbody = document.querySelector("#myappscontainer .panel-body");
|
||||
panelbody.innerHTML = appsInstalled.map(appInstalled => {
|
||||
var app = appNameToApp(appInstalled.id);
|
||||
var version = getVersionInfo(app, appInstalled);
|
||||
var username = "espruino";
|
||||
var githubMatch = window.location.href.match(/\/(\w+)\.github\.io/);
|
||||
let app = appNameToApp(appInstalled.id);
|
||||
let version = getVersionInfo(app, appInstalled);
|
||||
let username = "espruino";
|
||||
let githubMatch = window.location.href.match(/\/(\w+)\.github\.io/);
|
||||
if(githubMatch) username = githubMatch[1];
|
||||
var url = `https://github.com/${username}/BangleApps/tree/master/apps/${app.id}`;
|
||||
let url = `https://github.com/${username}/BangleApps/tree/master/apps/${app.id}`;
|
||||
return `<div class="tile column col-6 col-sm-12 col-xs-12">
|
||||
<div class="tile-icon">
|
||||
<figure class="avatar"><img src="apps/${app.icon?`${app.id}/${app.icon}`:"unknown.png"}" alt="${escapeHtml(app.name)}"></figure>
|
||||
|
|
@ -482,10 +482,10 @@ function refreshMyApps() {
|
|||
`}).join("");
|
||||
htmlToArray(panelbody.getElementsByTagName("button")).forEach(button => {
|
||||
button.addEventListener("click",event => {
|
||||
var button = event.currentTarget;
|
||||
var icon = button.firstChild;
|
||||
var appid = button.getAttribute("appid");
|
||||
var app = appNameToApp(appid);
|
||||
let button = event.currentTarget;
|
||||
let icon = button.firstChild;
|
||||
let appid = button.getAttribute("appid");
|
||||
let app = appNameToApp(appid);
|
||||
if (!app) throw new Error("App "+appid+" not found");
|
||||
// check icon to figure out what we should do
|
||||
if (icon.classList.contains("icon-delete")) removeApp(app);
|
||||
|
|
@ -493,9 +493,9 @@ function refreshMyApps() {
|
|||
if (icon.classList.contains("icon-download")) handleAppInterface(app);
|
||||
});
|
||||
});
|
||||
var appsToUpdate = getAppsToUpdate();
|
||||
var tab = document.querySelector("#tab-myappscontainer a");
|
||||
var updateApps = document.querySelector("#myappscontainer .updateapps");
|
||||
let appsToUpdate = getAppsToUpdate();
|
||||
let tab = document.querySelector("#tab-myappscontainer a");
|
||||
let updateApps = document.querySelector("#myappscontainer .updateapps");
|
||||
if (appsToUpdate.length) {
|
||||
updateApps.innerHTML = `Update ${appsToUpdate.length} apps`;
|
||||
updateApps.classList.remove("hidden");
|
||||
|
|
@ -529,10 +529,10 @@ function getInstalledApps(refresh) {
|
|||
|
||||
/// Removes everything and install the given apps, eg: installMultipleApps(["boot","mclock"], "minimal")
|
||||
function installMultipleApps(appIds, promptName) {
|
||||
var apps = appIds.map( appid => appJSON.find(app=>app.id==appid) );
|
||||
let apps = appIds.map( appid => appJSON.find(app=>app.id==appid) );
|
||||
if (apps.some(x=>x===undefined))
|
||||
return Promise.reject("Not all apps found");
|
||||
var appCount = apps.length;
|
||||
let appCount = apps.length;
|
||||
return showPrompt("Install Defaults",`Remove everything and install ${promptName} apps?`).then(() => {
|
||||
return Comms.removeAllApps();
|
||||
}).then(()=>{
|
||||
|
|
@ -541,7 +541,7 @@ function installMultipleApps(appIds, promptName) {
|
|||
showToast(`Existing apps removed. Installing ${appCount} apps...`);
|
||||
return new Promise((resolve,reject) => {
|
||||
function upload() {
|
||||
var app = apps.shift();
|
||||
let app = apps.shift();
|
||||
if (app===undefined) return resolve();
|
||||
Progress.show({title:`${app.name} (${appCount-apps.length}/${appCount})`,sticky:true});
|
||||
Comms.uploadApp(app,"skip_reset").then((appJSON) => {
|
||||
|
|
@ -564,7 +564,7 @@ function installMultipleApps(appIds, promptName) {
|
|||
});
|
||||
}
|
||||
|
||||
var connectMyDeviceBtn = document.getElementById("connectmydevice");
|
||||
let connectMyDeviceBtn = document.getElementById("connectmydevice");
|
||||
|
||||
function handleConnectionChange(connected) {
|
||||
connectMyDeviceBtn.textContent = connected ? 'Disconnect' : 'Connect';
|
||||
|
|
@ -577,11 +577,11 @@ htmlToArray(document.querySelectorAll(".btn.refresh")).map(button => button.addE
|
|||
});
|
||||
}));
|
||||
htmlToArray(document.querySelectorAll(".btn.updateapps")).map(button => button.addEventListener("click", () => {
|
||||
var appsToUpdate = getAppsToUpdate();
|
||||
var count = appsToUpdate.length;
|
||||
let appsToUpdate = getAppsToUpdate();
|
||||
let count = appsToUpdate.length;
|
||||
function updater() {
|
||||
if (!appsToUpdate.length) return;
|
||||
var app = appsToUpdate.pop();
|
||||
let app = appsToUpdate.pop();
|
||||
return updateApp(app).then(function() {
|
||||
return updater();
|
||||
});
|
||||
|
|
@ -603,7 +603,7 @@ connectMyDeviceBtn.addEventListener("click", () => {
|
|||
});
|
||||
Comms.watchConnectionChange(handleConnectionChange);
|
||||
|
||||
var filtersContainer = document.querySelector("#librarycontainer .filter-nav");
|
||||
let filtersContainer = document.querySelector("#librarycontainer .filter-nav");
|
||||
filtersContainer.addEventListener('click', ({ target }) => {
|
||||
if (target.classList.contains('active')) return;
|
||||
|
||||
|
|
@ -613,14 +613,14 @@ filtersContainer.addEventListener('click', ({ target }) => {
|
|||
window.location.hash = activeFilter;
|
||||
});
|
||||
|
||||
var librarySearchInput = document.querySelector("#searchform input");
|
||||
let librarySearchInput = document.querySelector("#searchform input");
|
||||
librarySearchInput.value = currentSearch;
|
||||
librarySearchInput.addEventListener('input', evt => {
|
||||
currentSearch = evt.target.value.toLowerCase();
|
||||
refreshLibrary();
|
||||
});
|
||||
|
||||
var sortContainer = document.querySelector("#librarycontainer .sort-nav");
|
||||
let sortContainer = document.querySelector("#librarycontainer .sort-nav");
|
||||
sortContainer.addEventListener('click', ({ target }) => {
|
||||
if (target.classList.contains('active')) return;
|
||||
|
||||
|
|
@ -646,13 +646,13 @@ if (window.location.host=="banglejs.com") {
|
|||
}
|
||||
|
||||
// Settings
|
||||
var SETTINGS_HOOKS = {}; // stuff to get called when a setting is loaded
|
||||
let SETTINGS_HOOKS = {}; // stuff to get called when a setting is loaded
|
||||
/// Load settings and update controls
|
||||
function loadSettings() {
|
||||
var j = localStorage.getItem("settings");
|
||||
let j = localStorage.getItem("settings");
|
||||
if (typeof j != "string") return;
|
||||
try {
|
||||
var s = JSON.parse(j);
|
||||
let s = JSON.parse(j);
|
||||
Object.keys(s).forEach( k => {
|
||||
SETTINGS[k]=s[k];
|
||||
if (SETTINGS_HOOKS[k]) SETTINGS_HOOKS[k]();
|
||||
|
|
@ -668,7 +668,7 @@ function saveSettings() {
|
|||
}
|
||||
// Link in settings DOM elements
|
||||
function settingsCheckbox(id, name) {
|
||||
var setting = document.getElementById(id);
|
||||
let setting = document.getElementById(id);
|
||||
function update() {
|
||||
setting.checked = SETTINGS[name];
|
||||
}
|
||||
|
|
@ -720,7 +720,7 @@ document.getElementById("installdefault").addEventListener("click",event=>{
|
|||
|
||||
// Install all favourite apps in one go
|
||||
document.getElementById("installfavourite").addEventListener("click",event=>{
|
||||
var favApps = SETTINGS.favourites;
|
||||
let favApps = SETTINGS.favourites;
|
||||
installMultipleApps(favApps, "favourite").catch(err=>{
|
||||
Progress.hide({sticky:true});
|
||||
showToast("App Install failed, "+err,"error");
|
||||
|
|
|
|||
22
js/ui.js
22
js/ui.js
|
|
@ -1,7 +1,7 @@
|
|||
// General UI tools (progress bar, toast, prompt)
|
||||
|
||||
/// Handle progress bars
|
||||
var Progress = {
|
||||
let Progress = {
|
||||
domElement : null, // the DOM element
|
||||
sticky : false, // Progress.show({..., sticky:true}) don't remove until Progress.hide({sticky:true})
|
||||
interval : undefined, // the interval used if Progress.show({progress:"animate"})
|
||||
|
|
@ -18,11 +18,11 @@ var Progress = {
|
|||
}) */
|
||||
show : function(options) {
|
||||
options = options||{};
|
||||
var text = options.title;
|
||||
let text = options.title;
|
||||
if (options.sticky) Progress.sticky = true;
|
||||
if (options.min!==undefined) Progress.min = options.min;
|
||||
if (options.max!==undefined) Progress.max = options.max;
|
||||
var percent = options.percent;
|
||||
let percent = options.percent;
|
||||
if (percent!==undefined)
|
||||
percent = Progress.min*100 + (Progress.max-Progress.min)*percent;
|
||||
if (!Progress.domElement) {
|
||||
|
|
@ -39,7 +39,7 @@ var Progress = {
|
|||
percent = 0;
|
||||
}
|
||||
|
||||
var toastcontainer = document.getElementById("toastcontainer");
|
||||
let toastcontainer = document.getElementById("toastcontainer");
|
||||
Progress.domElement = htmlElement(`<div class="toast">
|
||||
${text ? `<div>${text}</div>`:``}
|
||||
<div class="bar bar-sm">
|
||||
|
|
@ -48,7 +48,7 @@ var Progress = {
|
|||
</div>`);
|
||||
toastcontainer.append(Progress.domElement);
|
||||
} else {
|
||||
var pt=document.getElementById("Progress.domElement");
|
||||
let pt=document.getElementById("Progress.domElement");
|
||||
pt.setAttribute("aria-valuenow",percent);
|
||||
pt.style.width = percent+"%";
|
||||
}
|
||||
|
|
@ -76,20 +76,20 @@ Puck.writeProgress = function(charsSent, charsTotal) {
|
|||
Progress.hide();
|
||||
return;
|
||||
}
|
||||
var percent = Math.round(charsSent*100/charsTotal);
|
||||
let percent = Math.round(charsSent*100/charsTotal);
|
||||
Progress.show({percent: percent});
|
||||
}
|
||||
|
||||
/// Show a 'toast' message for status
|
||||
function showToast(message, type) {
|
||||
// toast-primary, toast-success, toast-warning or toast-error
|
||||
var style = "toast-primary";
|
||||
let style = "toast-primary";
|
||||
if (type=="success") style = "toast-success";
|
||||
else if (type=="error") style = "toast-error";
|
||||
else if (type=="warning") style = "toast-warning";
|
||||
else if (type!==undefined) console.log("showToast: unknown toast "+type);
|
||||
var toastcontainer = document.getElementById("toastcontainer");
|
||||
var msgDiv = htmlElement(`<div class="toast ${style}"></div>`);
|
||||
let toastcontainer = document.getElementById("toastcontainer");
|
||||
let msgDiv = htmlElement(`<div class="toast ${style}"></div>`);
|
||||
msgDiv.innerHTML = message;
|
||||
toastcontainer.append(msgDiv);
|
||||
setTimeout(function() {
|
||||
|
|
@ -103,7 +103,7 @@ function showPrompt(title, text, buttons, shouldEscapeHtml) {
|
|||
if (typeof(shouldEscapeHtml) === 'undefined' || shouldEscapeHtml === null) shouldEscapeHtml = true;
|
||||
|
||||
return new Promise((resolve,reject) => {
|
||||
var modal = htmlElement(`<div class="modal active">
|
||||
let modal = htmlElement(`<div class="modal active">
|
||||
<!--<a href="#close" class="modal-overlay" aria-label="Close"></a>-->
|
||||
<div class="modal-container">
|
||||
<div class="modal-header">
|
||||
|
|
@ -133,7 +133,7 @@ function showPrompt(title, text, buttons, shouldEscapeHtml) {
|
|||
htmlToArray(modal.getElementsByTagName("button")).forEach(button => {
|
||||
button.addEventListener("click",event => {
|
||||
event.preventDefault();
|
||||
var isYes = event.target.getAttribute("isyes")=="1";
|
||||
let isYes = event.target.getAttribute("isyes")=="1";
|
||||
if (isYes) resolve();
|
||||
else reject("User cancelled");
|
||||
modal.remove();
|
||||
|
|
|
|||
14
js/utils.js
14
js/utils.js
|
|
@ -1,5 +1,5 @@
|
|||
function escapeHtml(text) {
|
||||
var map = {
|
||||
let map = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
|
|
@ -24,13 +24,13 @@ function htmlToArray(collection) {
|
|||
return [].slice.call(collection);
|
||||
}
|
||||
function htmlElement(str) {
|
||||
var div = document.createElement('div');
|
||||
let div = document.createElement('div');
|
||||
div.innerHTML = str.trim();
|
||||
return div.firstChild;
|
||||
}
|
||||
function httpGet(url) {
|
||||
return new Promise((resolve,reject) => {
|
||||
var oReq = new XMLHttpRequest();
|
||||
let oReq = new XMLHttpRequest();
|
||||
oReq.addEventListener("load", () => {
|
||||
if (oReq.status==200) resolve(oReq.responseText)
|
||||
else reject(oReq.status+" - "+oReq.statusText);
|
||||
|
|
@ -51,8 +51,8 @@ function toJS(txt) {
|
|||
function appSorter(a,b) {
|
||||
if (a.unknown || b.unknown)
|
||||
return (a.unknown)? 1 : -1;
|
||||
var sa = 0|a.sortorder;
|
||||
var sb = 0|b.sortorder;
|
||||
let sa = 0|a.sortorder;
|
||||
let sb = 0|b.sortorder;
|
||||
if (sa<sb) return -1;
|
||||
if (sa>sb) return 1;
|
||||
return (a.name==b.name) ? 0 : ((a.name<b.name) ? -1 : 1);
|
||||
|
|
@ -61,8 +61,8 @@ function appSorter(a,b) {
|
|||
/* Given 2 JSON structures (1st from apps.json, 2nd from an installed app)
|
||||
work out what to display re: versions and if we can update */
|
||||
function getVersionInfo(appListing, appInstalled) {
|
||||
var versionText = "";
|
||||
var canUpdate = false;
|
||||
let versionText = "";
|
||||
let canUpdate = false;
|
||||
function clicky(v) {
|
||||
return `<a class="c-hand" onclick="showChangeLog('${appListing.id}')">${v}</a>`;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue