diff --git a/apps/a_dndtoggle/settings.js b/apps/a_dndtoggle/settings.js index 5316525b3..483af8c97 100644 --- a/apps/a_dndtoggle/settings.js +++ b/apps/a_dndtoggle/settings.js @@ -29,5 +29,4 @@ } E.showMenu(buildMainMenu()); - }); - \ No newline at end of file + }) diff --git a/apps/activepedom/settings.js b/apps/activepedom/settings.js index 3b64d8735..16799f0db 100644 --- a/apps/activepedom/settings.js +++ b/apps/activepedom/settings.js @@ -109,4 +109,4 @@ }, }; E.showMenu(menu); -}); +}) diff --git a/apps/agpsdata/settings.js b/apps/agpsdata/settings.js index 64fa25330..95b06fe55 100644 --- a/apps/agpsdata/settings.js +++ b/apps/agpsdata/settings.js @@ -68,4 +68,4 @@ function buildMainMenu() { } E.showMenu(buildMainMenu()); -}); +}) diff --git a/apps/alarm/settings.js b/apps/alarm/settings.js index 765e5a5fa..2843fbdb1 100644 --- a/apps/alarm/settings.js +++ b/apps/alarm/settings.js @@ -48,4 +48,4 @@ }; E.showMenu(appMenu); -}); +}) diff --git a/apps/andark/settings.js b/apps/andark/settings.js index 708913705..7bbceb2c2 100644 --- a/apps/andark/settings.js +++ b/apps/andark/settings.js @@ -25,4 +25,4 @@ }; E.showMenu(appMenu); -}); +}) diff --git a/apps/android/ChangeLog b/apps/android/ChangeLog index 1e62173dc..f1107fc84 100644 --- a/apps/android/ChangeLog +++ b/apps/android/ChangeLog @@ -38,4 +38,5 @@ 0.36: Move from wrapper function to {} and let - faster execution at boot Allow `calendar-` to take an array of items to remove 0.37: Support Gadgetbridge canned responses -0.38: Don't rewrite settings file on every boot! \ No newline at end of file +0.38: Don't rewrite settings file on every boot! +0.39: Move GB message handling into a library to reduce boot time from 40ms->13ms \ No newline at end of file diff --git a/apps/android/boot.js b/apps/android/boot.js index 53cf51ff2..18297d84f 100644 --- a/apps/android/boot.js +++ b/apps/android/boot.js @@ -1,349 +1,24 @@ /* global GB */ { - let gbSend = function(message) { - Bluetooth.println(""); - Bluetooth.println(JSON.stringify(message)); - } - let lastMsg; // for music messages - may not be needed now... - let actInterval; // Realtime activity reporting interval when `act` is true - let actHRMHandler; // For Realtime activity reporting - let gpsState = {}; // keep information on GPS via Gadgetbridge - - // this settings var is deleted after this executes to save memory + // settings var is deleted after this executes to save memory let settings = Object.assign({rp:true,as:true,vibrate:".."}, require("Storage").readJSON("android.settings.json",1)||{} ); let _GB = global.GB; - let fetchRecInterval; - global.GB = (event) => { + global.GB = e => { // feed a copy to other handlers if there were any - if (_GB) setTimeout(_GB,0,Object.assign({},event)); - - /* TODO: Call handling, fitness */ - var HANDLERS = { - // {t:"notify",id:int, src,title,subject,body,sender,tel:string} add - "notify" : function() { - Object.assign(event,{t:"add",positive:true, negative:true}); - // Detect a weird GadgetBridge bug and fix it - // For some reason SMS messages send two GB notifications, with different sets of info - if (lastMsg && event.body == lastMsg.body && lastMsg.src == undefined && event.src == "Messages") { - // Mutate the other message - event.id = lastMsg.id; - } - lastMsg = event; - require("messages").pushMessage(event); - }, - // {t:"notify~",id:int, title:string} // modified - "notify~" : function() { event.t="modify";require("messages").pushMessage(event); }, - // {t:"notify-",id:int} // remove - "notify-" : function() { event.t="remove";require("messages").pushMessage(event); }, - // {t:"find", n:bool} // find my phone - "find" : function() { - if (Bangle.findDeviceInterval) { - clearInterval(Bangle.findDeviceInterval); - delete Bangle.findDeviceInterval; - } - if (event.n) // Ignore quiet mode: we always want to find our watch - Bangle.findDeviceInterval = setInterval(_=>Bangle.buzz(),1000); - }, - // {t:"musicstate", state:"play/pause",position,shuffle,repeat} - "musicstate" : function() { - require("messages").pushMessage({t:"modify",id:"music",title:"Music",state:event.state}); - }, - // {t:"musicinfo", artist,album,track,dur,c(track count),n(track num} - "musicinfo" : function() { - require("messages").pushMessage(Object.assign(event, {t:"modify",id:"music",title:"Music"})); - }, - // {"t":"call","cmd":"incoming/end","name":"Bob","number":"12421312"}) - "call" : function() { - Object.assign(event, { - t:event.cmd=="incoming"?"add":"remove", - id:"call", src:"Phone", - positive:true, negative:true, - title:event.name||/*LANG*/"Call", body:/*LANG*/"Incoming call\n"+event.number}); - require("messages").pushMessage(event); - }, - "canned_responses_sync" : function() { - require("Storage").writeJSON("replies.json", event.d); - }, - // {"t":"alarm", "d":[{h:int,m:int,rep:int},... } - "alarm" : function() { - //wipe existing GB alarms - var sched; - try { sched = require("sched"); } catch (e) {} - if (!sched) return; // alarms may not be installed - var gbalarms = sched.getAlarms().filter(a=>a.appid=="gbalarms"); - for (var i = 0; i < gbalarms.length; i++) - sched.setAlarm(gbalarms[i].id, undefined); - var alarms = sched.getAlarms(); - var time = new Date(); - var currentTime = time.getHours() * 3600000 + - time.getMinutes() * 60000 + - time.getSeconds() * 1000; - for (var j = 0; j < event.d.length; j++) { - // prevents all alarms from going off at once?? - var dow = event.d[j].rep; - var rp = false; - if (!dow) { - dow = 127; //if no DOW selected, set alarm to all DOW - } else { - rp = true; - } - var last = (event.d[j].h * 3600000 + event.d[j].m * 60000 < currentTime) ? (new Date()).getDate() : 0; - var a = require("sched").newDefaultAlarm(); - a.id = "gb"+j; - a.appid = "gbalarms"; - a.on = event.d[j].on !== undefined ? event.d[j].on : true; - a.t = event.d[j].h * 3600000 + event.d[j].m * 60000; - a.dow = ((dow&63)<<1) | (dow>>6); // Gadgetbridge sends DOW in a different format - a.rp = rp; - a.last = last; - alarms.push(a); - } - sched.setAlarms(alarms); - sched.reload(); - }, - //TODO perhaps move those in a library (like messages), used also for viewing events? - //add and remove events based on activity on phone (pebble-like) - // {t:"calendar", id:int, type:int, timestamp:seconds, durationInSeconds, title:string, description:string,location:string,calName:string.color:int,allDay:bool - "calendar" : function() { - var cal = require("Storage").readJSON("android.calendar.json",true); - if (!cal || !Array.isArray(cal)) cal = []; - var i = cal.findIndex(e=>e.id==event.id); - if(i<0) - cal.push(event); - else - cal[i] = event; - require("Storage").writeJSON("android.calendar.json", cal); - }, - // {t:"calendar-", id:int} - "calendar-" : function() { - var cal = require("Storage").readJSON("android.calendar.json",true); - //if any of those happen we are out of sync! - if (!cal || !Array.isArray(cal)) cal = []; - if (Array.isArray(event.id)) - cal = cal.filter(e=>!event.id.includes(e.id)); - else - cal = cal.filter(e=>e.id!=event.id); - require("Storage").writeJSON("android.calendar.json", cal); - }, - //triggered by GB, send all ids - // { t:"force_calendar_sync_start" } - "force_calendar_sync_start" : function() { - var cal = require("Storage").readJSON("android.calendar.json",true); - if (!cal || !Array.isArray(cal)) cal = []; - gbSend({t:"force_calendar_sync", ids: cal.map(e=>e.id)}); - }, - // {t:"http",resp:"......",[id:"..."]} - "http":function() { - //get the promise and call the promise resolve - if (Bangle.httpRequest === undefined) return; - var request=Bangle.httpRequest[event.id]; - if (request === undefined) return; //already timedout or wrong id - delete Bangle.httpRequest[event.id]; - clearTimeout(request.t); //t = timeout variable - if(event.err!==undefined) //if is error - request.j(event.err); //r = reJect function - else - request.r(event); //r = resolve function - }, - // {t:"gps", lat, lon, alt, speed, course, time, satellites, hdop, externalSource:true } - "gps": function() { - if (!settings.overwriteGps) return; - // modify event for using it as Bangle GPS event - delete event.t; - if (!isFinite(event.satellites)) event.satellites = NaN; - if (!isFinite(event.course)) event.course = NaN; - event.fix = 1; - if (event.long!==undefined) { // for earlier Gadgetbridge implementations - event.lon = event.long; - delete event.long; - } - if (event.time){ - event.time = new Date(event.time); - } - - if (!gpsState.lastGPSEvent) { - // this is the first event, save time of arrival and deactivate internal GPS - Bangle.moveGPSPower(0); - } else { - // this is the second event, store the intervall for expecting the next GPS event - gpsState.interval = Date.now() - gpsState.lastGPSEvent; - } - gpsState.lastGPSEvent = Date.now(); - // in any case, cleanup the GPS state in case no new events arrive - if (gpsState.timeoutGPS) clearTimeout(gpsState.timeoutGPS); - gpsState.timeoutGPS = setTimeout(()=>{ - // reset state - gpsState.lastGPSEvent = undefined; - gpsState.timeoutGPS = undefined; - gpsState.interval = undefined; - // did not get an expected GPS event but have GPS clients, switch back to internal GPS - if (Bangle.isGPSOn()) Bangle.moveGPSPower(1); - }, (gpsState.interval || 10000) + 1000); - Bangle.emit('GPS', event); - }, - // {t:"is_gps_active"} - "is_gps_active": function() { - gbSend({ t: "gps_power", status: Bangle.isGPSOn() }); - }, - // {t:"act", hrm:bool, stp:bool, int:int} - "act": function() { - if (actInterval) clearInterval(actInterval); - actInterval = undefined; - if (actHRMHandler) - actHRMHandler = undefined; - Bangle.setHRMPower(event.hrm,"androidact"); - if (!(event.hrm || event.stp)) return; - if (!isFinite(event.int)) event.int=1; - var lastSteps = Bangle.getStepCount(); - var lastBPM = 0; - actHRMHandler = function(e) { - lastBPM = e.bpm; - }; - Bangle.on('HRM',actHRMHandler); - actInterval = setInterval(function() { - var steps = Bangle.getStepCount(); - gbSend({ t: "act", stp: steps-lastSteps, hrm: lastBPM, rt:1 }); - lastSteps = steps; - }, event.int*1000); - }, - // {t:"actfetch", ts:long} - "actfetch": function() { - gbSend({t: "actfetch", state: "start"}); - var actCount = 0; - var actCb = function(r) { - // The health lib saves the samples at the start of the 10-minute block - // However, GB expects them at the end of the block, so let's offset them - // here to keep a consistent API in the health lib - var sampleTs = r.date.getTime() + 600000; - if (sampleTs >= event.ts) { - gbSend({ - t: "act", - ts: sampleTs, - stp: r.steps, - hrm: r.bpm, - mov: r.movement - }); - actCount++; - } - } - if (event.ts != 0) { - require("health").readAllRecordsSince(new Date(event.ts - 600000), actCb); - } else { - require("health").readFullDatabase(actCb); - } - gbSend({t: "actfetch", state: "end", count: actCount}); - }, - //{t:"listRecs", id:"20230616a"} - "listRecs": function() { - let recs = require("Storage").list(/^recorder\.log.*\.csv$/,{sf:true}).map(s => s.slice(12, 21)); - if (event.id.length > 2) { // Handle if there was no id supplied. Then we send a list all available recorder logs back. - let firstNonsyncedIdx = recs.findIndex((logId) => logId > event.id); - if (-1 == firstNonsyncedIdx) { - recs = [] - } else { - recs = recs.slice(firstNonsyncedIdx); - } - } - gbSend({t:"actTrksList", list: recs}); // TODO: split up in multiple transmissions? - }, - //{t:"fetchRec", id:"20230616a"} - "fetchRec": function() { - // TODO: Decide on what names keys should have. - if (fetchRecInterval) { - clearInterval(fetchRecInterval); - fetchRecInterval = undefined; - } - if (event.id=="stop") { - return - } else { - let log = require("Storage").open("recorder.log"+event.id+".csv","r"); - let lines = "init";// = log.readLine(); - let pkgcnt = 0; - gbSend({t:"actTrk", log:event.id, lines:"erase", cnt:pkgcnt}); // "erase" will prompt Gadgetbridge to erase the contents of a already fetched log so we can rewrite it without keeping lines from the previous (probably failed) fetch. - let sendlines = ()=>{ - lines = log.readLine(); - for (var i = 0; i < 3; i++) { - let line = log.readLine(); - if (line) lines += line; - } - pkgcnt++; - gbSend({t:"actTrk", log:event.id, lines:lines, cnt:pkgcnt}); - if (!lines && fetchRecInterval) { - clearInterval(fetchRecInterval); - fetchRecInterval = undefined; - } - } - fetchRecInterval = setInterval(sendlines, 50) - } - }, - "nav": function() { - event.id="nav"; - if (event.instr) { - event.t="add"; - event.src="maps"; // for the icon - event.title="Navigation"; - if (require("messages").getMessages().find(m=>m.id=="nav")) - event.t = "modify"; - } else { - event.t="remove"; - } - require("messages").pushMessage(event); - }, - "cards" : function() { - // we receive all, just override what we have - if (Array.isArray(event.d)) - require("Storage").writeJSON("android.cards.json", event.d); - }, - "accelsender": function () { - require("Storage").writeJSON("accelsender.json", {enabled: event.enable, interval: event.interval}); - load(); - } - }; - var h = HANDLERS[event.t]; - if (h) h(); else console.log("GB Unknown",event); + if (_GB) setTimeout(_GB,0,Object.assign({},e)); + Bangle.emit("GB",e); + require("android").gbHandler(e); }; // HTTP request handling - see the readme - // options = {id,timeout,xpath} - Bangle.http = (url,options)=>{ - options = options||{}; - if (!NRF.getSecurityStatus().connected) - return Promise.reject(/*LANG*/"Not connected to Bluetooth"); - if (Bangle.httpRequest === undefined) - Bangle.httpRequest={}; - if (options.id === undefined) { - // try and create a unique ID - do { - options.id = Math.random().toString().substr(2); - } while( Bangle.httpRequest[options.id]!==undefined); - } - //send the request - var req = {t: "http", url:url, id:options.id}; - if (options.xpath) req.xpath = options.xpath; - if (options.return) req.return = options.return; // for xpath - if (options.method) req.method = options.method; - if (options.body) req.body = options.body; - if (options.headers) req.headers = options.headers; - gbSend(req); - //create the promise - var promise = new Promise(function(resolve,reject) { - //save the resolve function in the dictionary and create a timeout (30 seconds default) - Bangle.httpRequest[options.id]={r:resolve,j:reject,t:setTimeout(()=>{ - //if after "timeoutMillisec" it still hasn't answered -> reject - delete Bangle.httpRequest[options.id]; - reject("Timeout"); - },options.timeout||30000)}; - }); - return promise; - }; - + Bangle.http = (url,options)=>require("android").httpHandler(url,options); // Battery monitor - let sendBattery = function() { gbSend({ t: "status", bat: E.getBattery(), chg: Bangle.isCharging()?1:0 }); } + let sendBattery = function() { require("android").gbSend({ t: "status", bat: E.getBattery(), chg: Bangle.isCharging()?1:0 }); } Bangle.on("charging", sendBattery); NRF.on("connect", () => setTimeout(function() { sendBattery(); - gbSend({t: "ver", fw: process.env.VERSION, hw: process.env.HWVERSION}); + require("android").gbSend({t: "ver", fw: process.env.VERSION, hw: process.env.HWVERSION}); GB({t:"force_calendar_sync_start"}); // send a list of our calendar entries to start off the sync process }, 2000)); NRF.on("disconnect", () => { @@ -357,81 +32,24 @@ setInterval(sendBattery, 10*60*1000); // Health tracking - if 'realtime' data is sent with 'rt:1', but let's still send our activity log every 10 mins Bangle.on('health', h=>{ - gbSend({ t: "act", stp: h.steps, hrm: h.bpm, mov: h.movement }); + require("android").gbSend({ t: "act", stp: h.steps, hrm: h.bpm, mov: h.movement }); }); // Music control Bangle.musicControl = cmd => { // play/pause/next/previous/volumeup/volumedown - gbSend({ t: "music", n:cmd }); + require("android").gbSend({ t: "music", n:cmd }); }; // Message response Bangle.messageResponse = (msg,response) => { - if (msg.id=="call") return gbSend({ t: "call", n:response?"ACCEPT":"REJECT" }); - if (isFinite(msg.id)) return gbSend({ t: "notify", n:response?"OPEN":"DISMISS", id: msg.id }); + if (msg.id=="call") return require("android").gbSend({ t: "call", n:response?"ACCEPT":"REJECT" }); + if (isFinite(msg.id)) return require("android").gbSend({ t: "notify", n:response?"OPEN":"DISMISS", id: msg.id }); // error/warn here? }; Bangle.messageIgnore = msg => { - if (isFinite(msg.id)) return gbSend({ t: "notify", n:"MUTE", id: msg.id }); + if (isFinite(msg.id)) return require("android").gbSend({ t: "notify", n:"MUTE", id: msg.id }); }; // GPS overwrite logic - if (settings.overwriteGps) { // if the overwrite option is set.. - const origSetGPSPower = Bangle.setGPSPower; - Bangle.moveGPSPower = (state) => { - if (Bangle.isGPSOn()){ - let orig = Bangle._PWR.GPS; - delete Bangle._PWR.GPS; - origSetGPSPower(state); - Bangle._PWR.GPS = orig; - } - }; - - // work around Serial1 for GPS not working when connected to something - let serialTimeout; - let wrap = function(f){ - return (s)=>{ - if (serialTimeout) clearTimeout(serialTimeout); - origSetGPSPower(1, "androidgpsserial"); - f(s); - serialTimeout = setTimeout(()=>{ - serialTimeout = undefined; - origSetGPSPower(0, "androidgpsserial"); - }, 10000); - }; - }; - Serial1.println = wrap(Serial1.println); - Serial1.write = wrap(Serial1.write); - - // replace set GPS power logic to suppress activation of gps (and instead request it from the phone) - Bangle.setGPSPower = ((isOn, appID) => { - let pwr; - if (!this.lastGPSEvent){ - // use internal GPS power function if no gps event has arrived from GadgetBridge - pwr = origSetGPSPower(isOn, appID); - } else { - // we are currently expecting the next GPS event from GadgetBridge, keep track of GPS state per app - if (!Bangle._PWR) Bangle._PWR={}; - if (!Bangle._PWR.GPS) Bangle._PWR.GPS=[]; - if (!appID) appID="?"; - if (isOn && !Bangle._PWR.GPS.includes(appID)) Bangle._PWR.GPS.push(appID); - if (!isOn && Bangle._PWR.GPS.includes(appID)) Bangle._PWR.GPS.splice(Bangle._PWR.GPS.indexOf(appID),1); - pwr = Bangle._PWR.GPS.length>0; - // stop internal GPS, no clients left - if (!pwr) origSetGPSPower(0); - } - // always update Gadgetbridge on current power state - gbSend({ t: "gps_power", status: pwr }); - return pwr; - }).bind(gpsState); - // allow checking for GPS via GadgetBridge - Bangle.isGPSOn = () => { - return !!(Bangle._PWR && Bangle._PWR.GPS && Bangle._PWR.GPS.length>0); - }; - // stop GPS on boot if not activated - setTimeout(()=>{ - if (!Bangle.isGPSOn()) gbSend({ t: "gps_power", status: false }); - },3000); - } - + if (settings.overwriteGps) require("android").overwriteGPS(); // remove settings object so it's not taking up RAM delete settings; } diff --git a/apps/android/lib.js b/apps/android/lib.js new file mode 100644 index 000000000..038d154b3 --- /dev/null +++ b/apps/android/lib.js @@ -0,0 +1,388 @@ +exports.gbSend = function(message) { + Bluetooth.println(""); + Bluetooth.println(JSON.stringify(message)); +} +let lastMsg, // for music messages - may not be needed now... + gpsState = {}, // keep information on GPS via Gadgetbridge + settings = Object.assign({rp:true,as:true,vibrate:".."}, + require("Storage").readJSON("android.settings.json",1)||{} + ); + +exports.gbHandler = (event) => { + var HANDLERS = { + // {t:"notify",id:int, src,title,subject,body,sender,tel:string} add + "notify" : function() { + print("notify",event); + Object.assign(event,{t:"add",positive:true, negative:true}); + // Detect a weird GadgetBridge bug and fix it + // For some reason SMS messages send two GB notifications, with different sets of info + if (lastMsg && event.body == lastMsg.body && lastMsg.src == undefined && event.src == "Messages") { + // Mutate the other message + event.id = lastMsg.id; + } + lastMsg = event; + require("messages").pushMessage(event); + }, + // {t:"notify~",id:int, title:string} // modified + "notify~" : function() { event.t="modify";require("messages").pushMessage(event); }, + // {t:"notify-",id:int} // remove + "notify-" : function() { event.t="remove";require("messages").pushMessage(event); }, + // {t:"find", n:bool} // find my phone + "find" : function() { + if (Bangle.findDeviceInterval) { + clearInterval(Bangle.findDeviceInterval); + delete Bangle.findDeviceInterval; + } + if (event.n) // Ignore quiet mode: we always want to find our watch + Bangle.findDeviceInterval = setInterval(_=>Bangle.buzz(),1000); + }, + // {t:"musicstate", state:"play/pause",position,shuffle,repeat} + "musicstate" : function() { + require("messages").pushMessage({t:"modify",id:"music",title:"Music",state:event.state}); + }, + // {t:"musicinfo", artist,album,track,dur,c(track count),n(track num} + "musicinfo" : function() { + require("messages").pushMessage(Object.assign(event, {t:"modify",id:"music",title:"Music"})); + }, + // {"t":"call","cmd":"incoming/end/start/outgoing","name":"Bob","number":"12421312"}) + "call" : function() { + Object.assign(event, { + t:event.cmd=="incoming"?"add":"remove", + id:"call", src:"Phone", + positive:true, negative:true, + title:event.name||/*LANG*/"Call", body:/*LANG*/"Incoming call\n"+event.number}); + require("messages").pushMessage(event); + }, + "canned_responses_sync" : function() { + require("Storage").writeJSON("replies.json", event.d); + }, + // {"t":"alarm", "d":[{h:int,m:int,rep:int},... } + "alarm" : function() { + //wipe existing GB alarms + var sched; + try { sched = require("sched"); } catch (e) {} + if (!sched) return; // alarms may not be installed + var gbalarms = sched.getAlarms().filter(a=>a.appid=="gbalarms"); + for (var i = 0; i < gbalarms.length; i++) + sched.setAlarm(gbalarms[i].id, undefined); + var alarms = sched.getAlarms(); + var time = new Date(); + var currentTime = time.getHours() * 3600000 + + time.getMinutes() * 60000 + + time.getSeconds() * 1000; + for (var j = 0; j < event.d.length; j++) { + // prevents all alarms from going off at once?? + var dow = event.d[j].rep; + var rp = false; + if (!dow) { + dow = 127; //if no DOW selected, set alarm to all DOW + } else { + rp = true; + } + var last = (event.d[j].h * 3600000 + event.d[j].m * 60000 < currentTime) ? (new Date()).getDate() : 0; + var a = require("sched").newDefaultAlarm(); + a.id = "gb"+j; + a.appid = "gbalarms"; + a.on = event.d[j].on !== undefined ? event.d[j].on : true; + a.t = event.d[j].h * 3600000 + event.d[j].m * 60000; + a.dow = ((dow&63)<<1) | (dow>>6); // Gadgetbridge sends DOW in a different format + a.rp = rp; + a.last = last; + alarms.push(a); + } + sched.setAlarms(alarms); + sched.reload(); + }, + //TODO perhaps move those in a library (like messages), used also for viewing events? + //add and remove events based on activity on phone (pebble-like) + // {t:"calendar", id:int, type:int, timestamp:seconds, durationInSeconds, title:string, description:string,location:string,calName:string.color:int,allDay:bool + "calendar" : function() { + var cal = require("Storage").readJSON("android.calendar.json",true); + if (!cal || !Array.isArray(cal)) cal = []; + var i = cal.findIndex(e=>e.id==event.id); + if(i<0) + cal.push(event); + else + cal[i] = event; + require("Storage").writeJSON("android.calendar.json", cal); + }, + // {t:"calendar-", id:int} + "calendar-" : function() { + var cal = require("Storage").readJSON("android.calendar.json",true); + //if any of those happen we are out of sync! + if (!cal || !Array.isArray(cal)) cal = []; + if (Array.isArray(event.id)) + cal = cal.filter(e=>!event.id.includes(e.id)); + else + cal = cal.filter(e=>e.id!=event.id); + require("Storage").writeJSON("android.calendar.json", cal); + }, + //triggered by GB, send all ids + // { t:"force_calendar_sync_start" } + "force_calendar_sync_start" : function() { + var cal = require("Storage").readJSON("android.calendar.json",true); + if (!cal || !Array.isArray(cal)) cal = []; + exports.gbSend({t:"force_calendar_sync", ids: cal.map(e=>e.id)}); + }, + // {t:"http",resp:"......",[id:"..."]} + "http":function() { + //get the promise and call the promise resolve + if (Bangle.httpRequest === undefined) return; + var request=Bangle.httpRequest[event.id]; + if (request === undefined) return; //already timedout or wrong id + delete Bangle.httpRequest[event.id]; + clearTimeout(request.t); //t = timeout variable + if(event.err!==undefined) //if is error + request.j(event.err); //r = reJect function + else + request.r(event); //r = resolve function + }, + // {t:"gps", lat, lon, alt, speed, course, time, satellites, hdop, externalSource:true } + "gps": function() { + if (!settings.overwriteGps) return; + // modify event for using it as Bangle GPS event + delete event.t; + if (!isFinite(event.satellites)) event.satellites = NaN; + if (!isFinite(event.course)) event.course = NaN; + event.fix = 1; + if (event.long!==undefined) { // for earlier Gadgetbridge implementations + event.lon = event.long; + delete event.long; + } + if (event.time){ + event.time = new Date(event.time); + } + + if (!gpsState.lastGPSEvent) { + // this is the first event, save time of arrival and deactivate internal GPS + Bangle.moveGPSPower(0); + } else { + // this is the second event, store the intervall for expecting the next GPS event + gpsState.interval = Date.now() - gpsState.lastGPSEvent; + } + gpsState.lastGPSEvent = Date.now(); + // in any case, cleanup the GPS state in case no new events arrive + if (gpsState.timeoutGPS) clearTimeout(gpsState.timeoutGPS); + gpsState.timeoutGPS = setTimeout(()=>{ + // reset state + gpsState.lastGPSEvent = undefined; + gpsState.timeoutGPS = undefined; + gpsState.interval = undefined; + // did not get an expected GPS event but have GPS clients, switch back to internal GPS + if (Bangle.isGPSOn()) Bangle.moveGPSPower(1); + }, (gpsState.interval || 10000) + 1000); + Bangle.emit('GPS', event); + }, + // {t:"is_gps_active"} + "is_gps_active": function() { + exports.gbSend({ t: "gps_power", status: Bangle.isGPSOn() }); + }, + // {t:"act", hrm:bool, stp:bool, int:int} + "act": function() { + if (exports.actInterval) clearInterval(exports.actInterval); + exports.actInterval = undefined; + if (exports.actHRMHandler) + exports.actHRMHandler = undefined; + Bangle.setHRMPower(event.hrm,"androidact"); + if (!(event.hrm || event.stp)) return; + if (!isFinite(event.int)) event.int=1; + var lastSteps = Bangle.getStepCount(); + var lastBPM = 0; + exports.actHRMHandler = function(e) { + lastBPM = e.bpm; + }; + Bangle.on('HRM',exports.actHRMHandler); + exports.actInterval = setInterval(function() { + var steps = Bangle.getStepCount(); + exports.gbSend({ t: "act", stp: steps-lastSteps, hrm: lastBPM, rt:1 }); + lastSteps = steps; + }, event.int*1000); + }, + // {t:"actfetch", ts:long} + "actfetch": function() { + exports.gbSend({t: "actfetch", state: "start"}); + var actCount = 0; + var actCb = function(r) { + // The health lib saves the samples at the start of the 10-minute block + // However, GB expects them at the end of the block, so let's offset them + // here to keep a consistent API in the health lib + var sampleTs = r.date.getTime() + 600000; + if (sampleTs >= event.ts) { + exports.gbSend({ + t: "act", + ts: sampleTs, + stp: r.steps, + hrm: r.bpm, + mov: r.movement + }); + actCount++; + } + } + if (event.ts != 0) { + require("health").readAllRecordsSince(new Date(event.ts - 600000), actCb); + } else { + require("health").readFullDatabase(actCb); + } + exports.gbSend({t: "actfetch", state: "end", count: actCount}); + }, + //{t:"listRecs", id:"20230616a"} + "listRecs": function() { + let recs = require("Storage").list(/^recorder\.log.*\.csv$/,{sf:true}).map(s => s.slice(12, 21)); + if (event.id.length > 2) { // Handle if there was no id supplied. Then we send a list all available recorder logs back. + let firstNonsyncedIdx = recs.findIndex((logId) => logId > event.id); + if (-1 == firstNonsyncedIdx) { + recs = [] + } else { + recs = recs.slice(firstNonsyncedIdx); + } + } + exports.gbSend({t:"actTrksList", list: recs}); // TODO: split up in multiple transmissions? + }, + //{t:"fetchRec", id:"20230616a"} + "fetchRec": function() { + // TODO: Decide on what names keys should have. + if (exports.fetchRecInterval) { + clearInterval(exports.fetchRecInterval); + exports.fetchRecInterval = undefined; + } + if (event.id=="stop") { + return; + } else { + let log = require("Storage").open("recorder.log"+event.id+".csv","r"); + let lines = "init";// = log.readLine(); + let pkgcnt = 0; + exports.gbSend({t:"actTrk", log:event.id, lines:"erase", cnt:pkgcnt}); // "erase" will prompt Gadgetbridge to erase the contents of a already fetched log so we can rewrite it without keeping lines from the previous (probably failed) fetch. + let sendlines = ()=>{ + lines = log.readLine(); + for (var i = 0; i < 3; i++) { + let line = log.readLine(); + if (line) lines += line; + } + pkgcnt++; + exports.gbSend({t:"actTrk", log:event.id, lines:lines, cnt:pkgcnt}); + if (!lines && exports.fetchRecInterval) { + clearInterval(exports.fetchRecInterval); + exports.fetchRecInterval = undefined; + } + }; + exports.fetchRecInterval = setInterval(sendlines, 50); + } + }, + "nav": function() { + event.id="nav"; + if (event.instr) { + event.t="add"; + event.src="maps"; // for the icon + event.title="Navigation"; + if (require("messages").getMessages().find(m=>m.id=="nav")) + event.t = "modify"; + } else { + event.t="remove"; + } + require("messages").pushMessage(event); + }, + "cards" : function() { + // we receive all, just override what we have + if (Array.isArray(event.d)) + require("Storage").writeJSON("android.cards.json", event.d); + }, + "accelsender": function () { + require("Storage").writeJSON("accelsender.json", {enabled: event.enable, interval: event.interval}); + load(); + } + }; + var h = HANDLERS[event.t]; + if (h) h(); else console.log("GB Unknown",event); +}; + +// HTTP request handling - see the readme +// options = {id,timeout,xpath} +exports.httpHandler = (url,options) => { + options = options||{}; + if (!NRF.getSecurityStatus().connected) + return Promise.reject(/*LANG*/"Not connected to Bluetooth"); + if (Bangle.httpRequest === undefined) + Bangle.httpRequest={}; + if (options.id === undefined) { + // try and create a unique ID + do { + options.id = Math.random().toString().substr(2); + } while( Bangle.httpRequest[options.id]!==undefined); + } + //send the request + var req = {t: "http", url:url, id:options.id}; + if (options.xpath) req.xpath = options.xpath; + if (options.return) req.return = options.return; // for xpath + if (options.method) req.method = options.method; + if (options.body) req.body = options.body; + if (options.headers) req.headers = options.headers; + exports.gbSend(req); + //create the promise + var promise = new Promise(function(resolve,reject) { + //save the resolve function in the dictionary and create a timeout (30 seconds default) + Bangle.httpRequest[options.id]={r:resolve,j:reject,t:setTimeout(()=>{ + //if after "timeoutMillisec" it still hasn't answered -> reject + delete Bangle.httpRequest[options.id]; + reject("Timeout"); + },options.timeout||30000)}; + }); + return promise; +}; + +exports.overwriteGPS = () => { // if the overwrite option is set, call this on init.. + const origSetGPSPower = Bangle.setGPSPower; + Bangle.moveGPSPower = (state) => { + if (Bangle.isGPSOn()){ + let orig = Bangle._PWR.GPS; + delete Bangle._PWR.GPS; + origSetGPSPower(state); + Bangle._PWR.GPS = orig; + } + }; + + // work around Serial1 for GPS not working when connected to something + let serialTimeout; + let wrap = function(f){ + return (s)=>{ + if (serialTimeout) clearTimeout(serialTimeout); + origSetGPSPower(1, "androidgpsserial"); + f(s); + serialTimeout = setTimeout(()=>{ + serialTimeout = undefined; + origSetGPSPower(0, "androidgpsserial"); + }, 10000); + }; + }; + Serial1.println = wrap(Serial1.println); + Serial1.write = wrap(Serial1.write); + + // replace set GPS power logic to suppress activation of gps (and instead request it from the phone) + Bangle.setGPSPower = ((isOn, appID) => { + let pwr; + if (!this.lastGPSEvent){ + // use internal GPS power function if no gps event has arrived from GadgetBridge + pwr = origSetGPSPower(isOn, appID); + } else { + // we are currently expecting the next GPS event from GadgetBridge, keep track of GPS state per app + if (!Bangle._PWR) Bangle._PWR={}; + if (!Bangle._PWR.GPS) Bangle._PWR.GPS=[]; + if (!appID) appID="?"; + if (isOn && !Bangle._PWR.GPS.includes(appID)) Bangle._PWR.GPS.push(appID); + if (!isOn && Bangle._PWR.GPS.includes(appID)) Bangle._PWR.GPS.splice(Bangle._PWR.GPS.indexOf(appID),1); + pwr = Bangle._PWR.GPS.length>0; + // stop internal GPS, no clients left + if (!pwr) origSetGPSPower(0); + } + // always update Gadgetbridge on current power state + require("android").gbSend({ t: "gps_power", status: pwr }); + return pwr; + }).bind(gpsState); + // allow checking for GPS via GadgetBridge + Bangle.isGPSOn = () => { + return !!(Bangle._PWR && Bangle._PWR.GPS && Bangle._PWR.GPS.length>0); + }; + // stop GPS on boot if not activated + setTimeout(()=>{ + if (!Bangle.isGPSOn()) require("android").gbSend({ t: "gps_power", status: false }); + },3000); +}; \ No newline at end of file diff --git a/apps/android/metadata.json b/apps/android/metadata.json index b1c3ecfaa..584c071cf 100644 --- a/apps/android/metadata.json +++ b/apps/android/metadata.json @@ -2,7 +2,7 @@ "id": "android", "name": "Android Integration", "shortName": "Android", - "version": "0.38", + "version": "0.39", "description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.", "icon": "app.png", "tags": "tool,system,messages,notifications,gadgetbridge", @@ -13,7 +13,8 @@ {"name":"android.app.js","url":"app.js"}, {"name":"android.settings.js","url":"settings.js"}, {"name":"android.img","url":"app-icon.js","evaluate":true}, - {"name":"android.boot.js","url":"boot.js"} + {"name":"android.boot.js","url":"boot.js"}, + {"name":"android","url":"lib.js"} ], "data": [{"name":"android.settings.json"}, {"name":"android.calendar.json"}, {"name":"android.cards.json"}], "sortorder": -8 diff --git a/apps/antonclkplus/settings.js b/apps/antonclkplus/settings.js index 4448c00ed..70851e983 100644 --- a/apps/antonclkplus/settings.js +++ b/apps/antonclkplus/settings.js @@ -94,4 +94,4 @@ E.showMenu(mainmenu); -}); +}) diff --git a/apps/assistedgps/ChangeLog b/apps/assistedgps/ChangeLog index 13f928f18..89b1c80f8 100644 --- a/apps/assistedgps/ChangeLog +++ b/apps/assistedgps/ChangeLog @@ -3,4 +3,5 @@ 0.03: Select GNSS systems to use for Bangle.js 2 0.04: Now turns GPS off after upload 0.05: Fix regression in 0.04 that caused AGPS data not to get loaded -0.06: Auto-set GPS output sentences - newer Bangle.js 2 don't include RMC (GPS direction + time) by default \ No newline at end of file +0.06: Auto-set GPS output sentences - newer Bangle.js 2 don't include RMC (GPS direction + time) by default +0.07: Bangle.js 2 now gets estimated time + lat/lon from the browser (~3x faster fix) \ No newline at end of file diff --git a/apps/assistedgps/custom.html b/apps/assistedgps/custom.html index 994f6d053..a51219346 100644 --- a/apps/assistedgps/custom.html +++ b/apps/assistedgps/custom.html @@ -60,6 +60,7 @@ diff --git a/apps/assistedgps/metadata.json b/apps/assistedgps/metadata.json index 8d4e07fa3..73f775a72 100644 --- a/apps/assistedgps/metadata.json +++ b/apps/assistedgps/metadata.json @@ -2,7 +2,7 @@ "id": "assistedgps", "name": "Assisted GPS Updater (AGPS)", "shortName": "AGPS", - "version": "0.06", + "version": "0.07", "description": "Downloads assisted GPS (AGPS) data to Bangle.js for faster GPS startup and more accurate fixes. **No app will be installed**, this just uploads new data to the GPS chip.", "sortorder": -1, "icon": "app.png", diff --git a/apps/banglebridge/ChangeLog b/apps/banglebridge/ChangeLog index 62542be60..a4b22db22 100644 --- a/apps/banglebridge/ChangeLog +++ b/apps/banglebridge/ChangeLog @@ -1,2 +1,3 @@ 0.01: New app! 0.02: Minor code improvements +0.03: Remove clearing of the screen (will break running apps) and fix lint errors \ No newline at end of file diff --git a/apps/banglebridge/metadata.json b/apps/banglebridge/metadata.json index 86e1face0..1dbb2e1de 100644 --- a/apps/banglebridge/metadata.json +++ b/apps/banglebridge/metadata.json @@ -2,8 +2,8 @@ "id": "banglebridge", "name": "BangleBridge", "shortName": "BangleBridge", - "version": "0.02", - "description": "Widget that allows Bangle Js to record pair and end data using Bluetooth Low Energy in combination with the BangleBridge Android App", + "version": "0.03", + "description": "Widget that allows Bangle.js to record pair and end data using Bluetooth Low Energy in combination with the BangleBridge Android App (**Note:** this has nothing to do with Gadgetbridge)", "icon": "widget.png", "type": "widget", "tags": "widget", diff --git a/apps/banglebridge/widget.js b/apps/banglebridge/widget.js index 692822b39..c805b0f39 100644 --- a/apps/banglebridge/widget.js +++ b/apps/banglebridge/widget.js @@ -1,10 +1,10 @@ (() => { /** * Widget measurements - * Description: + * Description: * name: connection.wid.js *icon: conectionIcon.icon - * + * */ //Font @@ -24,7 +24,7 @@ //Sensors code /** - * + * * @author Jorge */ function accel() { @@ -35,8 +35,7 @@ }); setInterval(function () { - - acclS = accelN.x + "##" + accelN.y + "##" + accelN.z + "\n" + accelN.diff + "##" + accelN.mag; + //acclS = accelN.x + "##" + accelN.y + "##" + accelN.z + "\n" + accelN.diff + "##" + accelN.mag; data[3] = accelN; }, 2 * 1000); @@ -45,8 +44,7 @@ function btt() { setInterval(function () { - - bttS = E.getBattery(); //return String + //bttS = E.getBattery(); //return String data[2] = E.getBattery(); }, 15 * 1000); @@ -65,9 +63,9 @@ setInterval(function () { - compssS = "A: " + compssN.x + " ## " + compssN.y + " ## " + compssN.z + "\n" + + /*compssS = "A: " + compssN.x + " ## " + compssN.y + " ## " + compssN.z + "\n" + "B: " + compssN.dx + " ## " + compssN.dy + " ## " + compssN.dz + " ## " + "\n" + - "C: " + compssN.heading; //return String + "C: " + compssN.heading; *///return String data[4] = compssN; }, 2 * 1000); @@ -86,8 +84,8 @@ setInterval(function () { - gpsS = "A: " + gpsN.lat + " ## " + gpsN.lon + " ## " + gpsN.alt + "\n" + "B: " + gpsN.speed + " ## " + gpsN.course + " ## " + gpsN.time + "\n" + - "C: " + gpsN.satellites + " ## " + gpsN.fix; //return String + /*gpsS = "A: " + gpsN.lat + " ## " + gpsN.lon + " ## " + gpsN.alt + "\n" + "B: " + gpsN.speed + " ## " + gpsN.course + " ## " + gpsN.time + "\n" + + "C: " + gpsN.satellites + " ## " + gpsN.fix; *///return String // work out how to display the current time var d = new Date(); var year = d.getFullYear(); @@ -150,7 +148,7 @@ //console.log("Index ==> "+ index); msr[indexFinal] = nueva; - item = nueva; + //item = nueva; lastInsert = indexFinal; } @@ -180,7 +178,7 @@ hrmN = normalize(hrmN); var roundedRate = parseFloat(hrmN).toFixed(2); - hrmS = String.valueOf(roundedRate); //return String + //hrmS = String.valueOf(roundedRate); //return String //console.log("array----->" + msr); data[0] = roundedRate; @@ -205,7 +203,7 @@ setInterval(function () { - stepS = String.valueOf(stepN); //return String + //stepS = String.valueOf(stepN); //return String data[1] = stepN; }, 2 * 1000); @@ -240,12 +238,11 @@ g.setFont("Vector", 45); g.drawString(prueba,100,200);*/ if (flip == 1) { //when off - + flip = 0; //Bangle.buzz(1000); - g.clear(); } else { //when on - + flip = 1; g.setFont("Vector", 30); g.drawString(data[0], 65, 180); @@ -283,7 +280,7 @@ com: data[4], gps: data[5] }; - /* g.clear(); + /* g.drawString(compssS,100,200); */ @@ -293,7 +290,7 @@ //draw(); }, 5 * 1000); - + WIDGETS["banglebridge"]={ area: "tl", width: 10, diff --git a/apps/banglexercise/settings.js b/apps/banglexercise/settings.js index 0b52acd72..a52634faf 100644 --- a/apps/banglexercise/settings.js +++ b/apps/banglexercise/settings.js @@ -17,4 +17,4 @@ } } }); -}); +}) diff --git a/apps/barclock/settings.js b/apps/barclock/settings.js index 04f0a38ba..bc292ef6f 100644 --- a/apps/barclock/settings.js +++ b/apps/barclock/settings.js @@ -32,4 +32,4 @@ } require("ClockFace_menu").addItems(menu, save, items); E.showMenu(menu); -}); +}) diff --git a/apps/berlinc/settings.js b/apps/berlinc/settings.js index a1b655a62..b240a5f46 100644 --- a/apps/berlinc/settings.js +++ b/apps/berlinc/settings.js @@ -23,4 +23,4 @@ }; E.showMenu(mainmenu); -}); +}) diff --git a/apps/bikespeedo/settings.js b/apps/bikespeedo/settings.js index bb943c081..0326d7529 100644 --- a/apps/bikespeedo/settings.js +++ b/apps/bikespeedo/settings.js @@ -68,4 +68,4 @@ E.showMenu(appMenu); -}); +}) diff --git a/apps/blc/blc.settings.js b/apps/blc/blc.settings.js index 00e9c284b..9143fc16e 100644 --- a/apps/blc/blc.settings.js +++ b/apps/blc/blc.settings.js @@ -69,4 +69,4 @@ //}; E.showMenu(mainmenu); -}); +}) diff --git a/apps/blecsc/clkinfo.js b/apps/blecsc/clkinfo.js index 9a9515c3a..69cd022af 100644 --- a/apps/blecsc/clkinfo.js +++ b/apps/blecsc/clkinfo.js @@ -70,5 +70,3 @@ }; return ci; }) - - diff --git a/apps/boot/ChangeLog b/apps/boot/ChangeLog index 1d8e44b72..5b0fcc583 100644 --- a/apps/boot/ChangeLog +++ b/apps/boot/ChangeLog @@ -72,3 +72,7 @@ 0.61: Instead of breaking execution with an Exception when updating boot, just use if..else (fix 'Uncaught undefined') 0.62: Handle setting for configuring BLE privacy 0.63: Only set BLE `display:1` if we have a passkey +0.64: Automatically create .widcache and .clkinfocache to speed up loads + Bangle.loadWidgets overwritten with fast version on success + Refuse to work on firmware <2v16 and remove old polyfills +0.65: Only display interpreter errors if log is nonzero \ No newline at end of file diff --git a/apps/boot/bootupdate.js b/apps/boot/bootupdate.js index aa4a7e7b5..07d8d2031 100644 --- a/apps/boot/bootupdate.js +++ b/apps/boot/bootupdate.js @@ -12,14 +12,13 @@ if (DEBUG) { boot += "var _tm=Date.now()\n"; bootPost += "delete _tm;"; } -if (require('Storage').hash) { // new in 2v11 - helps ensure files haven't changed - let CRC = E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\.boot\.js/)+E.CRC32(process.env.GIT_COMMIT); - boot += `if(E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\\.boot\\.js/)+E.CRC32(process.env.GIT_COMMIT)!=${CRC})`; -} else { - let CRC = E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/))+E.CRC32(process.env.GIT_COMMIT); - boot += `if(E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\\.boot\\.js/))+E.CRC32(process.env.GIT_COMMIT)!=${CRC})`; +if (FWVERSION < 216) { + E.showMessage(/*LANG*/"Please update Bangle.js firmware\n\nCurrent = "+process.env.VERSION,{title:"ERROR"}); + throw new Error("Old firmware"); } -boot += `{eval(require('Storage').read('bootupdate.js'));print("Storage Updated!")}else{\n`; +let CRC = E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\.js$/)+E.CRC32(process.env.GIT_COMMIT); +boot += `if(E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\\.js$/)+E.CRC32(process.env.GIT_COMMIT)!=${CRC})`; +boot += `{eval(require('Storage').read('bootupdate.js'));}else{\n`; boot += `E.setFlags({pretokenise:1});\n`; boot += `var bleServices = {}, bleServiceOptions = { uart : true};\n`; bootPost += `NRF.setServices(bleServices,bleServiceOptions);delete bleServices,bleServiceOptions;\n`; // executed after other boot code @@ -44,7 +43,7 @@ LoopbackA.setConsole(true);\n`; boot += ` Bluetooth.line=""; Bluetooth.on('data',function(d) { - var l = (Bluetooth.line + d).split(/[\\n\\r]/); + let l = (Bluetooth.line + d).split(/[\\n\\r]/); Bluetooth.line = l.pop(); l.forEach(n=>Bluetooth.emit("line",n)); }); @@ -67,12 +66,12 @@ if (s.ble===false) boot += `if (!NRF.getSecurityStatus().connected) NRF.sleep(); if (s.timeout!==undefined) boot += `Bangle.setLCDTimeout(${s.timeout});\n`; if (!s.timeout) boot += `Bangle.setLCDPower(1);\n`; boot += `E.setTimeZone(${s.timezone});`; -// Draw out of memory errors onto the screen -boot += `E.on('errorFlag', function(errorFlags) { +// Draw out of memory errors onto the screen if logging enabled +if (s.log) boot += `E.on('errorFlag', function(errorFlags) { g.reset(1).setColor("#ff0000").setFont("6x8").setFontAlign(0,1).drawString(errorFlags,g.getWidth()/2,g.getHeight()-1).flip(); print("Interpreter error:", errorFlags); - E.getErrorFlags(); // clear flags so we get called next time -});\n`; + E.getErrorFlags(); +});\n`;// E.getErrorFlags() -> clear flags so we get called next time // stop users doing bad things! if (global.save) boot += `global.save = function() { throw new Error("You can't use save() on Bangle.js without overwriting the bootloader!"); }\n`; // Apply any settings-specific stuff @@ -86,30 +85,18 @@ if (s.bleprivacy || (s.passkey!==undefined && s.passkey.length==6)) { if (s.blename === false) boot+=`NRF.setAdvertising({},{showName:false});\n`; if (s.whitelist && !s.whitelist_disabled) boot+=`NRF.on('connect', function(addr) { if (!NRF.ignoreWhitelist) { let whitelist = (require('Storage').readJSON('setting.json',1)||{}).whitelist; if (NRF.resolveAddress !== undefined) { let resolvedAddr = NRF.resolveAddress(addr); if (resolvedAddr !== undefined) addr = resolvedAddr + " (resolved)"; } if (!whitelist.includes(addr)) NRF.disconnect(); }});\n`; if (s.rotate) boot+=`g.setRotation(${s.rotate&3},${s.rotate>>2});\n` // screen rotation +boot+=`Bangle.loadWidgets=function(){if(!global.WIDGETS)eval(require("Storage").read(".widcache"))};\n`; // ================================================== FIXING OLDER FIRMWARES -if (FWVERSION<215.068) // 2v15.68 and before had compass heading inverted. - boot += `Bangle.on('mag',e=>{if(!isNaN(e.heading))e.heading=360-e.heading;}); -Bangle.getCompass=(c=>(()=>{e=c();if(!isNaN(e.heading))e.heading=360-e.heading;return e;}))(Bangle.getCompass);`; - // deleting stops us getting confused by our own decl. builtins can't be deleted // this is a polyfill without fastloading capability delete Bangle.showClock; if (!Bangle.showClock) boot += `Bangle.showClock = ()=>{load(".bootcde")};\n`; delete Bangle.load; if (!Bangle.load) boot += `Bangle.load = load;\n`; -let date = new Date(); -delete date.toLocalISOString; // toLocalISOString was only introduced in 2v15 -if (!date.toLocalISOString) boot += `Date.prototype.toLocalISOString = function() { - var o = this.getTimezoneOffset(); - var d = new Date(this.getTime() - o*60000); - var sign = o>0?"-":"+"; - o = Math.abs(o); - return d.toISOString().slice(0,-1)+sign+Math.floor(o/60).toString().padStart(2,0)+(o%60).toString().padStart(2,0); -};\n`; // show timings if (DEBUG) boot += `print(".boot0",0|(Date.now()-_tm),"ms");_tm=Date.now();\n` -// ================================================== BOOT.JS +// ================================================== .BOOT0 // Append *.boot.js files. // Name files with a number - eg 'foo.5.boot.js' to enforce order (lowest first). Numbered files get placed before non-numbered // These could change bleServices/bleServiceOptions if needed @@ -128,17 +115,47 @@ let bootFiles = require('Storage').list(/\.boot\.js$/).sort((a,b)=>{ }); // precalculate file size bootPost += "}"; -let fileSize = boot.length + bootPost.length; -bootFiles.forEach(bootFile=>{ - // match the size of data we're adding below in bootFiles.forEach - if (DEBUG) fileSize += 2+bootFile.length+1; // `//${bootFile}\n` comment - fileSize += require('Storage').read(bootFile).length+2; // boot code plus ";\n" - if (DEBUG) fileSize += 48+E.toJS(bootFile).length; // `print(${E.toJS(bootFile)},0|(Date.now()-_tm),"ms");_tm=Date.now();\n` -}); -// write file in chunks (so as not to use up all RAM) -require('Storage').write('.boot0',boot,0,fileSize); -let fileOffset = boot.length; -bootFiles.forEach(bootFile=>{ +let fileOffset,fileSize; +/* code to output a file, plus preable and postable +when called with dst==undefined it just increments +fileOffset so we can see ho wbig the file has to be */ +let outputFile = (dst,src,pre,post) => {"ram"; + if (DEBUG) { + if (dst) require('Storage').write(dst,`//${src}\n`,fileOffset); + fileOffset+=2+src.length+1; + } + if (pre) { + if (dst) require('Storage').write(dst,pre,fileOffset); + fileOffset+=pre.length; + } + let f = require('Storage').read(src); + if (src.endsWith("clkinfo.js") && f[0]!="(") { + /* we shouldn't have to do this but it seems sometimes (sched 0.28) folks have + used libraries which get added into the clockinfo, and we can't use them directly + to we have to revert back to eval */ + f = `eval(require('Storage').read(${E.toJS(src)}))`; + } + if (dst) { + // we can't just write 'f' in one go because it can be too big + let len = f.length; + let offset = 0; + while (len) { + let chunk = Math.min(len, 2048); + require('Storage').write(dst,f.substr(offset, chunk),fileOffset); + fileOffset+=chunk; + offset+=chunk; + len-=chunk; + } + } else + fileOffset+=f.length; + if (dst) require('Storage').write(dst,post,fileOffset); + fileOffset+=post.length; + if (DEBUG) { + if (dst) require('Storage').write(dst,`print(${E.toJS(src)},0|(Date.now()-_tm),"ms");_tm=Date.now();\n`,fileOffset); + fileOffset += 48+E.toJS(src).length; + } +}; +let outputFileComplete = (dst,fn) => { // we add a semicolon so if the file is wrapped in (function(){ ... }() // with no semicolon we don't end up with (function(){ ... }()(function(){ ... }() // which would cause an error! @@ -146,31 +163,48 @@ bootFiles.forEach(bootFile=>{ // "//"+bootFile+"\n"+require('Storage').read(bootFile)+";\n"; // but we need to do this without ever loading everything into RAM as some // boot files seem to be getting pretty big now. - if (DEBUG) { - require('Storage').write('.boot0',`//${bootFile}\n`,fileOffset); - fileOffset+=2+bootFile.length+1; - } - let bf = require('Storage').read(bootFile); - // we can't just write 'bf' in one go because at least in 2v13 and earlier - // Espruino wants to read the whole file into RAM first, and on Bangle.js 1 - // it can be too big (especially BTHRM). - let bflen = bf.length; - let bfoffset = 0; - while (bflen) { - let bfchunk = Math.min(bflen, 2048); - require('Storage').write('.boot0',bf.substr(bfoffset, bfchunk),fileOffset); - fileOffset+=bfchunk; - bfoffset+=bfchunk; - bflen-=bfchunk; - } - require('Storage').write('.boot0',";\n",fileOffset); - fileOffset+=2; - if (DEBUG) { - require('Storage').write('.boot0',`print(${E.toJS(bootFile)},0|(Date.now()-_tm),"ms");_tm=Date.now();\n`,fileOffset); - fileOffset += 48+E.toJS(bootFile).length - } -}); + outputFile(dst,fn,"",";\n"); +}; +fileOffset = boot.length + bootPost.length; +bootFiles.forEach(fn=>outputFileComplete(undefined,fn)); // just get sizes +fileSize = fileOffset; +require('Storage').write('.boot0',boot,0,fileSize); +fileOffset = boot.length; +bootFiles.forEach(fn=>outputFileComplete('.boot0',fn)); require('Storage').write('.boot0',bootPost,fileOffset); +delete boot,bootPost,bootFiles; +// ================================================== .WIDCACHE for widgets +let widgetFiles = require("Storage").list(/\.wid\.js$/); +let widget = `// Made by bootupdate.js\nglobal.WIDGETS={};`, widgetPost = `var W=WIDGETS;WIDGETS={}; +Object.keys(W).sort((a,b)=>(0|W[b].sortorder)-(0|W[a].sortorder)).forEach(k=>WIDGETS[k]=W[k]);`; // sort +if (DEBUG) widget+="var _tm=Date.now();"; +outputFileComplete = (dst,fn) => { + outputFile(dst,fn,"try{",`}catch(e){print(${E.toJS(fn)},e,e.stack)}\n`); +}; +fileOffset = widget.length + widgetPost.length; +widgetFiles.forEach(fn=>outputFileComplete(undefined,fn)); // just get sizes +fileSize = fileOffset; +require('Storage').write('.widcache',widget,0,fileSize); +fileOffset = widget.length; +widgetFiles.forEach(fn=>outputFileComplete('.widcache',fn)); +require('Storage').write('.widcache',widgetPost,fileOffset); +delete widget,widgetPost,widgetFiles; +// ================================================== .clkinfocache for clockinfos +let ciFiles = require("Storage").list(/\.clkinfo\.js$/); +let ci = `// Made by bootupdate.js\n`; +if (DEBUG) ci+="var _tm=Date.now();"; +outputFileComplete = (dst,fn) => { + outputFile(dst,fn,"try{let fn=",`;let a=fn(),b=menu.find(x=>x.name===a.name);if(b)b.items=b.items.concat(a.items)else menu=menu.concat(a);}catch(e){print(${E.toJS(fn)},e,e.stack)}\n`); +}; +fileOffset = ci.length; +ciFiles.forEach(fn=>outputFileComplete(undefined,fn)); // just get sizes +fileSize = fileOffset; +require('Storage').write('.clkinfocache',ci,0,fileSize); +fileOffset = ci.length; +ciFiles.forEach(fn=>outputFileComplete('.clkinfocache',fn)); +delete ci,ciFiles; +// test with require("clock_info").load() +// ================================================== END E.showMessage(/*LANG*/"Reloading..."); } // .bootcde should be run automatically after if required, since diff --git a/apps/boot/metadata.json b/apps/boot/metadata.json index dcc55da58..afe576e71 100644 --- a/apps/boot/metadata.json +++ b/apps/boot/metadata.json @@ -1,7 +1,7 @@ { "id": "boot", "name": "Bootloader", - "version": "0.63", + "version": "0.65", "description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings", "icon": "bootloader.png", "type": "bootloader", @@ -11,6 +11,9 @@ {"name":".boot0","url":"boot0.js"}, {"name":".bootcde","url":"bootloader.js"}, {"name":"bootupdate.js","url":"bootupdate.js"} + ],"data": [ + {"name":".widcache"}, + {"name":".clkinfocache"} ], "sortorder": -10 } diff --git a/apps/boxclk/settings.js b/apps/boxclk/settings.js index e35db455d..c4b41101b 100644 --- a/apps/boxclk/settings.js +++ b/apps/boxclk/settings.js @@ -91,4 +91,4 @@ }); E.showMenu(menu); -}); +}) diff --git a/apps/bthrm/settings.js b/apps/bthrm/settings.js index 68e958db8..310816dda 100644 --- a/apps/bthrm/settings.js +++ b/apps/bthrm/settings.js @@ -371,4 +371,4 @@ }; E.showMenu(buildMainMenu()); -}); +}) diff --git a/apps/carcrazy/settings.js b/apps/carcrazy/settings.js index ee3bbd417..48301a865 100644 --- a/apps/carcrazy/settings.js +++ b/apps/carcrazy/settings.js @@ -17,4 +17,4 @@ } }; E.showMenu(menu); -}); +}) diff --git a/apps/chimer/settings.js b/apps/chimer/settings.js index 55160c9be..1cfb980f4 100644 --- a/apps/chimer/settings.js +++ b/apps/chimer/settings.js @@ -91,4 +91,4 @@ settings = readSettings(); showMainMenu(); -}); +}) diff --git a/apps/circlesclock/settings.js b/apps/circlesclock/settings.js index 714b48f2e..0a92f5a5a 100644 --- a/apps/circlesclock/settings.js +++ b/apps/circlesclock/settings.js @@ -92,4 +92,4 @@ } showMainMenu(); -}); +}) diff --git a/apps/clicompleteclk/settings.js b/apps/clicompleteclk/settings.js index 0213ead6e..f062b98b1 100644 --- a/apps/clicompleteclk/settings.js +++ b/apps/clicompleteclk/settings.js @@ -47,4 +47,4 @@ }, '< Back': back, }); -}); +}) diff --git a/apps/clkinfocal/settings.js b/apps/clkinfocal/settings.js index 6fe8f2817..508de5ddc 100644 --- a/apps/clkinfocal/settings.js +++ b/apps/clkinfocal/settings.js @@ -34,4 +34,4 @@ } }); -}); +}) diff --git a/apps/clkinfogps/clkinfo.js b/apps/clkinfogps/clkinfo.js index 7db9bbdae..740e05eda 100644 --- a/apps/clkinfogps/clkinfo.js +++ b/apps/clkinfogps/clkinfo.js @@ -124,4 +124,4 @@ }; return info; -}); +}) diff --git a/apps/clkinfom/clkinfo.js b/apps/clkinfom/clkinfo.js index a3dae43e1..f01649c4c 100644 --- a/apps/clkinfom/clkinfo.js +++ b/apps/clkinfom/clkinfo.js @@ -58,4 +58,4 @@ }; return info; -}); +}) diff --git a/apps/clkinfostopw/clkinfo.js b/apps/clkinfostopw/clkinfo.js index fbbe80a55..8b7a6a9ad 100644 --- a/apps/clkinfostopw/clkinfo.js +++ b/apps/clkinfostopw/clkinfo.js @@ -74,4 +74,4 @@ } ] }; -}); +}) diff --git a/apps/clkinfostopw/clkinfo.ts b/apps/clkinfostopw/clkinfo.ts index f0c2a6ccb..78794205e 100644 --- a/apps/clkinfostopw/clkinfo.ts +++ b/apps/clkinfostopw/clkinfo.ts @@ -80,4 +80,4 @@ } ] }; -}) satisfies ClockInfoFunc +}) satisfies ClockInfoFunc // FIXME: semi-colon added automatically when Typescript generates Javascript file? diff --git a/apps/clock_info/ChangeLog b/apps/clock_info/ChangeLog index 8276321ac..7d2043899 100644 --- a/apps/clock_info/ChangeLog +++ b/apps/clock_info/ChangeLog @@ -10,4 +10,6 @@ 0.09: Save clkinfo settings on kill and remove 0.10: Fix focus bug when changing focus between two clock infos 0.11: Prepend swipe listener if possible -0.12: Add drawFilledImage to allow drawing icons with a separately coloured middle \ No newline at end of file +0.12: Add drawFilledImage to allow drawing icons with a separately coloured middle +0.13: Cache loaded ClockInfos so if we have clockInfoWidget and a clock, we don't load them twice (saves ~300ms) +0.14: Check for .clkinfocache and use that if exists (from boot 0.64) \ No newline at end of file diff --git a/apps/clock_info/lib.js b/apps/clock_info/lib.js index a9ca7de31..0e20ab855 100644 --- a/apps/clock_info/lib.js +++ b/apps/clock_info/lib.js @@ -14,6 +14,8 @@ if (stepGoal == undefined) { exports.loadCount = 0; /// A list of all the instances returned by addInteractive exports.clockInfos = []; +/// A list of loaded clockInfos +exports.clockInfoMenus = undefined; /// Load the settings, with defaults exports.loadSettings = function() { @@ -29,6 +31,8 @@ exports.loadSettings = function() { /// Load a list of ClockInfos - this does not cache and reloads each time exports.load = function() { + if (exports.clockInfoMenus) + return exports.clockInfoMenus; var settings = exports.loadSettings(); delete settings.apps; // keep just the basic settings in memory // info used for drawing... @@ -131,10 +135,14 @@ exports.load = function() { hide : function() { clearInterval(this.interval); delete this.interval; }, }); } - + var clkInfoCache = require('Storage').read('.clkinfocache'); + if (clkInfoCache!==undefined) { + // note: code below is included in clkinfocache by bootupdate.js + // we use clkinfocache if it exists as it's faster + eval(clkInfoCache); + } else require("Storage").list(/clkinfo\.js$/).forEach(fn => { // In case there exists already a menu object b with the same name as the next // object a, we append the items. Otherwise we add the new object a to the list. - require("Storage").list(/clkinfo.js$/).forEach(fn => { try{ var a = eval(require("Storage").read(fn))(); var b = menu.find(x => x.name === a.name); @@ -146,6 +154,7 @@ exports.load = function() { }); // return it all! + exports.clockInfoMenus = menu; return menu; }; @@ -345,6 +354,9 @@ exports.addInteractive = function(menu, options) { menuHideItem(menu[options.menuA].items[options.menuB]); exports.loadCount--; delete exports.clockInfos[options.index]; + // If nothing loaded now, clear our list of loaded menus + if (exports.loadCount==0) + exports.clockInfoMenus = undefined; }; options.redraw = function() { drawItem(menu[options.menuA].items[options.menuB]); diff --git a/apps/clock_info/metadata.json b/apps/clock_info/metadata.json index 3d47c5062..1d9a73ce3 100644 --- a/apps/clock_info/metadata.json +++ b/apps/clock_info/metadata.json @@ -1,7 +1,7 @@ { "id": "clock_info", "name": "Clock Info Module", "shortName": "Clock Info", - "version":"0.12", + "version":"0.14", "description": "A library used by clocks to provide extra information on the clock face (Altitude, BPM, etc)", "icon": "app.png", "type": "module", diff --git a/apps/clockbg/ChangeLog b/apps/clockbg/ChangeLog index 3c681a638..a370befc0 100644 --- a/apps/clockbg/ChangeLog +++ b/apps/clockbg/ChangeLog @@ -4,4 +4,5 @@ 0.04: More options for different background colors 'Plasma' generative background Add a 'view' option in settings menu to view the current background -0.05: Random square+plasma speed improvements (~2x faster) \ No newline at end of file +0.05: Random square+plasma speed improvements (~2x faster) +0.06: 25% speed improvement if Math.randInt exists (2v25 fw) \ No newline at end of file diff --git a/apps/clockbg/lib.js b/apps/clockbg/lib.js index 59345340f..256f2f372 100644 --- a/apps/clockbg/lib.js +++ b/apps/clockbg/lib.js @@ -1,6 +1,7 @@ let settings; exports.reload = function() { + //let t = Date.now(); settings = Object.assign({ style : "randomcolor", colors : ["#F00","#0F0","#00F"] @@ -17,7 +18,8 @@ exports.reload = function() { let bpp = (settings.colors.length>4)?4:2; let bg = Graphics.createArrayBuffer(11,11,bpp,{msb:true}); let u32 = new Uint32Array(bg.buffer); // faster to do 1/4 of the ops of E.mapInPlace(bg.buffer, bg.buffer, ()=>Math.random()*256); - E.mapInPlace(u32, u32, function(r,n){"ram";return r()*n}.bind(null,Math.random,0x100000000)); // random pixels + if (Math.randInt) E.mapInPlace(u32, u32, Math.randInt); // random pixels + else E.mapInPlace(u32, u32, function(r,n){"ram";return r()*n}.bind(null,Math.random,0x100000000)); // random pixels bg.buffer[bg.buffer.length-1]=Math.random()*256; // 11x11 isn't a multiple of 4 bytes - we need to set the last one! bg.palette = new Uint16Array(1<g.toColor(c))); @@ -28,7 +30,8 @@ exports.reload = function() { settings.style = "image"; let bg = Graphics.createArrayBuffer(16,16,4,{msb:true}); let u32 = new Uint32Array(bg.buffer); // faster to do 1/4 of the ops of E.mapInPlace(bg.buffer, bg.buffer, ()=>Math.random()*256); - E.mapInPlace(u32, u32, function(r,n){"ram";return r()*n}.bind(null,Math.random,0x100000000)); // random pixels + if (Math.randInt) E.mapInPlace(u32, u32, Math.randInt); // random pixels + else E.mapInPlace(u32, u32, function(r,n){"ram";return r()*n}.bind(null,Math.random,0x100000000)); // random pixels bg.filter([ // a gaussian filter to smooth out 1, 4, 7, 4, 1, 4,16,26,16, 4, @@ -42,6 +45,7 @@ exports.reload = function() { settings.imgOpt = {scale:11}; delete settings.colors; } + //console.log("bg",Date.now()-t); }; exports.reload(); diff --git a/apps/clockbg/metadata.json b/apps/clockbg/metadata.json index 85b1f8a5a..ba6fb6712 100644 --- a/apps/clockbg/metadata.json +++ b/apps/clockbg/metadata.json @@ -1,7 +1,7 @@ { "id": "clockbg", "name": "Clock Backgrounds", "shortName":"Backgrounds", - "version": "0.05", + "version": "0.06", "description": "Library that allows clocks to include a custom background (generated on demand or uploaded).", "icon": "app.png", "screenshots": [{"url":"screenshot.png"},{"url":"screenshot2.png"},{"url":"screenshot3.png"}], diff --git a/apps/clockcal/settings.js b/apps/clockcal/settings.js index ea613f5c0..ddacb4a16 100644 --- a/apps/clockcal/settings.js +++ b/apps/clockcal/settings.js @@ -114,4 +114,4 @@ }; // Show the menu E.showMenu(menu); -}); +}) diff --git a/apps/cogclock/settings.js b/apps/cogclock/settings.js index fb1dd761c..deae484c9 100644 --- a/apps/cogclock/settings.js +++ b/apps/cogclock/settings.js @@ -7,4 +7,4 @@ "showDate", "hideWidgets" ]); E.showMenu(menu); -}); +}) diff --git a/apps/counter2/settings.js b/apps/counter2/settings.js index b38df1824..f97d49ad3 100644 --- a/apps/counter2/settings.js +++ b/apps/counter2/settings.js @@ -52,4 +52,4 @@ }; // Show the menu E.showMenu(menu); -}); +}) diff --git a/apps/cprassist/settings.js b/apps/cprassist/settings.js index 5776baa0b..5099d0b7d 100644 --- a/apps/cprassist/settings.js +++ b/apps/cprassist/settings.js @@ -61,4 +61,4 @@ } }; E.showMenu(menu); -}); +}) diff --git a/apps/daisy/settings.js b/apps/daisy/settings.js index c0a2ffeea..250633dea 100644 --- a/apps/daisy/settings.js +++ b/apps/daisy/settings.js @@ -56,4 +56,3 @@ } }); }) - diff --git a/apps/dane_tcr/settings.js b/apps/dane_tcr/settings.js index 46988ec26..c13a0825d 100644 --- a/apps/dane_tcr/settings.js +++ b/apps/dane_tcr/settings.js @@ -54,4 +54,4 @@ }, '< Back': back }); -}); \ No newline at end of file +}) diff --git a/apps/dragboard/settings.js b/apps/dragboard/settings.js index 59a13c443..2aac13b28 100644 --- a/apps/dragboard/settings.js +++ b/apps/dragboard/settings.js @@ -45,4 +45,4 @@ }; E.showMenu(appMenu); -}); \ No newline at end of file +}) diff --git a/apps/draguboard/settings.js b/apps/draguboard/settings.js index ff4ede637..58634b1b3 100644 --- a/apps/draguboard/settings.js +++ b/apps/draguboard/settings.js @@ -41,4 +41,4 @@ }; E.showMenu(appMenu); -}); \ No newline at end of file +}) diff --git a/apps/drained/ChangeLog b/apps/drained/ChangeLog index 3767ad71e..0667d8ff6 100644 --- a/apps/drained/ChangeLog +++ b/apps/drained/ChangeLog @@ -5,3 +5,4 @@ 0.04: Enhance menu: enable bluetooth, visit settings & visit recovery 0.05: Enhance menu: permit toggling bluetooth 0.06: Display clock in green when charging, with "charging" text +0.07: Correctly restore full power when the charged threshold is reached diff --git a/apps/drained/app.js b/apps/drained/app.js index deafe7d68..d4cb97db8 100644 --- a/apps/drained/app.js +++ b/apps/drained/app.js @@ -88,7 +88,7 @@ var reload = function () { }; reload(); Bangle.emit("drained", E.getBattery()); -var _a = require("Storage").readJSON("".concat(app, ".setting.json"), true) || {}, _b = _a.keepStartup, keepStartup = _b === void 0 ? true : _b, _c = _a.restore, restore = _c === void 0 ? 20 : _c, _d = _a.exceptions, exceptions = _d === void 0 ? ["widdst.0"] : _d; +var _a = require("Storage").readJSON("".concat(app, ".setting.json"), true) || {}, _b = _a.keepStartup, keepStartup = _b === void 0 ? true : _b, _c = _a.restore, restore = _c === void 0 ? 20 : _c, _d = _a.exceptions, exceptions = _d === void 0 ? ["widdst.0"] : _d, _e = _a.interval, interval = _e === void 0 ? 10 : _e; function drainedRestore() { if (!keepStartup) { try { @@ -110,8 +110,10 @@ var checkCharge = function () { if (Bangle.isCharging()) checkCharge(); Bangle.on("charging", function (charging) { + if (drainedInterval) + drainedInterval = clearInterval(drainedInterval); if (charging) - checkCharge(); + drainedInterval = setInterval(checkCharge, interval * 60 * 1000); }); if (!keepStartup) { var storage = require("Storage"); diff --git a/apps/drained/app.ts b/apps/drained/app.ts index a779a8660..fd39b11bd 100644 --- a/apps/drained/app.ts +++ b/apps/drained/app.ts @@ -115,7 +115,7 @@ reload(); Bangle.emit("drained", E.getBattery()); // restore normal boot on charge -const { keepStartup = true, restore = 20, exceptions = ["widdst.0"] }: DrainedSettings +const { keepStartup = true, restore = 20, exceptions = ["widdst.0"], interval = 10 }: DrainedSettings = require("Storage").readJSON(`${app}.setting.json`, true) || {}; // re-enable normal boot code when we're above a threshold: @@ -142,7 +142,10 @@ if (Bangle.isCharging()) checkCharge(); Bangle.on("charging", charging => { - if(charging) checkCharge(); + if(drainedInterval) + drainedInterval = clearInterval(drainedInterval) as undefined; + if(charging) + drainedInterval = setInterval(checkCharge, interval * 60 * 1000); }); if(!keepStartup){ diff --git a/apps/drained/metadata.json b/apps/drained/metadata.json index a5389a91b..eff9a331b 100644 --- a/apps/drained/metadata.json +++ b/apps/drained/metadata.json @@ -1,7 +1,7 @@ { "id": "drained", "name": "Drained", - "version": "0.06", + "version": "0.07", "description": "Switches to displaying a simple clock when the battery percentage is low, and disables some peripherals", "readme": "README.md", "icon": "icon.png", diff --git a/apps/drained/settings.js b/apps/drained/settings.js index ce72f215f..d82a9f6d4 100644 --- a/apps/drained/settings.js +++ b/apps/drained/settings.js @@ -87,4 +87,4 @@ }); }; updateAndRedraw(); -}); +}) diff --git a/apps/drinkcounter/settings.js b/apps/drinkcounter/settings.js index 336229b73..8353103e3 100644 --- a/apps/drinkcounter/settings.js +++ b/apps/drinkcounter/settings.js @@ -55,4 +55,4 @@ E.showMenu(mainmenu); -}); +}) diff --git a/apps/dtlaunch/ChangeLog b/apps/dtlaunch/ChangeLog index 5cac5770e..585e89ef9 100644 --- a/apps/dtlaunch/ChangeLog +++ b/apps/dtlaunch/ChangeLog @@ -30,3 +30,4 @@ when moving pages. Add caching for faster startups. 0.23: Bangle 1: Fix issue with missing icons, added touch screen interactions 0.24: Add buzz-on-interaction setting 0.25: Minor code improvements +0.26: Bangle 2: Postpone loading icons that are not needed initially. diff --git a/apps/dtlaunch/app-b2.js b/apps/dtlaunch/app-b2.js index 2108910fc..9da914980 100644 --- a/apps/dtlaunch/app-b2.js +++ b/apps/dtlaunch/app-b2.js @@ -33,10 +33,10 @@ s.writeJSON("launch.cache.json", launchCache); } let apps = launchCache.apps; - apps.forEach(app=>{ - if (app.icon) - app.icon = s.read(app.icon); // should just be a link to a memory area - }); + for (let i = 0; i < 4; i++) { // Initially only load icons for the current page. + if (apps[i].icon) + apps[i].icon = s.read(apps[i].icon); // should just be a link to a memory area + } let Napps = apps.length; let Npages = Math.ceil(Napps/4); @@ -101,6 +101,11 @@ Bangle.loadWidgets(); drawPage(0); + for (let i = 4; i < apps.length; i++) { // Load the rest of the app icons that were not initially. + if (apps[i].icon) + apps[i].icon = s.read(apps[i].icon); // should just be a link to a memory area + } + let swipeListenerDt = function(dirLeftRight, dirUpDown){ updateTimeoutToClock(); selected = -1; diff --git a/apps/dtlaunch/metadata.json b/apps/dtlaunch/metadata.json index bac0ed369..0f6430829 100644 --- a/apps/dtlaunch/metadata.json +++ b/apps/dtlaunch/metadata.json @@ -1,7 +1,7 @@ { "id": "dtlaunch", "name": "Desktop Launcher", - "version": "0.25", + "version": "0.26", "description": "Desktop style App Launcher with six (four for Bangle 2) apps per page - fast access if you have lots of apps installed.", "screenshots": [{"url":"shot1.png"},{"url":"shot2.png"},{"url":"shot3.png"}], "icon": "icon.png", diff --git a/apps/dtlaunch/settings-b2.js b/apps/dtlaunch/settings-b2.js index 6a50f90d4..f6894e289 100644 --- a/apps/dtlaunch/settings-b2.js +++ b/apps/dtlaunch/settings-b2.js @@ -65,4 +65,4 @@ } }, }); -}); +}) diff --git a/apps/ffcniftyb/settings.js b/apps/ffcniftyb/settings.js index da350edd8..8c3bb6e4d 100644 --- a/apps/ffcniftyb/settings.js +++ b/apps/ffcniftyb/settings.js @@ -28,4 +28,4 @@ }); E.showMenu(menu); -}); +}) diff --git a/apps/folderlaunch/settings.js b/apps/folderlaunch/settings.js index b589bb3f7..98720a1db 100644 --- a/apps/folderlaunch/settings.js +++ b/apps/folderlaunch/settings.js @@ -248,4 +248,4 @@ }); }; showMainMenu(); -}); +}) diff --git a/apps/gassist/settings.js b/apps/gassist/settings.js index 20634ed5e..0b54c350f 100644 --- a/apps/gassist/settings.js +++ b/apps/gassist/settings.js @@ -29,4 +29,4 @@ } // Initially show the menu showMenu(); -}); +}) diff --git a/apps/gbmusic/settings.js b/apps/gbmusic/settings.js index 9b8d35be9..70d96c1e9 100644 --- a/apps/gbmusic/settings.js +++ b/apps/gbmusic/settings.js @@ -38,4 +38,4 @@ }; E.showMenu(menu); -}); +}) diff --git a/apps/getup/settings.js b/apps/getup/settings.js index f34262f2a..db84fd84d 100644 --- a/apps/getup/settings.js +++ b/apps/getup/settings.js @@ -45,4 +45,4 @@ }, }; E.showMenu(menu); -}); +}) diff --git a/apps/gipy/settings.js b/apps/gipy/settings.js index e3b00359c..9562bc3c1 100644 --- a/apps/gipy/settings.js +++ b/apps/gipy/settings.js @@ -99,4 +99,4 @@ } } }); -}); +}) diff --git a/apps/gpsrec/settings.js b/apps/gpsrec/settings.js index 23a58d58f..287073b0c 100644 --- a/apps/gpsrec/settings.js +++ b/apps/gpsrec/settings.js @@ -1,4 +1,4 @@ (function(back) { - // just go right to our app - we need all the memory + // just go right to our app - we need all the memory */ load("gpsrec.app.js"); -})(); +})() diff --git a/apps/gpssetup/settings.js b/apps/gpssetup/settings.js index 0e3c621d1..59988b788 100644 --- a/apps/gpssetup/settings.js +++ b/apps/gpssetup/settings.js @@ -1,4 +1,4 @@ (function(back) { - // just go right to our app + /* just go right to our app*/ load("gpssetup.app.js"); -})(); +})() diff --git a/apps/hworldclock/ChangeLog b/apps/hworldclock/ChangeLog index 42e785b0a..ad327831a 100644 --- a/apps/hworldclock/ChangeLog +++ b/apps/hworldclock/ChangeLog @@ -21,3 +21,4 @@ 0.34: Fix 'fast load' so clock doesn't always redraw when screen unlocked/locked 0.35: Minor code improvements 0.36: Minor code improvements +0.37: Fix settings (show default as '90' not 'off') \ No newline at end of file diff --git a/apps/hworldclock/metadata.json b/apps/hworldclock/metadata.json index 422ef6b89..aeae2d254 100644 --- a/apps/hworldclock/metadata.json +++ b/apps/hworldclock/metadata.json @@ -2,7 +2,7 @@ "id": "hworldclock", "name": "Hanks World Clock", "shortName": "Hanks World Clock", - "version": "0.36", + "version": "0.37", "description": "Current time zone plus up to three others", "allow_emulator":true, "icon": "app.png", diff --git a/apps/hworldclock/settings.js b/apps/hworldclock/settings.js index 98b91dc7b..457bc47b3 100644 --- a/apps/hworldclock/settings.js +++ b/apps/hworldclock/settings.js @@ -2,6 +2,7 @@ var FILE = "hworldclock.json"; var settings = Object.assign({ secondsOnUnlock: false, + rotationTarget: "90", }, require('Storage').readJSON(FILE, true) || {}); function writeSettings() { @@ -55,4 +56,4 @@ E.showMenu(mainmenu); -}); +}) \ No newline at end of file diff --git a/apps/iconlaunch/settings.js b/apps/iconlaunch/settings.js index 3278075e4..ef2c188d7 100644 --- a/apps/iconlaunch/settings.js +++ b/apps/iconlaunch/settings.js @@ -50,4 +50,4 @@ }, }; E.showMenu(appMenu); -}); +}) diff --git a/apps/infoclk/ChangeLog b/apps/infoclk/ChangeLog index def512856..911e64894 100644 --- a/apps/infoclk/ChangeLog +++ b/apps/infoclk/ChangeLog @@ -12,3 +12,4 @@ Broke out config loading into separate file to avoid duplicating a whole bunch of code Added support for fast loading 0.10: Minor code improvements +0.11: Make sure variables are properly defined in settings.js diff --git a/apps/infoclk/metadata.json b/apps/infoclk/metadata.json index 1b1b6e604..ddd16c4a6 100644 --- a/apps/infoclk/metadata.json +++ b/apps/infoclk/metadata.json @@ -1,7 +1,7 @@ { "id": "infoclk", "name": "Informational clock", - "version": "0.10", + "version": "0.11", "description": "A configurable clock with extra info and shortcuts when unlocked, but large time when locked", "readme": "README.md", "icon": "icon.png", @@ -41,4 +41,4 @@ "dependencies": { "weather": "app" } -} \ No newline at end of file +} diff --git a/apps/infoclk/settings.js b/apps/infoclk/settings.js index a9adf87fc..dd18626e1 100644 --- a/apps/infoclk/settings.js +++ b/apps/infoclk/settings.js @@ -17,6 +17,9 @@ } else return '' + hour; } + let minute; // Is used in onchange functions. Defined here to appease the linter. + let hour; // Is used in onchange functions. Defined here to appease the linter. + // The menu for configuring when the seconds are shown function showSecondsMenu() { E.showMenu({ @@ -744,4 +747,4 @@ } showMainMenu(); -}); \ No newline at end of file +}) diff --git a/apps/kbedgewrite/ChangeLog b/apps/kbedgewrite/ChangeLog index 5560f00bc..93f9336cf 100644 --- a/apps/kbedgewrite/ChangeLog +++ b/apps/kbedgewrite/ChangeLog @@ -1 +1,3 @@ 0.01: New App! +0.02: Accents and extended mode characters +0.03: Bugfix - draw initial text after the back button has been removed diff --git a/apps/kbedgewrite/README.md b/apps/kbedgewrite/README.md index 8d0ca5dc5..2f668d4cf 100644 --- a/apps/kbedgewrite/README.md +++ b/apps/kbedgewrite/README.md @@ -12,7 +12,14 @@ To display the in app character chart, long press the screen; you can scroll thr For a full character chart see [EwChart.pdf](EwChart.pdf) -**Supported:** Letters (including capitals), numbers, backspace, word backspace, space, punctuation, new line, and some cursor controls (left, right, word left/right, home, end). +**Supported:** Letters (including capitals), numbers, backspace, word backspace, space, punctuation, new line, accents, extended mode (if characters are supported by the vector font), and some cursor controls (left, right, word left/right, home, end). -**Unsupported:** Extended mode, accents, and word-level stroking. +**Unsupported:** Word-level stroking. +## Settings + +Font size can be selected in Settings app > "Apps" > "EdgeWrite Keyboard" + +## Author + +Woogal [github](https://github.com/retcurve) diff --git a/apps/kbedgewrite/characterset.json b/apps/kbedgewrite/characterset.json index 74276b683..9aa70ae71 100644 --- a/apps/kbedgewrite/characterset.json +++ b/apps/kbedgewrite/characterset.json @@ -193,17 +193,93 @@ "2425": "`", "31": " \n", "24": " ", + "46": "\xb7", + "432146": "\xb0", + "412346": "\xb0", + "123246": "\xae", + "2123246": "\xae", + "123146": "\xae", + "2123146": "\xae", + "2346": "\xac", + "32146": "\xa9", + "41236": "\xa2", + "24316": "\xd7", + "31246": "\xd7", + "316": "\xf7", + "136": "\xf7", + "232146": "\x80", + "23246": "\x80", + "132146": "\x80", + "412316": "\x80", + "323146": "\x80", + "324146": "\x80", + "24346": "\xa5", + "243416": "\xa5", + "2143416": "\xa5", + "34146": "\xf0", + "342146": "\xf0", + "343146": "\xf0", + "4214346": "\xf0", + "414346": "\xf0", + "434146": "\xf0", + "123416": "\xf0", + "2123416": "\xf0", + "1346": "\xe6", + "1246": "\xe6", + "13416": "\xe6", + "12416": "\xe6", + "3214346": "\xe6", + "21416": "\xdf", + "213416": "\xdf", + "212416": "\xdf", + "141216": "\xdf", + "1341216": "\xdf", + "121416": "\xdf", + "1232416": "\xdf", + "1231416": "\xdf", + "21232416": "\xdf", + "21231416": "\xdf", + "2321416": "\xdf", + "2146": "\xa3", + "21436": "\xb5", + "214346": "\xb5", + "121436": "\xb5", + "1214346": "\xb5", + "3214316": "\xf8", + "3412316": "\xf8", + "4126": "\xbf", + "216": "\xa1", + "346": "\xa6", + "21236": "\xb1", + "212326": "\xb1", + "31426": "\xa4", + "24136": "\xa4", + "3146": "\xab", + "2416": "\xbb", "32": "#bs", "41": "#wbs", "12": "#pu-on", "43": "#pu-on", "325": "#pu-off", "415": "#pu-off", + "42": "#ex-on", + "326": "#ex-off", + "416": "#ex-off", "323": "#cur-left", "232": "#cur-right", "414": "#cur-word-left", "141": "#cur-word-right", "4141": "#cur-home", - "1414": "#cur-end" + "1414": "#cur-end", + "242": "#grave", + "313": "#acute", + "431": "#circumflex", + "421": "#circumflex", + "3421": "#tilde", + "43412": "#umlaut", + "43214": "#ring", + "41234": "#ring", + "142": "#cedilla", + "143": "#cedilla" } diff --git a/apps/kbedgewrite/lib.js b/apps/kbedgewrite/lib.js index a69eabc2c..62e699fa7 100644 --- a/apps/kbedgewrite/lib.js +++ b/apps/kbedgewrite/lib.js @@ -7,10 +7,20 @@ exports.input = function(options) { let chartX = 0; let chartY = 0; + let settings = Object.assign({ + fontSize: 32, + }, require('Storage').readJSON("kbedgewrite.json", true)); + let shouldShowWidgetBar = Bangle.appRect.y > 0; options = options||{}; let text = options.text; + // Substring doesn't play well with UTF8 + if (E.isUTF8(text)) { + text = E.decodeUTF8(text); + } + let wrappedText = ''; + if ('string' != typeof text) text=''; // Colours for number of corner occurrences @@ -18,40 +28,140 @@ exports.input = function(options) { const cornerSize = g.getWidth() / 3; let punctuationMode = false; + let extendedMode = false; let path = ''; let cursorPos = text.length; let chartShown = false; let characterSet = Object.assign({}, require('Storage').readJSON('kbedgewrite.charset.json', true) || {}); - function draw() { - g.clearRect(Bangle.appRect).setClipRect(Bangle.appRect.x, Bangle.appRect.y, Bangle.appRect.x2, Bangle.appRect.y2); + const accentedCharacters = { + '#grave': { + 'a': String.fromCharCode(0xE0), + 'A': String.fromCharCode(0xC0), + 'e': String.fromCharCode(0xE8), + 'E': String.fromCharCode(0xC8), + 'i': String.fromCharCode(0xEC), + 'I': String.fromCharCode(0xCC), + 'o': String.fromCharCode(0xF2), + 'O': String.fromCharCode(0xD2), + 'u': String.fromCharCode(0xF9), + 'U': String.fromCharCode(0xD9) + }, + '#acute': { + 'a': String.fromCharCode(0xE1), + 'A': String.fromCharCode(0xC1), + 'e': String.fromCharCode(0xE9), + 'E': String.fromCharCode(0xC9), + 'i': String.fromCharCode(0xED), + 'I': String.fromCharCode(0xCD), + 'o': String.fromCharCode(0xF3), + 'O': String.fromCharCode(0xD3), + 'u': String.fromCharCode(0xFA), + 'U': String.fromCharCode(0xDA), + 'y': String.fromCharCode(0xFD), + 'Y': String.fromCharCode(0xDD) + }, + '#circumflex': { + 'a': String.fromCharCode(0xE2), + 'A': String.fromCharCode(0xC2), + 'e': String.fromCharCode(0xEA), + 'E': String.fromCharCode(0xCA), + 'i': String.fromCharCode(0xEE), + 'I': String.fromCharCode(0xCE), + 'o': String.fromCharCode(0xF4), + 'O': String.fromCharCode(0xD4), + 'u': String.fromCharCode(0xFB), + 'U': String.fromCharCode(0xDB) + }, + '#umlaut': { + 'a': String.fromCharCode(0xE4), + 'A': String.fromCharCode(0xC4), + 'e': String.fromCharCode(0xEB), + 'E': String.fromCharCode(0xCB), + 'i': String.fromCharCode(0xEF), + 'I': String.fromCharCode(0xCF), + 'o': String.fromCharCode(0xF6), + 'O': String.fromCharCode(0xD6), + 'u': String.fromCharCode(0xFC), + 'U': String.fromCharCode(0xDC), + 'y': String.fromCharCode(0xFF) + }, + '#tilde': { + 'a': String.fromCharCode(0xE3), + 'A': String.fromCharCode(0xC3), + 'n': String.fromCharCode(0xF1), + 'N': String.fromCharCode(0xD1), + 'o': String.fromCharCode(0xF5), + 'O': String.fromCharCode(0xD5) + }, + '#ring': { + 'a': String.fromCharCode(0xE5), + 'A': String.fromCharCode(0xC5) + }, + '#cedilla': { + 'c': String.fromCharCode(0xE7), + 'C': String.fromCharCode(0xC7) + }, - // Draw the text string - let l = g.setFont('6x8:4').wrapString(text.substring(0, cursorPos) + '_' + text.substring(cursorPos), g.getWidth()); - if (!l) l = []; - if (l.length>5) { + }; + + function wrapText() { + let stringToWrap = text.substring(0, cursorPos) + '_' + text.substring(cursorPos); + let l = []; + let startPos = 0; + + g.setFont("Vector", settings.fontSize); // set the font so we can calculate a string width + + // Wrap the string into array of lines that will fit the screen width + for (let i = 0; i < stringToWrap.length; i++) { + // wrap if string is too long or we hit a line break + if (stringToWrap.charCodeAt(i) == 10 || g.stringWidth(stringToWrap.substring(startPos, i+1)) > 176) { + l.push(stringToWrap.substring(startPos, i)); + // skip the line break + if (stringToWrap.charCodeAt(i) == 10) { + i++; + } + startPos = i; + } + } + // Add the final line + l.push(stringToWrap.substring(startPos)); + + // Number of lines that can fit on the screen + let numLines = Math.floor(g.getHeight() / g.getFontHeight()); + + // If too many lines, reposition so the cursor can be seen + if (l.length > numLines) { let textPos = 0; let lineNum; for (lineNum = 0; lineNum < l.length; lineNum++) { - textPos = textPos + l[lineNum].length - 1; + textPos = textPos + l[lineNum].length; if (textPos >= cursorPos) break; } - l=l.slice(lineNum - l.length - 5); + l=l.slice(lineNum - l.length - numLines + 1); } - g.setColor(g.theme.fg); - g.setFontAlign(-1, -1, 0); - g.drawString(l.join('\n'), Bangle.appRect.x, Bangle.appRect.y); - // Draw punctuation flag - if (punctuationMode > 0) { + wrappedText = l.join('\n'); + } + + function draw() { + g.clearRect(Bangle.appRect).setClipRect(Bangle.appRect.x, Bangle.appRect.y, Bangle.appRect.x2, Bangle.appRect.y2); + + g.setColor(g.theme.fg); + g.setFont("Vector", settings.fontSize); + g.setFontAlign(-1, -1, 0); + g.drawString(wrappedText, Bangle.appRect.x, Bangle.appRect.y); + + // Draw punctuation or extended flags + if (punctuationMode || extendedMode) { let x = (g.getWidth() / 2) - 12; let y = g.getHeight() - 32; - g.setColor('#F00'); + g.setColor(punctuationMode ? '#F00' : '#0F0'); g.fillRect(x,y,x+24,y+32); g.setColor('#FFF'); g.setFont('6x8:4'); - g.drawString('P', x+4, y+4, false); + g.drawString(punctuationMode ? 'P' : 'E', x+4, y+4, false); } // Draw corners @@ -69,30 +179,27 @@ exports.input = function(options) { } function processPath() { - let capital = false; - // Punctuation paths end in 5 if (punctuationMode) { path = path + '5'; } - - // Capital letters end in 2, remove that and set a capital flag - // but only if the path isn't 232 (cursor right) - if (path != '232' && path.length > 2 && path.slice(-1) == '2') { - path = path.slice(0,-1); - capital = true; + // Extended paths end in 6 + if (extendedMode) { + path = path + '6'; } // Find character from path let char = characterSet[path]; - // Handle capitals - if (capital && char != 'undefined') { - if (char.charCodeAt(0)>96 && char.charCodeAt(0)<123) { - char = char.toUpperCase(); - } else { - // Anything that can't be capitalised is an invalid path - char = undefined; + // Unknown character, but ends in a 2 so may be a capital letter + if (char == 'undefined' && path.slice(-1) == '2') { + // Remove the 2 and look for a letter + char = characterSet[path.slice(0,-1)]; + // Handle capitals + if (char != 'undefined') { + if (char.charCodeAt(0)>96 && char.charCodeAt(0)<123) { + char = char.toUpperCase(); + } } } @@ -129,6 +236,17 @@ exports.input = function(options) { punctuationMode = false; break; } + // Enable extended mode + case '#ex-on': { + extendedMode = true; + break; + } + // Disable extended mode + case '#ex-off': { + extendedMode = false; + break; + } + // Cursor controls case '#cur-left': { if (cursorPos > 0) { cursorPos--; @@ -168,6 +286,22 @@ exports.input = function(options) { cursorPos = text.length; break; } + // Accents + case '#grave': + case '#acute': + case '#circumflex': + case '#umlaut': + case '#tilde': + case '#ring': + case '#cedilla': + // If the previous character can be accented, replace it with the accented version + if (cursorPos > 0) { + char = accentedCharacters[char][text.substring(cursorPos-1, cursorPos)]; + if (char != 'undefined') { + text = text.substring(0, cursorPos-1) + char + text.substring(cursorPos); + } + } + break; // Append character default: { text = text.substring(0, cursorPos) + char + text.substring(cursorPos); @@ -184,6 +318,7 @@ exports.input = function(options) { if (!chartShown) { if (e.b == 0) { // Finger lifted, process completed path processPath(); + wrapText(); draw(); } else { let corner = 0; @@ -217,10 +352,6 @@ exports.input = function(options) { } }; - // Draw initial string - require("widget_utils").hide(); - g.setBgColor(g.theme.bg); - draw(); return new Promise((resolve,reject) => { Bangle.setUI({ @@ -249,6 +380,13 @@ exports.input = function(options) { } } }); + + // Draw initial string + require("widget_utils").hide(); + g.setBgColor(g.theme.bg); + wrapText(); + draw(); + }); diff --git a/apps/kbedgewrite/metadata.json b/apps/kbedgewrite/metadata.json index cbf873a09..01436c6aa 100644 --- a/apps/kbedgewrite/metadata.json +++ b/apps/kbedgewrite/metadata.json @@ -1,14 +1,19 @@ { "id": "kbedgewrite", "name": "EdgeWrite keyboard", - "version":"0.01", + "version":"0.03", "description": "A library for text input via EdgeWrite swipe gestures", "icon": "app.png", "type":"textinput", "tags": "keyboard", "supports" : ["BANGLEJS2"], "readme": "README.md", + "screenshots" : [ { "url":"screenshot.png" } ], "storage": [ {"name":"textinput","url":"lib.js"}, - {"name":"kbedgewrite.charset.json","url":"characterset.json"} + {"name":"kbedgewrite.charset.json","url":"characterset.json"}, + {"name":"kbedgewrite.settings.js","url":"settings.js"} + ], + "data": [ + {"name":"kbedgewrite.json"} ] } diff --git a/apps/kbedgewrite/screenshot.png b/apps/kbedgewrite/screenshot.png new file mode 100644 index 000000000..48db615e1 Binary files /dev/null and b/apps/kbedgewrite/screenshot.png differ diff --git a/apps/kbedgewrite/settings.js b/apps/kbedgewrite/settings.js new file mode 100644 index 000000000..d6cc851e6 --- /dev/null +++ b/apps/kbedgewrite/settings.js @@ -0,0 +1,40 @@ +(function(back) { + var FILE = 'kbedgewrite.json'; + + // Load settings + var settings = Object.assign({ + fontSize: 32 + }, require('Storage').readJSON(FILE, true) || {}); + + function setSetting(key,value) { + settings[key] = value; + require('Storage').writeJSON(FILE, settings); + } + + // Helper method which uses int-based menu item for set of string values and their labels + function stringItems(key, startvalue, values, labels) { + return { + value: (startvalue === undefined ? 0 : values.indexOf(startvalue)), + format: v => labels[v], + min: 0, + max: values.length - 1, + wrap: true, + step: 1, + onchange: v => { + setSetting(key,values[v]); + } + }; + } + + // Helper method which breaks string set settings down to local settings object + function stringInSettings(name, values, labels) { + return stringItems(name,settings[name], values, labels); + } + + // Show the menu + E.showMenu({ + '' : { 'title' : 'EdgeWrite' }, + '< Back' : () => back(), + 'Font Size': stringInSettings('fontSize', [24, 32, 48], ['Small', 'Medium', 'Large']) + }); +}) \ No newline at end of file diff --git a/apps/largeclock/settings.js b/apps/largeclock/settings.js index 4ebf842ce..a5e35192a 100644 --- a/apps/largeclock/settings.js +++ b/apps/largeclock/settings.js @@ -82,4 +82,4 @@ }; E.showMenu(mainMenu); -}); +}) diff --git a/apps/launch/settings.js b/apps/launch/settings.js index 496a6d77e..f4f3bb31b 100644 --- a/apps/launch/settings.js +++ b/apps/launch/settings.js @@ -34,4 +34,4 @@ } }; E.showMenu(appMenu); -}); +}) diff --git a/apps/lint_exemptions.js b/apps/lint_exemptions.js index b5c6bc640..1b3a3cc11 100644 --- a/apps/lint_exemptions.js +++ b/apps/lint_exemptions.js @@ -515,12 +515,6 @@ module.exports = { "no-undef" ] }, - "apps/widbt_notify/widget.js": { - "hash": "16372ffcbc6bd1419ca326c7da40c2195f82a4bfceb6f123c15872624c4f0adf", - "rules": [ - "no-undef" - ] - }, "apps/widbgjs/widget.js": { "hash": "9852ce9aafb0a1ca3029d497282c8cdf07438ea36a3323313bad5b7569b1081b", "rules": [ @@ -534,7 +528,7 @@ module.exports = { ] }, "apps/usgs/settings.js": { - "hash": "af1b7bc7e041c1e6988b407b6c8ee66dbd6a0e181a20caf102d2abdb6dbd5ac0", + "hash": "00ee672a6920f5667bfbd2988fd2853cfd579895a843ae036a00028dcb13878d", "rules": [ "no-undef" ] @@ -1157,12 +1151,6 @@ module.exports = { "no-undef" ] }, - "apps/banglebridge/widget.js": { - "hash": "4ee8d6749e1d0e28c58ad871fd9f6ccbca2d716bb4fbd3511ba4c34a6a5897e1", - "rules": [ - "no-undef" - ] - }, "apps/bad/bad.app.js": { "hash": "d1354613102818190dd4e6e28fd715db7dc4d51b8e618cae61a3135529cc97eb", "rules": [ diff --git a/apps/locale/locales.js b/apps/locale/locales.js index 7e4105f3d..c704e0f90 100644 --- a/apps/locale/locales.js +++ b/apps/locale/locales.js @@ -177,7 +177,7 @@ var locales = { speed: "kmh", distance: { 0: "m", 1: "km" }, temperature: "°C", - ampm: { 0: "", 1: "" }, + ampm: { 0: "am", 1: "pm" }, timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, datePattern: { 0: "%d. %b %Y", "1": "%d.%m.%Y" }, // 1. Mär 2020 // 01.03.20 abmonth: "Jan,Feb,Mär,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez", @@ -194,7 +194,7 @@ var locales = { speed: "kmh", distance: { 0: "m", 1: "km" }, temperature: "°C", - ampm: { 0: "", 1: "" }, + ampm: { 0: "am", 1: "pm" }, timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, datePattern: { 0: "%Y/%m/%d", 1: "%y/%m/%d" }, abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", @@ -210,7 +210,7 @@ var locales = { speed: "kmh", distance: { 0: "m", 1: "km" }, temperature: "°C", - ampm: { 0: "", 1: "" }, + ampm: { 0: "am", 1: "pm" }, timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, datePattern: { 0: "%d %b %Y", 1: "%d-%m-%Y" }, // 28 feb 2020 // 28-02-2020 abday: "zo,ma,di,wo,do,vr,za", @@ -258,7 +258,7 @@ var locales = { speed: "km/h", distance: { 0: "m", 1: "km" }, temperature: "°C", - ampm: { 0: "", 1: "" }, + ampm: { 0: "am", 1: "pm" }, timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, datePattern: { 0: "%d %B %Y", "1": "%d/%m/%Y" }, // 1 mars 2020 // 01/03/2020 abmonth: "janv,févr,mars,avril,mai,juin,juil,août,sept,oct,nov,déc", @@ -290,7 +290,7 @@ var locales = { speed: 'km/h', distance: { "0": "m", "1": "km" }, temperature: '°C', - ampm: { 0: "", 1: "" }, + ampm: { 0: "am", 1: "pm" }, timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, datePattern: { 0: "%B %d %Y", "1": "%Y-%m-%d" }, // March 1 2020 // 2020-03-01 abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", @@ -306,7 +306,7 @@ var locales = { speed: "km/t", distance: { 0: "m", 1: "km" }, temperature: "°C", - ampm: { 0: "", 1: "" }, + ampm: { 0: "am", 1: "pm" }, timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, datePattern: { 0: "%d. %b. %Y", 1: "%d/%m %Y" }, // 1. feb. 2020 // 01/02 2020 // a better short ver. is 1/2 2020 but its not supported abmonth: "jan,feb,mar,apr,maj,jun,jul,aug,sep,okt,nov,dec", @@ -322,7 +322,7 @@ var locales = { speed: "km/h", distance: { 0: "m", 1: "km" }, temperature: "°C", - ampm: { 0: "", 1: "" }, + ampm: { 0: "am", 1: "pm" }, timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, datePattern: { 0: "%d. %b. %Y", 1: "%d/%m %Y" }, // 1. feb. 2020 // 01/02 2020 // a better short ver. is 1/2 2020 but its not supported abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", @@ -370,7 +370,7 @@ var locales = { speed: "kmh", distance: { 0: "m", 1: "km" }, temperature: "°C", - ampm: { 0: "", 1: "" }, + ampm: { 0: "am", 1: "pm" }, timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, datePattern: { 0: "%A, %d. %B %Y", "1": "%d.%m.%y" }, // Sonntag, 1. März 2020 // 01.03.20 abmonth: "Jän,Feb,März,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez", @@ -403,7 +403,7 @@ var locales = { speed: "kmh", distance: { 0: "m", 1: "km" }, temperature: "°C", - ampm: { 0: "", 1: "" }, + ampm: { 0: "am", 1: "pm" }, timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, datePattern: { 0: "%A, %d de %B de %Y", "1": "%d/%m/%y" }, // domingo, 1 de marzo de 2020 // 01/03/20 abmonth: "ene,feb,mar,abr,may,jun,jul,ago,sept,oct,nov,dic", @@ -420,7 +420,7 @@ var locales = { speed: "kmh", distance: { 0: "m", 1: "km" }, temperature: "°C", - ampm: { 0: "", 1: "" }, + ampm: { 0: "am", 1: "pm" }, timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, datePattern: { 0: "%A %B %d %Y", "1": "%d/%m/%y" }, // dimanche 1 mars 2020 // 01/03/20 abmonth: "janv.,févr.,mars,avril,mai,juin,juil.,août,sept.,oct.,nov.,déc.", @@ -484,7 +484,7 @@ var locales = { speed: 'kmh', distance: { "0": "m", "1": "km" }, temperature: '°C', - ampm: { 0: "", 1: "" }, + ampm: { 0: "am", 1: "pm" }, timePattern: { 0: "%HH:%MM.%SS", 1: "%HH:%MM" }, // 17:00.00 // 17:00 datePattern: { 0: "%d %b %Y", "1": "%d/%m/%Y" }, // 1 marzo 2020 // 01/03/2020 abmonth: "gen,feb,mar,apr,mag,giu,lug,ago,set,ott,nov,dic", @@ -500,7 +500,7 @@ var locales = { speed: 'kmh', distance: { "0": "m", "1": "km" }, temperature: '°C', - ampm: { 0: "", 1: "" }, + ampm: { 0: "am", 1: "pm" }, timePattern: { 0: "%HH:%MM.%SS", 1: "%HH:%MM" }, // 17:00.00 // 17:00 datePattern: { 0: "%d %b %Y", "1": "%d/%m/%Y" }, // 1 marzo 2020 // 01/03/2020 abmonth: "gen,feb,mar,apr,mag,giu,lug,ago,set,ott,nov,dic", @@ -516,7 +516,7 @@ var locales = { speed: 'kmh', distance: { "0": "m", "1": "km" }, temperature: '°C', - ampm: { 0: "", 1: "" }, + ampm: { 0: "am", 1: "pm" }, timePattern: { 0: "%HH.%MM.%SS", 1: "%HH.%MM" }, // 17.00.00 // 17.00 datePattern: { 0: "%A, %d. %B %Y", "1": "%Y-%m-%d" }, // Sunntag, 1. Märze 2020 // 2020-03-01 abmonth: "Jen,Hor,Mär,Abr,Mei,Brá,Hei,Öig,Her,Wím,Win,Chr", @@ -564,7 +564,7 @@ var locales = { speed: "km/h", distance: { 0: "m", 1: "km" }, temperature: "°C", - ampm: { 0: "", 1: "" }, + ampm: { 0: "am", 1: "pm" }, timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, datePattern: { 0: "%A %d %B de %Y", "1": "%d/%m/%Y" }, // dimenge 1 de març de 2020 // 01/03/2020 abmonth: "gen.,febr.,març,abril,mai,junh,julh,ago.,set.,oct.,nov.,dec.", @@ -660,7 +660,7 @@ var locales = { speed: "kmh", distance: { 0: "m", 1: "km" }, temperature: "°C", - ampm: { 0: "", 1: "" }, + ampm: { 0: "am", 1: "pm" }, timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, datePattern: { 0: "%d. %b %Y", "1": "%d.%m.%Y" }, // 1. Mar 2021 // 01.03.2021 abmonth: "Sty,Lut,Mar,Kwi,Maj,Cze,Lip,Sie,Wrz,Paź,Lis,Gru", @@ -676,7 +676,7 @@ var locales = { speed: "kmh", distance: { 0: "m", 1: "km" }, temperature: "°C", - ampm: { 0: "", 1: "" }, + ampm: { 0: "am", 1: "pm" }, timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, datePattern: { 0: "%d. %b %Y", "1": "%d.%m.%Y" }, // 1. Mar 2020 // 01.03.20 abmonth: "Jan,Feb,Mar,Apr,Mai,Jūn,Jūl,Aug,Sep,Okt,Nov,Dec", @@ -692,7 +692,7 @@ var locales = { speed: "kmt", distance: { 0: "m", 1: "km" }, temperature: "°C", - ampm: { 0: "", 1: "" }, + ampm: { 0: "am", 1: "pm" }, timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, datePattern: { 0: "%d. %b %Y", "1": "%d.%m.%Y" }, // 1. Mar 2020 // 01.03.20 abmonth: "Jan,Feb,Mar,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Des", @@ -708,7 +708,7 @@ var locales = { speed: "kmh", distance: { 0: "m", 1: "km" }, temperature: "°C", - ampm: { 0: "", 1: "" }, + ampm: { 0: "am", 1: "pm" }, timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, datePattern: { 0: "%d. %b %Y", "1": "%d.%m.%Y" }, // 1. Mar 2020 // 01.03.20 abmonth: "Jan,Feb,Mar,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Des", @@ -725,7 +725,7 @@ var locales = { speed: "kmh", distance: { 0: "m", 1: "km" }, temperature: "°C", - ampm: { 0: "", 1: "" }, + ampm: { 0: "am", 1: "pm" }, timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" }, datePattern: { 0: "%d %B %Y", "1": "%d/%m/%y" }, abmonth: "gen.,febr.,març,abr.,maig,juny,jul.,ag.,set.,oct.,nov.,des.", diff --git a/apps/locale/sanitycheck.js b/apps/locale/sanitycheck.js index 06c7ad3d6..ceca89b8d 100644 --- a/apps/locale/sanitycheck.js +++ b/apps/locale/sanitycheck.js @@ -4,15 +4,15 @@ */ const datetime_length_map = { // %A, %a, %B, %b vary depending on the locale, so they are calculated later - "%Y": [4, 4], - "%y": [2, 2], - "%m": [2, 2], - "%-m": [1, 2], - "%d": [2, 2], - "%-d": [1, 2], - "%HH": [2, 2], - "%MM": [2, 2], - "%SS": [2, 2], + "%Y": [4, 4, "2024", "2024"], + "%y": [2, 2, "24", "24"], + "%m": [2, 2, "10", "10"], + "%-m": [1, 2, "1", "10"], + "%d": [2, 2, "10", "10"], + "%-d": [1, 2, "1", "10"], + "%HH": [2, 2, "10", "10"], + "%MM": [2, 2, "10", "10"], + "%SS": [2, 2, "10", "10"], }; /** @@ -30,20 +30,21 @@ function getLengthOfDatetimeFormat(name, datetimeEspruino, locale, errors) { ["%a", locale.abday], ["%B", locale.month], ["%b", locale.abmonth], - ]){ + ]) { const length = [Infinity, 0]; for(const value of values.split(",")){ - if(length[0] > value.length) length[0] = value.length; - if(length[1] < value.length) length[1] = value.length; + if(length[0] > value.length) { length[0] = value.length; length[2] = value; } + if(length[1] < value.length) { length[1] = value.length; length[3] = value; } } length_map[symbol] = length; } // Find the length of the output - let formatLength = [0, 0]; + let formatLength = [0, 0, "", ""]; let i = 0; while (i < datetimeEspruino.length) { - if (datetimeEspruino[i] === "%") { + let ch = datetimeEspruino[i]; + if (ch === "%") { let match; for(const symbolLength of [2, 3]){ const length = length_map[datetimeEspruino.substring(i, i+symbolLength)]; @@ -57,16 +58,22 @@ function getLengthOfDatetimeFormat(name, datetimeEspruino, locale, errors) { if(match){ formatLength[0] += match.length[0]; formatLength[1] += match.length[1]; + formatLength[2] += match.length[2]; + formatLength[3] += match.length[3]; i += match.symbolLength; }else{ errors.push({name, value: datetimeEspruino, lang: locale.lang, error: `uses an unsupported format symbol: ${datetimeEspruino.substring(i, i+3)}`}); formatLength[0]++; formatLength[1]++; + formatLength[2]+=" "; + formatLength[3]+=" "; i++; } } else { formatLength[0]++; formatLength[1]++; + formatLength[2]+=ch; + formatLength[3]+=ch; i++; } } @@ -154,10 +161,10 @@ function checkLocale(locale, {speedUnits, distanceUnits, codePages, CODEPAGE_CON function checkFormatLength(name, value, min, max) { const length = getLengthOfDatetimeFormat(name, value, locale, errors); if (min && length[0] < min) { - errors.push({name, value, lang: locale.lang, error: `output must be longer than ${min-1} characters`}); + errors.push({name, value, lang: locale.lang, error: `output must be longer than ${min-1} characters (${length[2]} -> ${length[0]})`}); } if (max && length[1] > max) { - errors.push({name, value, lang: locale.lang, error: `output must be shorter than ${max+1} characters`}); + errors.push({name, value, lang: locale.lang, error: `output must be shorter than ${max+1} characters (${length[3]} -> ${length[1]})`}); } } function checkIsIn(name, value, listName, list) { diff --git a/apps/messagegui/ChangeLog b/apps/messagegui/ChangeLog index 42a385b52..fb1214933 100644 --- a/apps/messagegui/ChangeLog +++ b/apps/messagegui/ChangeLog @@ -110,4 +110,5 @@ 0.81: Fix issue stopping Music message for being marked as read Make sure play button image is transparent Add top-right menu to music playback to allow message to be deleted -0.82: Add option to not open the first unread message +0.82: Stop buzzing when a message is removed (e.g. call answered) +0.83: Add option to not open the first unread message diff --git a/apps/messagegui/lib.js b/apps/messagegui/lib.js index 54d79866a..43141531f 100644 --- a/apps/messagegui/lib.js +++ b/apps/messagegui/lib.js @@ -18,6 +18,10 @@ exports.listener = function(type, msg) { clearTimeout(exports.messageTimeout); delete exports.messageTimeout; } + if (type==="clearAll") { + require("messages").stopBuzz(); + return; + } if (msg.t==="remove") { // we won't open the UI for removed messages, so make sure to delete it from flash if (Bangle.MESSAGES) { @@ -25,6 +29,8 @@ exports.listener = function(type, msg) { require("messages").apply(msg, Bangle.MESSAGES); if (!Bangle.MESSAGES.length) delete Bangle.MESSAGES; } + if(type!=="music") + require("messages").stopBuzz(); return require("messages").save(msg); // always write removal to flash } diff --git a/apps/messagegui/metadata.json b/apps/messagegui/metadata.json index 4e3ca6057..e41a28801 100644 --- a/apps/messagegui/metadata.json +++ b/apps/messagegui/metadata.json @@ -2,7 +2,7 @@ "id": "messagegui", "name": "Message UI", "shortName": "Messages", - "version": "0.82", + "version": "0.83", "description": "Default app to display notifications from iOS and Gadgetbridge/Android", "icon": "app.png", "type": "app", diff --git a/apps/messagelist/settings.js b/apps/messagelist/settings.js index 1dc80ade1..1bf60c744 100644 --- a/apps/messagelist/settings.js +++ b/apps/messagelist/settings.js @@ -136,4 +136,4 @@ } showMainMenu(); -}); +}) diff --git a/apps/messages/settings.js b/apps/messages/settings.js index cb4644d4a..d5bc0332e 100644 --- a/apps/messages/settings.js +++ b/apps/messages/settings.js @@ -102,4 +102,4 @@ }, }; E.showMenu(mainmenu); -}); +}) diff --git a/apps/messages_light/messages_light.settings.js b/apps/messages_light/messages_light.settings.js index cd813d928..890c23692 100644 --- a/apps/messages_light/messages_light.settings.js +++ b/apps/messages_light/messages_light.settings.js @@ -23,5 +23,4 @@ }, }; E.showMenu(mainmenu); - }); - \ No newline at end of file + }) diff --git a/apps/messagesoverlay/settings.js b/apps/messagesoverlay/settings.js index cd76bf115..93e57fae0 100644 --- a/apps/messagesoverlay/settings.js +++ b/apps/messagesoverlay/settings.js @@ -63,4 +63,4 @@ } E.showMenu(buildMainMenu()); -}); +}) diff --git a/apps/metronome/settings.js b/apps/metronome/settings.js index 1dd4d92df..3eacd737d 100644 --- a/apps/metronome/settings.js +++ b/apps/metronome/settings.js @@ -45,4 +45,4 @@ }, }; E.showMenu(menu); -}); +}) diff --git a/apps/multitimer/settings.js b/apps/multitimer/settings.js index 4faeb2573..fbed187a9 100644 --- a/apps/multitimer/settings.js +++ b/apps/multitimer/settings.js @@ -29,4 +29,4 @@ } }, }); -}); +}) diff --git a/apps/nesclock/settings.js b/apps/nesclock/settings.js index 0bbecc2ce..154b3b6b6 100644 --- a/apps/nesclock/settings.js +++ b/apps/nesclock/settings.js @@ -39,4 +39,4 @@ E.showMenu(mainmenu); -}); +}) diff --git a/apps/nightwatch/nightwatch.settings.js b/apps/nightwatch/nightwatch.settings.js index 744ebd8dc..f4afe2e85 100644 --- a/apps/nightwatch/nightwatch.settings.js +++ b/apps/nightwatch/nightwatch.settings.js @@ -22,4 +22,4 @@ } }, }); -}); +}) diff --git a/apps/owmweather/settings.js b/apps/owmweather/settings.js index f6b84c785..69aabfad0 100644 --- a/apps/owmweather/settings.js +++ b/apps/owmweather/settings.js @@ -81,4 +81,4 @@ } E.showMenu(buildMainMenu()); -}); +}) diff --git a/apps/pebble/pebble.settings.js b/apps/pebble/pebble.settings.js index 83032270a..c4b860792 100644 --- a/apps/pebble/pebble.settings.js +++ b/apps/pebble/pebble.settings.js @@ -54,4 +54,4 @@ } }, }); -}); +}) diff --git a/apps/pebbled/pebbled.settings.js b/apps/pebbled/pebbled.settings.js index d54517a70..29f8c85bc 100644 --- a/apps/pebbled/pebbled.settings.js +++ b/apps/pebbled/pebbled.settings.js @@ -55,4 +55,4 @@ }, } }); -}); +}) diff --git a/apps/pokertimer/ChangeLog b/apps/pokertimer/ChangeLog new file mode 100644 index 000000000..ceddbf533 --- /dev/null +++ b/apps/pokertimer/ChangeLog @@ -0,0 +1,6 @@ +0.01: Packaged app +0.02: Fix alert buzz time, Indicate when paused +0.03: Start app with paused timer +0.04: Added 20-second warning buzz +0.05: Added screenshots +0.06: Fix bug when play/pause during alert diff --git a/apps/pokertimer/LICENSE b/apps/pokertimer/LICENSE new file mode 100644 index 000000000..a2bb2da01 --- /dev/null +++ b/apps/pokertimer/LICENSE @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright © 2024 Keith Irwin + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/apps/pokertimer/README.md b/apps/pokertimer/README.md new file mode 100644 index 000000000..ce8316def --- /dev/null +++ b/apps/pokertimer/README.md @@ -0,0 +1,55 @@ +# Poker Timer +*v.0.06* + +A blinds timer for poker. Don't know what that means? See [Wikipedia: Blind (poker)](https://en.wikipedia.org/wiki/Blind_(poker)) and [Wikipedia: Texas hold 'em](https://en.wikipedia.org/wiki/Texas_hold_%27em). + +![Screenshot showing countdown paused on start](screenshots/01_paused-start.png) +![Screenshot showing active countdown](screenshots/02_counting-down.png) +![Screenshot showing blinds up alert](screenshots/03_blinds-up.png) + +The blinds are hardcoded and go up every ten minutes: + +- 1, 2 +- 2, 4 +- 4, 8 +- 5, 10 +- 10, 20 +- 20, 40 +- 40, 80 + +... and so on, doubling each round. + +## Features + +- Starts paused +- Button to pause/resume +- 20-second warning buzz +- Auto-exit after round 25 + +## Usage + +The timer will start as soon as you open the app. Time left in the round is on the top of the screen, currnt small and big blinds are shown below. After ten minutes, it will vibrate and flash and show the new blind. Then it starts over. + +### Auto-exit + +The program will automatically exit after the 25 round. This is not a bug. If the blinds double again, it will perform some kind of overflow and convert the blind values to floats. + +The blinds in round 25 are `20971520 / 41943040`. You probably aren't still playing poker at that point and just forgot to exit the program. + +## Controls + + - **Pause/Resume:** Press the button + - **Exit:** hold down the button + +## Roadmap + +- Set settings +- Better graphics + +## Requests + +[Contact Keith Irwin](https://www.ki9.us/contact/) + +## Creator + +[Keith Irwin](https://www.ki9.us) diff --git a/apps/pokertimer/app-icon.js b/apps/pokertimer/app-icon.js new file mode 100644 index 000000000..bf7227a80 --- /dev/null +++ b/apps/pokertimer/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("kso4cB7nW///7fWm2Vw8p7fOgH6lM6hEqgQCDkNCgmSpMkyUQqkEAQsJBYIOBkGohACD8dIiQaCpFBhUQAQVD+9qDQeQwWgkGCikg1lxknZBwUhgkooICCnMjpO1qIOBoUKwGCAQXt32TtMkwUkwkVgIdFg3SGQOBNYoCChuklfAgBQCPokqjdBluCNAJTBkN4UwVBm2C5ckxFBqEQq/+BYKMBtstz1JiBuC/+T2gjC7VYlmSkMFoFOQQON0mShO0iFNkhuC8iCBk4aBwdpFgNENwMOVgft1Ms9uiwNJLIM/VgWTxUFjdrtsUiUQ0NPXYe0qE27XRoLdC/wpCpxvBtuzpMiboX8DQXtQYPbtto0jdCm87zvOIwMh23YgDdBNwNBh/bYoL7BtuapDdE2VGWYMgw3brUpbocF/mcIYMIlu10WCpL4FihEB3PboBaBiFUGQOhAQcbqkgLQL1CdIYCBm2oIgNIru36VJkD+BboUgxMkyGcyOk1VIkGFboWhiRuBrHiqpBBIgUVboJEBoUf3dSK4IpBhUTtEiIgOEjlpGolBg3QoiuBJoZWCAQMN0kJFIIyCIIQCBkNb1JEBc4Ul2zKBAQW7qLzDBw7pBBYYCDwgpCoUEBYoCC0A7CBY4CCykooALIBwcRAoQ")) diff --git a/apps/pokertimer/app.js b/apps/pokertimer/app.js new file mode 100644 index 000000000..9d584b3b2 --- /dev/null +++ b/apps/pokertimer/app.js @@ -0,0 +1,142 @@ +const BLIND_INTERVAL = 600; // 10 minutes in seconds +const BLINDSUP_ALERT_DURATION = 10000; // 10 seconds in ms + +// Convert seconds to mm:ss +const secondsToMinutes = (s) => { + const mm = Math.floor(s/60); + const ss = s - mm * 60; + return `${mm}:${String(ss).padStart(2,'0')}`; +}; + +// Format screen +const fmtDark = () => { + g.clear(); + g.setFontAlign(0,0); + g.setBgColor(0,0.5,0); + g.setColor(1,1,1); +}; +const fmtLight = () => { + g.clear(); + g.setFontAlign(0,0); + g.setBgColor(0.5,1,0.5); + g.setColor(0,0,0); +}; + +// Start/stop/pause/resume timer +const startTimer = () => { + timer_running = true; tick(); + timer = setInterval(tick, 1000); +}; +const stopTimer = () => { + clearInterval(timer); + timer_running = false; +}; +const pauseResume = () => { + if (is_alerting) return; + if (timer_running) { + stopTimer(); + g.setFont('Vector',15); + g.drawString('(PAUSED)', + g.getWidth()/2, g.getHeight()*7/8); + } + else startTimer(); +}; + +// Calculate blinds for a round +const getBlinds = (i) => { + let small; + if (i===0) small = 1; + else if (i===1) small = 2; + else if (i===2) small = 4; + else small = 5*(Math.pow(2,(i-3))); + return [small, small*2]; +}; + +// Sound the alarm +const blindsUp = () => { + is_alerting = true; + // Display message + const showMessage = () => { + g.clear(); + g.setFont('Vector',34); + g.drawString('Blinds Up!', + g.getWidth()/2, g.getHeight()/3); + g.setFont('Vector',40); + g.drawString(`${blinds[0]}/${blinds[1]}`, + g.getWidth()/2, g.getHeight()*2/3); + }; + stopTimer(); + // Increase blinds + b++; + // TODO: Kill program between round 25 and 26 + blinds = getBlinds(b); + console.log(`Blinds for round ${b} are ${blinds[0]} / ${blinds[1]}`); + // Buzz and light up every second + const buzzInterval = setInterval(() => { + Bangle.buzz(500); + Bangle.setLCDPower(1); + }, 1000); + // Invert colors every second + fmtLight(); showMessage(); let dark = false; + const flashInterval = setInterval(() => { + if (dark) { + fmtLight(); + dark = false; + } else { + fmtDark(); + dark = true; + } showMessage(); + }, 500); + // Restart timer + setTimeout(() => { + is_alerting = false; + fmtDark(); tick(); + clearInterval(buzzInterval); + clearInterval(flashInterval); + time_left = BLIND_INTERVAL + 1; + startTimer(); + }, BLINDSUP_ALERT_DURATION); +}; + +// Tick every second +const tick = () => { + if (!timer_running) return; + time_left--; + // 20-second warning buzz + if (time_left==20) { + const buzzInterval = setInterval(Bangle.buzz, 500); + setTimeout(() => { + clearInterval(buzzInterval); + }, 5000); + } + if (time_left<=0) blindsUp(); + else { + g.clear(); + g.setFont('Vector',40); + g.drawString( + secondsToMinutes(time_left), + g.getWidth()/2, g.getHeight()/3); + g.drawString( + `${blinds[0]}/${blinds[1]}`, + g.getWidth()/2, g.getHeight()*2/3); + } + return; +}; + +// button listener +Bangle.setUI({ + mode: 'custom', + btn: pauseResume, +}); + +// RUNTIME +fmtDark(); +let time_left = BLIND_INTERVAL + 1; +let b = 0; +let blinds = getBlinds(b); +let timer_running = true; +let is_alerting = false; +let timer = setInterval(tick, 1000); +tick(); +// Start paused +pauseResume(); diff --git a/apps/pokertimer/app.png b/apps/pokertimer/app.png new file mode 100644 index 000000000..31a48dbb0 Binary files /dev/null and b/apps/pokertimer/app.png differ diff --git a/apps/pokertimer/metadata.json b/apps/pokertimer/metadata.json new file mode 100644 index 000000000..a22229e4f --- /dev/null +++ b/apps/pokertimer/metadata.json @@ -0,0 +1,15 @@ +{ + "id": "pokertimer", + "name": "Poker Timer", + "shortName":"Poker Timer", + "readme":"README.md", + "icon": "app.png", + "version":"0.06", + "description": "A blinds timer for use with Texas Hold 'Em", + "tags": "poker", + "supports": ["BANGLEJS2"], + "storage": [ + {"name":"pokertimer.app.js","url":"app.js"}, + {"name":"pokertimer.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/pokertimer/screenshots/01_paused-start.png b/apps/pokertimer/screenshots/01_paused-start.png new file mode 100644 index 000000000..929bd473e Binary files /dev/null and b/apps/pokertimer/screenshots/01_paused-start.png differ diff --git a/apps/pokertimer/screenshots/02_counting-down.png b/apps/pokertimer/screenshots/02_counting-down.png new file mode 100644 index 000000000..38425a68b Binary files /dev/null and b/apps/pokertimer/screenshots/02_counting-down.png differ diff --git a/apps/pokertimer/screenshots/03_blinds-up.png b/apps/pokertimer/screenshots/03_blinds-up.png new file mode 100644 index 000000000..b01749719 Binary files /dev/null and b/apps/pokertimer/screenshots/03_blinds-up.png differ diff --git a/apps/popconlaunch/settings.js b/apps/popconlaunch/settings.js index 29528c5dd..fa27f816b 100644 --- a/apps/popconlaunch/settings.js +++ b/apps/popconlaunch/settings.js @@ -12,4 +12,4 @@ }, }; E.showMenu(menu); -}); +}) diff --git a/apps/powermanager/interface.html b/apps/powermanager/interface.html index 51c31cc81..450ad4f26 100644 --- a/apps/powermanager/interface.html +++ b/apps/powermanager/interface.html @@ -148,7 +148,7 @@ function viewDeferredTable(filename) { //try finding possible sources for the given function, currently does not work because function.toString() not being identical to code in the *.js files. - /*Puck.eval(`require("Storage").list(/.*.js$/)`, (f)=>{ + /*Puck.eval(`require("Storage").list(/.*\.js$/)`, (f)=>{ console.log("Found files:", f, rows[1].func); for (let file of f){ let query = `require("Storage").read('${file}').includes('${rows[1].func}')`; diff --git a/apps/poweroff/ChangeLog b/apps/poweroff/ChangeLog index a62a1bc43..d39fb5a31 100644 --- a/apps/poweroff/ChangeLog +++ b/apps/poweroff/ChangeLog @@ -2,3 +2,4 @@ 0.02: Add prompt before shutdown 0.03: Add settings to configure prompt 0.04: Minor code improvements +0.05: Comment out unused function in settings.js diff --git a/apps/poweroff/metadata.json b/apps/poweroff/metadata.json index 9c558bc5b..218e4b441 100644 --- a/apps/poweroff/metadata.json +++ b/apps/poweroff/metadata.json @@ -1,7 +1,7 @@ { "id": "poweroff", "name": "Poweroff", "shortName":"Poweroff", -"version": "0.04", +"version": "0.05", "description": "Simple app to power off your Bangle.js", "icon": "app.png", "tags": "tool, poweroff, shutdown", diff --git a/apps/poweroff/settings.js b/apps/poweroff/settings.js index b22a7918a..8c700c8cd 100644 --- a/apps/poweroff/settings.js +++ b/apps/poweroff/settings.js @@ -8,26 +8,26 @@ require('Storage').writeJSON(FILE, settings); } - // Helper method which uses int-based menu item for set of string values - function stringItems(startvalue, writer, values) { - return { - value: (startvalue === undefined ? 0 : values.indexOf(startvalue)), - format: v => values[v], - min: 0, - max: values.length - 1, - wrap: true, - step: 1, - onchange: v => { - writer(values[v]); - writeSettings(); - } - }; - } - - // Helper method which breaks string set settings down to local settings object - function stringInSettings(name, values) { - return stringItems(settings[name], v => settings[name] = v, values); - } + //// Helper method which uses int-based menu item for set of string values + //function stringItems(startvalue, writer, values) { + // return { + // value: (startvalue === undefined ? 0 : values.indexOf(startvalue)), + // format: v => values[v], + // min: 0, + // max: values.length - 1, + // wrap: true, + // step: 1, + // onchange: v => { + // writer(values[v]); + // writeSettings(); + // } + // }; + //} + // + //// Helper method which breaks string set settings down to local settings object + //function stringInSettings(name, values) { + // return stringItems(settings[name], v => settings[name] = v, values); + //} var mainmenu = { "": { @@ -43,4 +43,4 @@ } }; E.showMenu(mainmenu); -}); +}) diff --git a/apps/puzzle15/puzzle15.settings.js b/apps/puzzle15/puzzle15.settings.js index 352ec4315..152b354b5 100644 --- a/apps/puzzle15/puzzle15.settings.js +++ b/apps/puzzle15/puzzle15.settings.js @@ -45,6 +45,4 @@ // Actually display the menu E.showMenu(mainmenu); -}); - -// end of file \ No newline at end of file +}) diff --git a/apps/qcenter/settings.js b/apps/qcenter/settings.js index ab7d561d1..2db1f28cc 100644 --- a/apps/qcenter/settings.js +++ b/apps/qcenter/settings.js @@ -152,4 +152,4 @@ } showMainMenu(); -}); +}) diff --git a/apps/rebble/ChangeLog b/apps/rebble/ChangeLog index 587c02ac8..0985cd3d5 100644 --- a/apps/rebble/ChangeLog +++ b/apps/rebble/ChangeLog @@ -16,3 +16,4 @@ 0.16: Use 'modules/suncalc.js' to avoid it being copied 8 times for different apps 0.17: Add fullscreen option (on by default) to show widgets, adjust sidebar 1 and 2 when fullscreen is off 0.18: Minor code improvements +0.19: Fix steps counter text white when it should be black diff --git a/apps/rebble/metadata.json b/apps/rebble/metadata.json index 3a06bebce..0a76c65a8 100644 --- a/apps/rebble/metadata.json +++ b/apps/rebble/metadata.json @@ -2,7 +2,7 @@ "id": "rebble", "name": "Rebble Clock", "shortName": "Rebble", - "version": "0.18", + "version": "0.19", "description": "A Pebble style clock, with configurable background, three sidebars including steps, day, date, sunrise, sunset, long live the rebellion", "readme": "README.md", "icon": "rebble.png", diff --git a/apps/rebble/rebble.app.js b/apps/rebble/rebble.app.js index 3f97dc5cb..68cb909ef 100644 --- a/apps/rebble/rebble.app.js +++ b/apps/rebble/rebble.app.js @@ -219,6 +219,8 @@ Graphics.prototype.setFontKdamThmor = function(scale) { } let drawSideBar2Alt=function() { + setTextColor(); + // steps g.drawImage(boot_img, 113, 59, { scale: 1 }); setSmallFont(); diff --git a/apps/rebbleagenda/settings.js b/apps/rebbleagenda/settings.js index 8ed2ceae5..40be27920 100644 --- a/apps/rebbleagenda/settings.js +++ b/apps/rebbleagenda/settings.js @@ -66,4 +66,4 @@ }, } }); -}); \ No newline at end of file +}) diff --git a/apps/recorder/clkinfo.js b/apps/recorder/clkinfo.js index 6ca4f59d5..cde5c8ef4 100644 --- a/apps/recorder/clkinfo.js +++ b/apps/recorder/clkinfo.js @@ -35,4 +35,4 @@ }, ] : [], }; -}); +}) diff --git a/apps/rep/settings.js b/apps/rep/settings.js index bfadacda1..8643f4274 100644 --- a/apps/rep/settings.js +++ b/apps/rep/settings.js @@ -41,4 +41,4 @@ }; } E.showMenu(menu); -}); +}) diff --git a/apps/reply/metadata.json b/apps/reply/metadata.json index 7f1dd6812..fa1abe920 100644 --- a/apps/reply/metadata.json +++ b/apps/reply/metadata.json @@ -6,11 +6,11 @@ "type": "module", "provides_modules" : ["reply"], "tags": "", - "supports" : ["BANGLEJS2"], + "supports": ["BANGLEJS","BANGLEJS2"], "readme": "README.md", "interface": "interface.html", "storage": [ {"name":"reply","url":"lib.js"} ], "data": [{"name":"replies.json"}] -} \ No newline at end of file +} diff --git a/apps/saclock/settings.js b/apps/saclock/settings.js index fc50d04dc..10d0489a7 100644 --- a/apps/saclock/settings.js +++ b/apps/saclock/settings.js @@ -20,4 +20,4 @@ hideWidgets: settings.hideWidgets, }); E.showMenu(menu); -}); +}) diff --git a/apps/sched/ChangeLog b/apps/sched/ChangeLog index 2ecbb9a94..83eb2ca20 100644 --- a/apps/sched/ChangeLog +++ b/apps/sched/ChangeLog @@ -29,3 +29,4 @@ 0.26: Fix hitting snooze on an alarm after when the snooze would've fired 0.27: Tapping clkinfo enables/disables the selected alarm 0.28: Added an icon for disabled events +0.29: Improve clkinfo startup time by 10ms \ No newline at end of file diff --git a/apps/sched/clkinfo.js b/apps/sched/clkinfo.js index 73ba4a259..8d4d747c0 100644 --- a/apps/sched/clkinfo.js +++ b/apps/sched/clkinfo.js @@ -1,15 +1,7 @@ (function() { - const alarm = require('sched'); - const iconAlarmOn = atob("GBiBAAAAAAAAAAYAYA4AcBx+ODn/nAP/wAf/4A/n8A/n8B/n+B/n+B/n+B/n+B/h+B/4+A/+8A//8Af/4AP/wAH/gAB+AAAAAAAAAA=="); - const iconAlarmOff = atob("GBiBAAAAAAAAAAYAYA4AcBx+ODn/nAP/wAf/4A/n8A/n8B/n+B/n+B/nAB/mAB/geB/5/g/5tg/zAwfzhwPzhwHzAwB5tgAB/gAAeA=="); - const iconTimerOn = atob("GBiBAAAAAAAAAAAAAAf/4Af/4AGBgAGBgAGBgAD/AAD/AAB+AAA8AAA8AAB+AADnAADDAAGBgAGBgAGBgAf/4Af/4AAAAAAAAAAAAA=="); - const iconTimerOff = atob("GBiBAAAAAAAAAAAAAAf/4Af/4AGBgAGBgAGBgAD/AAD/AAB+AAA8AAA8AAB+AADkeADB/gGBtgGDAwGDhwfzhwfzAwABtgAB/gAAeA=="); - const iconEventOn = atob("GBiBAAAAAAAAAAAAAA//8B//+BgAGBgAGBgAGB//+B//+B//+B/++B/8+B/5+B8z+B+H+B/P+B//+B//+B//+A//8AAAAAAAAAAAAA=="); - const iconEventOff = atob("GBgBAAAAAAAAAAAAD//wH//4GAAYGAAYGAAYH//4H//4H//4H/74H/wAH/gAHzB4H4H+H8m2H/MDH/OHH/OHD/MDAAG2AAH+AAB4"); - //from 0 to max, the higher the closer to fire (as in a progress bar) - function getAlarmValue(a){ - let min = Math.round(alarm.getTimeToAlarm(a)/(60*1000)); + function getAlarmValue(a) { + let min = Math.round(require('sched').getTimeToAlarm(a)/(60*1000)); if(!min) return 0; //not active or more than a day return getAlarmMax(a)-min; } @@ -23,20 +15,20 @@ function getAlarmIcon(a) { if(a.on) { - if(a.timer) return iconTimerOn; - if(a.date) return iconEventOn; - return iconAlarmOn; + if(a.timer) return atob("GBiBAAAAAAAAAAAAAAf/4Af/4AGBgAGBgAGBgAD/AAD/AAB+AAA8AAA8AAB+AADnAADDAAGBgAGBgAGBgAf/4Af/4AAAAAAAAAAAAA=="); + if(a.date) return atob("GBiBAAAAAAAAAAAAAA//8B//+BgAGBgAGBgAGB//+B//+B//+B/++B/8+B/5+B8z+B+H+B/P+B//+B//+B//+A//8AAAAAAAAAAAAA=="); + return atob("GBiBAAAAAAAAAAYAYA4AcBx+ODn/nAP/wAf/4A/n8A/n8B/n+B/n+B/n+B/n+B/h+B/4+A/+8A//8Af/4AP/wAH/gAB+AAAAAAAAAA=="); } else { - if(a.timer) return iconTimerOff; - if(a.date) return iconEventOff; - return iconAlarmOff; + if(a.timer) return atob("GBiBAAAAAAAAAAAAAAf/4Af/4AGBgAGBgAGBgAD/AAD/AAB+AAA8AAA8AAB+AADkeADB/gGBtgGDAwGDhwfzhwfzAwABtgAB/gAAeA=="); + if(a.date) return atob("GBgBAAAAAAAAAAAAD//wH//4GAAYGAAYGAAYH//4H//4H//4H/74H/wAH/gAHzB4H4H+H8m2H/MDH/OHH/OHD/MDAAG2AAH+AAB4"); + return atob("GBiBAAAAAAAAAAYAYA4AcBx+ODn/nAP/wAf/4A/n8A/n8B/n+B/n+B/nAB/mAB/geB/5/g/5tg/zAwfzhwPzhwHzAwB5tgAB/gAAeA=="); } } function getAlarmText(a){ if(a.timer) { if(!a.on) return /*LANG*/"off"; - let time = Math.round(alarm.getTimeToAlarm(a)/(60*1000)); + let time = Math.round(require('sched').getTimeToAlarm(a)/(60*1000)); if(time > 60) time = Math.round(time / 60) + "h"; else @@ -52,7 +44,7 @@ //workaround for sorting undefined values function getAlarmOrder(a) { - let val = alarm.getTimeToAlarm(a); + let val = require('sched').getTimeToAlarm(a); if(typeof val == "undefined") return 86400*1000; return val; } @@ -66,7 +58,7 @@ const minute = 60 * 1000; const halfhour = 30 * minute; const hour = 2 * halfhour; - let msecs = alarm.getTimeToAlarm(a); + let msecs = require('sched').getTimeToAlarm(a); if(typeof msecs == "undefined" || msecs == 0) return []; if(msecs > hour) { //refresh every half an hour @@ -103,15 +95,15 @@ }, switchTimeout); } - var img = iconAlarmOn; - var all = alarm.getAlarms(); + // read the file direct here to avoid loading sched library (saves 10ms!) + var all = /*require('sched').getAlarms()*/require("Storage").readJSON("sched.json",1)||[]; //get only alarms not created by other apps var alarmItems = { name: /*LANG*/"Alarms", - img: img, + img: getAlarmIcon({on:1}), dynamic: true, items: all.filter(a=>!a.appid) - //.sort((a,b)=>alarm.getTimeToAlarm(a)-alarm.getTimeToAlarm(b)) + //.sort((a,b)=>require('sched').getTimeToAlarm(a)-require('sched').getTimeToAlarm(b)) .sort((a,b)=>getAlarmOrder(a)-getAlarmOrder(b)) .map(a => ({ name: null, @@ -135,13 +127,12 @@ run: function() { if (a.date) return; // ignore events a.on = !a.on; - if(a.on && a.timer) alarm.resetTimer(a); + if(a.on && a.timer) require('sched').resetTimer(a); this.emit("redraw"); - alarm.setAlarms(all); - alarm.reload(); // schedule/unschedule the alarm + require('sched').setAlarms(all); + require('sched').reload(); // schedule/unschedule the alarm } })), }; - return alarmItems; }) diff --git a/apps/sched/interface.html b/apps/sched/interface.html index 73ceff3c1..fa4132574 100644 --- a/apps/sched/interface.html +++ b/apps/sched/interface.html @@ -173,7 +173,7 @@ function renderAlarm(alarm, exists) { }; } } - tdType.textContent = type; + tdType.textContent = type + (alarm.appid ? `\n(${alarm.appid})` : ""); if (!exists) { const asterisk = document.createElement('sup'); asterisk.textContent = '*'; @@ -331,6 +331,10 @@ function getData() { alarms.sort((a, b) => { let x; + // move app specific alarms to the bottom + x = !!a.appid - !!b.appid; + if(x) return x; + x = !!b.date - !!a.date; if(x) return x; diff --git a/apps/sched/lib.js b/apps/sched/lib.js index fcd971fc4..e11448a18 100644 --- a/apps/sched/lib.js +++ b/apps/sched/lib.js @@ -1,5 +1,6 @@ // Return an array of all alarms exports.getAlarms = function() { + // we do this direct in clkinfo.js to avoid loading the library return require("Storage").readJSON("sched.json",1)||[]; }; // Write a list of alarms back to storage diff --git a/apps/sched/metadata.json b/apps/sched/metadata.json index 4141c7797..274b83d14 100644 --- a/apps/sched/metadata.json +++ b/apps/sched/metadata.json @@ -1,7 +1,7 @@ { "id": "sched", "name": "Scheduler", - "version": "0.28", + "version": "0.29", "description": "Scheduling library for alarms and timers", "icon": "app.png", "type": "scheduler", diff --git a/apps/sched/settings.js b/apps/sched/settings.js index 76036db2b..c03cd6679 100644 --- a/apps/sched/settings.js +++ b/apps/sched/settings.js @@ -76,4 +76,4 @@ require("sched").setSettings(settings); }) }); -}); +}) diff --git a/apps/score/score.settings.js b/apps/score/score.settings.js index 88e367821..f32bb6189 100644 --- a/apps/score/score.settings.js +++ b/apps/score/score.settings.js @@ -216,4 +216,4 @@ } }); -})(); +})() diff --git a/apps/sensortools/settings.js b/apps/sensortools/settings.js index ae631e60c..a5d3dfad1 100644 --- a/apps/sensortools/settings.js +++ b/apps/sensortools/settings.js @@ -102,4 +102,4 @@ } E.showMenu(buildMainMenu()); -}); +}) diff --git a/apps/setuichange/ChangeLog b/apps/setuichange/ChangeLog index 89a3d1964..8a8e53131 100644 --- a/apps/setuichange/ChangeLog +++ b/apps/setuichange/ChangeLog @@ -1,5 +1,4 @@ 0.01: New App! -0.02: Fix case where we tried to push to Bangle.btnWatches but it wasn't - defined. -0.03: Throw exception if trying to add custom drag handler on mode updown and - leftright. +0.02: Fix case where we tried to push to Bangle.btnWatches but it wasn't defined. +0.03: Throw exception if trying to add custom drag handler on mode updown and leftright. +0.04: Bangle.js 1 support. No change to Bangle.js 2. diff --git a/apps/setuichange/boot-b1.js b/apps/setuichange/boot-b1.js new file mode 100644 index 000000000..df2bde62e --- /dev/null +++ b/apps/setuichange/boot-b1.js @@ -0,0 +1,118 @@ +Bangle.setUI = (function(mode, cb) { + var options = {}; + if ("object"==typeof mode) { + options = mode; + mode = options.mode; + if (!mode) throw new Error("Missing mode in setUI({...})"); + } + var redraw = true; + if (global.WIDGETS && WIDGETS.back) { + redraw = false; + WIDGETS.back.remove(mode && options.back); + } + if (Bangle.btnWatches) { + Bangle.btnWatches.forEach(clearWatch); + delete Bangle.btnWatches; + } + if (Bangle.swipeHandler) { + Bangle.removeListener("swipe", Bangle.swipeHandler); + delete Bangle.swipeHandler; + } + if (Bangle.touchHandler) { + Bangle.removeListener("touch", Bangle.touchHandler); + delete Bangle.touchHandler; + } + delete Bangle.uiRedraw; + delete Bangle.CLOCK; + if (Bangle.uiRemove) { + let r = Bangle.uiRemove; + delete Bangle.uiRemove; // stop recursion if setUI is called inside uiRemove + r(); + } + g.reset();// reset graphics state, just in case + if (!mode) return; + if (mode=="updown") { + Bangle.btnWatches = [ + setWatch(function() { cb(-1); }, BTN1, {repeat:1,edge:"rising"}), + setWatch(function() { cb(1); }, BTN3, {repeat:1,edge:"rising"}), + setWatch(function() { cb(); }, BTN2, {repeat:1,edge:"rising"}) + ]; + } else if (mode=="leftright") { + Bangle.btnWatches = [ + setWatch(function() { cb(-1); }, BTN1, {repeat:1,edge:"rising"}), + setWatch(function() { cb(1); }, BTN3, {repeat:1,edge:"rising"}), + setWatch(function() { cb(); }, BTN2, {repeat:1,edge:"rising"}) + ]; + Bangle.swipeHandler = d => {cb(d);}; + Bangle.on("swipe", Bangle.swipeHandler); + Bangle.touchHandler = d => {cb();}; + Bangle.on("touch", Bangle.touchHandler); + } else if (mode=="clock") { + Bangle.CLOCK=1; + Bangle.btnWatches = [ + setWatch(Bangle.showLauncher, BTN2, {repeat:1,edge:"rising"}) + ]; + } else if (mode=="clockupdown") { + Bangle.CLOCK=1; + Bangle.btnWatches = [ + setWatch(function() { cb(-1); }, BTN1, {repeat:1,edge:"rising"}), + setWatch(function() { cb(1); }, BTN3, {repeat:1,edge:"rising"}), + setWatch(Bangle.showLauncher, BTN2, {repeat:1,edge:"rising"}) + ]; + } else if (mode=="custom") { + if (options.clock) { + Bangle.btnWatches = [ + setWatch(Bangle.showLauncher, BTN2, {repeat:1,edge:"rising"}) + ]; + } + } else + throw new Error("Unknown UI mode "+E.toJS(mode)); + if (options.clock) Bangle.CLOCK=1; + if (options.touch) { + Bangle.touchHandler = options.touch; + Bangle.on("touch", Bangle.touchHandler); + } + if (options.swipe) { + Bangle.swipeHandler = options.swipe; + Bangle.on("swipe", Bangle.swipeHandler); + } + if ((options.btn || options.btnRelease) && !Bangle.btnWatches) Bangle.btnWatches = []; + if (options.btn) Bangle.btnWatches.push( + setWatch(function() { options.btn(1); }, BTN1, {repeat:1,edge:"rising"}), + setWatch(function() { options.btn(2); }, BTN2, {repeat:1,edge:"rising"}), + setWatch(function() { options.btn(3); }, BTN3, {repeat:1,edge:"rising"}) + ); + if (options.btnRelease) Bangle.btnWatches.push( + setWatch(function() { options.btn(1); }, BTN1, {repeat:1,edge:"falling"}), + setWatch(function() { options.btn(2); }, BTN2, {repeat:1,edge:"falling"}), + setWatch(function() { options.btn(3); }, BTN3, {repeat:1,edge:"falling"}) + ); + if (options.remove) // handler for removing the UI (intervals/etc) + Bangle.uiRemove = options.remove; + if (options.redraw) // handler for redrawing the UI + Bangle.uiRedraw = options.redraw; + if (options.back) { + var touchHandler = (z) => { + if (z==1) options.back(); + }; + Bangle.on("touch", touchHandler); + var btnWatch; + if (Bangle.btnWatches===undefined) // only add back button handler if there's no existing watch on BTN1 + btnWatch = setWatch(function() { + btnWatch = undefined; + options.back(); + }, BTN3, {edge:"rising"}); + WIDGETS = Object.assign({back:{ + area:"tl", width:24, + draw:e=>g.reset().setColor("#f00").drawImage(atob("GBiBAAAYAAH/gAf/4A//8B//+D///D///H/P/n+H/n8P/n4f/vwAP/wAP34f/n8P/n+H/n/P/j///D///B//+A//8Af/4AH/gAAYAA=="),e.x,e.y), + remove:(noclear)=>{ + if (btnWatch) clearWatch(btnWatch); + Bangle.removeListener("touch", touchHandler); + if (!noclear) g.reset().clearRect({x:WIDGETS.back.x, y:WIDGETS.back.y, w:24,h:24}); + delete WIDGETS.back; + if (!noclear) Bangle.drawWidgets(); + } + }},global.WIDGETS); + if (redraw) Bangle.drawWidgets(); + } +}) diff --git a/apps/setuichange/boot.js b/apps/setuichange/boot-b2.js similarity index 100% rename from apps/setuichange/boot.js rename to apps/setuichange/boot-b2.js diff --git a/apps/setuichange/metadata.json b/apps/setuichange/metadata.json index c5aad6929..f21c784ec 100644 --- a/apps/setuichange/metadata.json +++ b/apps/setuichange/metadata.json @@ -1,13 +1,14 @@ { "id": "setuichange", "name": "SetUI Proposals preview", - "version":"0.03", + "version":"0.04", "description": "Try out potential future changes to `Bangle.setUI`. Makes hardware button interaction snappier. Makes it possible to set custom event handlers on any type/mode, not just `\"custom\"`. Please provide feedback - see `Read more...` below.", "icon": "app.png", "tags": "", "type": "bootloader", - "supports" : ["BANGLEJS2"], + "supports" : ["BANGLEJS","BANGLEJS2"], "readme": "README.md", "storage": [ - {"name":"setuichange.0.boot.js","url":"boot.js"} + {"name":"setuichange.0.boot.js","url":"boot-b1.js", "supports": ["BANGLEJS"]}, + {"name":"setuichange.0.boot.js","url":"boot-b2.js", "supports": ["BANGLEJS2"]} ] } diff --git a/apps/shadowclk/settings.js b/apps/shadowclk/settings.js index 1472cb099..183370082 100644 --- a/apps/shadowclk/settings.js +++ b/apps/shadowclk/settings.js @@ -155,4 +155,4 @@ } // Initially show the menu showMenu(); -}); +}) diff --git a/apps/shortcuts/settings.js b/apps/shortcuts/settings.js index 419b186d6..2c9ebd87a 100644 --- a/apps/shortcuts/settings.js +++ b/apps/shortcuts/settings.js @@ -62,5 +62,4 @@ "BTN3 app": () => showApps("BTN3") }; E.showMenu(mainMenu); -}); - \ No newline at end of file +}) diff --git a/apps/simplebgclock/settings.js b/apps/simplebgclock/settings.js index 8fd042b3b..17524ca8d 100644 --- a/apps/simplebgclock/settings.js +++ b/apps/simplebgclock/settings.js @@ -27,4 +27,4 @@ } } }); -}); +}) diff --git a/apps/slomoclock/settings.js b/apps/slomoclock/settings.js index dcaf0aff6..fd2717927 100644 --- a/apps/slomoclock/settings.js +++ b/apps/slomoclock/settings.js @@ -35,4 +35,4 @@ E.showMenu(appMenu); -}); +}) diff --git a/apps/slopeclockpp/settings.js b/apps/slopeclockpp/settings.js index 2c2d2c463..71c272753 100644 --- a/apps/slopeclockpp/settings.js +++ b/apps/slopeclockpp/settings.js @@ -57,4 +57,4 @@ showMainMenu(); -}); +}) diff --git a/apps/smclock/settings.js b/apps/smclock/settings.js index 30119e48d..3dff0a30c 100644 --- a/apps/smclock/settings.js +++ b/apps/smclock/settings.js @@ -1,8 +1,8 @@ -// settings menu for Monogram Watch Face -// Anton Clock settings were used as template -// helper functions taken from Anton Clock - (function (back) { + // settings menu for Monogram Watch Face + // Anton Clock settings were used as template + // helper functions taken from Anton Clock + var FILE = "smclock.json"; // load settings from the file // assign default values if it doesn't exist @@ -87,6 +87,4 @@ // Actually display the menu E.showMenu(mainmenu); -}); - -// end of file +}) diff --git a/apps/speedalt/settings.js b/apps/speedalt/settings.js index aeaa84f2c..a4e70c2c0 100644 --- a/apps/speedalt/settings.js +++ b/apps/speedalt/settings.js @@ -79,4 +79,4 @@ E.showMenu(appMenu); -}); +}) diff --git a/apps/speedalt2/settings.js b/apps/speedalt2/settings.js index 63fa424ba..d0e6c2fda 100644 --- a/apps/speedalt2/settings.js +++ b/apps/speedalt2/settings.js @@ -99,4 +99,4 @@ E.showMenu(appMenu); -}); +}) diff --git a/apps/sudoku/ChangeLog b/apps/sudoku/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/sudoku/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/sudoku/app-icon.js b/apps/sudoku/app-icon.js new file mode 100644 index 000000000..6762e0c1a --- /dev/null +++ b/apps/sudoku/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("l0vwQPM/4AI+E4ChEDwYgJhkwgGAgYHChED3cACwM8gHwBYXwn/wCwMLwELBQULgeACxXACwIPBCwotKAIIWIAAIWCn5EJgewFou4CwQpDAYhyCCQJWBgE7BwWDh6gICwgtDCwYtOF4QWIgf8CxMwL4gtFh7OGCwS0CCxABB4AWHnBbJIhc7LZI2DCw4AFwcDTIPw/6lD/gWB3YABhewAgW72CgQW6wWphELhYCBgYEBgYWC+AAHCwIQBAAwtEHgTOHAA+DB4QAGnk7TYYAFwCxCAAzVGAAo")) diff --git a/apps/sudoku/app.js b/apps/sudoku/app.js new file mode 100644 index 000000000..5a7f9cd1f --- /dev/null +++ b/apps/sudoku/app.js @@ -0,0 +1,163 @@ +var settings = require("Storage").readJSON("sudoku.json",1)||{}; + +const Layout = require("Layout"); +var puzzle = ""; // start puzzle space for blanks, 81 char str +var solution = ""; // the solution for the puzzle 81 char str +var current = ""; // current puzzle state, 81 char str +var sx=4, sy=4; // selected item +var dx=0, dy=0, dmoved=false; // current drag amount +const DRAG = 20; +const CHOOSERFONT = "12x20:2"; +const COL_GOOD = "#00F"; +const COL_BAD = "#F00"; + +function saveSettings() { + settings.puzzle = puzzle; + settings.solution = solution; + settings.current = current; + require("Storage").writeJSON("sudoku.json",settings); +} + +function startGame(difficulty) { + let file = require("Storage").read("sudoku.easy.txt"); + let games = 0|(file.length/163); + let game = Math.floor(Math.random()*games); + let line = file.substr(game*163, 162); + puzzle = line.substr(0,81).replaceAll("-"," "); + current = ""+puzzle; // new string + solution = line.substr(81,81); + showGrid(); +} + +function draw() { + g.clear(1); + for (var i=0;i<10;i++) { + g.setColor((i%3)?"#888":g.theme.fg); + g.fillRect(7+18*i, 7, 7+18*i, 169); + g.fillRect(7, 7+18*i, 169, 7+18*i); + } + i = 0; + g.setFont("6x8:2").setFontAlign(0,0); + for (var y=0;y<9;y++) + for (var x=0;x<9;x++) + g.setColor(puzzle[i]==" "?((solution[i]==current[i])?COL_GOOD:COL_BAD):g.theme.fg).drawString(current[i++], 16 + 18*x, 16 + 18*y); +} +function drawSq(x,y,sel) { + g.setColor(sel ? g.theme.bgH : g.theme.bg).fillRect(8+x*18,8+y*18,24+x*18,24+y*18); + g.setFont("6x8:2").setFontAlign(0,0).setColor(sel ? g.theme.fgH : g.theme.fg); + var i = x+y*9; + g.setColor(puzzle[i]==" "?((solution[i]==current[i])?COL_GOOD:COL_BAD):g.theme.fg).drawString(current[i++], 16 + 18*x, 16 + 18*y); +} + +// precalculate our layout +var choose = function(num) { + if (num!==undefined) { + Bangle.buzz(100); + var i = sx+sy*9; + current = current.substr(0,i)+num+current.substr(i+1); + } + showGrid(); + if (current == solution) { + saveSettings(); + E.showMessage(/*LANG*/"Well done,\nyou finished!", { + title:"Sudoku", + buttons : {"Ok":true}, + img : require("heatshrink").decompress(atob("k8pwcBkmSpICCnVp0IIFAQl27dt2wOJ9On7/582YtOJBws7tv/DoQKCBwubvvmzVp0wCCHAvf9u2DpE8uOn7waEjQdDq1Itv+DQQCB0gZBkMkyPHj1tHAuGAQOJktt/Ubvo4DAQI3ChMcuPHzd5HAeasICBtFOr/kw3fDQe27RhCofH/lx4xxFSQIFBiu/rNhHAp0DyHx447BHAh0Ekt/61YlodFOgaPBDoJWFAQeJrf1yySBDohWCoVJOgUcKw+YyVOrtly0LKw4eBp4dCjytHBwNOrNlyUbDoVpHAYDBo4dH02EFoTpBDoQ7B2MyZALMCHYsGtOOjALDAQQ7CyM6tFkFIgCDDoPBnkT4wLFAQegxP8z/JBxNwp8E+ALHAQMl+V/n4aIAQI4B4McBxU/z/P8gOKjnx4JHJAQP//5WKSoVwDRICBv//FJTRDBxeT/5WLkmR45WLkitLARNIA")) + }).then(showMenu); + } +}; +var numberChooser = new Layout( { + type:"v", c: [ + {type:"h", c: [ + {type:"btn", font:CHOOSERFONT, label:"1", fillx:1, cb:()=>choose(1) }, + {type:"btn", font:CHOOSERFONT, label:"2", fillx:1, cb:()=>choose(2) }, + {type:"btn", font:CHOOSERFONT, label:"3", fillx:1, cb:()=>choose(3) } + ]}, + {type:"h", c: [ + {type:"btn", font:CHOOSERFONT, label:"4", fillx:1, cb:()=>choose(4) }, + {type:"btn", font:CHOOSERFONT, label:"5", fillx:1, cb:()=>choose(5) }, + {type:"btn", font:CHOOSERFONT, label:"6", fillx:1, cb:()=>choose(6) } + ]}, + {type:"h", c: [ + {type:"btn", font:CHOOSERFONT, label:"7", fillx:1, cb:()=>choose(7) }, + {type:"btn", font:CHOOSERFONT, label:"8", fillx:1, cb:()=>choose(8) }, + {type:"btn", font:CHOOSERFONT, label:"9", fillx:1, cb:()=>choose(9) } + ]} + ], fillx:1 +}, {btns:[ {label:/*LANG*/"Back", cb: l=>choose()} ]}); + +function showGrid() { + Bangle.setUI({mode:"custom", drag:e=>{ + if (e.b) { + dx += e.dx; + dy += e.dy; + while (Math.abs(dx) >= DRAG) { + Bangle.buzz(40); + let old = sx; + sx = (sx+9+Math.sign(dx))%9; + dx -= Math.sign(dx)*DRAG; + drawSq(old,sy,0); + drawSq(sx,sy,1); + dmoved = true; + } + while (Math.abs(dy) >= DRAG) { + Bangle.buzz(40); + let old = sy; + sy = (sy+9+Math.sign(dy))%9; + dy -= Math.sign(dy)*DRAG; + drawSq(sx,old,0); + drawSq(sx,sy,1); + dmoved = true; + } + } else { + if (!dmoved && puzzle[sx+sy*9]==" ") { + Bangle.buzz(100); + setTimeout(showSelectNumber, 10); + } + dx = dy = 0; + dmoved = false; + } + }, btn:() => { + saveSettings(); + showMenu(); + }}); + draw(); + drawSq(sx,sy,1); +} + +function showSelectNumber() { + g.clear(); + numberChooser.setUI(); + numberChooser.render(); +} + +function showMenu() { + var menu = { "" : { title: "Sudoku" } }; + if (settings.puzzle) + menu["Resume Game"] = () => { + puzzle = settings.puzzle; + solution = settings.solution; + current = settings.current; + showGrid(); + }; + menu["New Game"] = () => { + E.showMenu({ "" : { title: /*LANG*/"Difficulty", back : showMenu }, + "Easy" : () => startGame("easy"), + "Medium" : () => startGame("medium"), + "Hard" : () => startGame("hard") + }); + }; + menu["Exit"] = () => load(); + E.showMenu(menu); +} + +if (settings.puzzle) { + puzzle = settings.puzzle; + solution = settings.solution; + current = settings.current; + showGrid(); +} else { + showMenu(); +} + +E.on("kill", saveSettings); // ensure we save the game diff --git a/apps/sudoku/app.png b/apps/sudoku/app.png new file mode 100644 index 000000000..ab5f449e8 Binary files /dev/null and b/apps/sudoku/app.png differ diff --git a/apps/sudoku/gen_sudoku.js b/apps/sudoku/gen_sudoku.js new file mode 100644 index 000000000..002c0a7d1 --- /dev/null +++ b/apps/sudoku/gen_sudoku.js @@ -0,0 +1,24 @@ +/* +Call this to generate the data files needed. + +npm install sudoku-gen + +*/ + +var getSudoku = require('sudoku-gen').getSudoku; + +// (easy, medium, hard, expert) +function gen(difficulty) { + console.log("Generate", difficulty); + // Get a sudoku of specific difficulty + let result = ""; + for (let i=0;i<200;i++) { + const sudoku = getSudoku('easy'); + result += sudoku.puzzle + sudoku.solution+"\n"; + } + require("fs").writeFileSync("sudoku."+difficulty+".txt", result); +} + +gen("easy"); +gen("medium"); +gen("hard"); diff --git a/apps/sudoku/metadata.json b/apps/sudoku/metadata.json new file mode 100644 index 000000000..63a534acf --- /dev/null +++ b/apps/sudoku/metadata.json @@ -0,0 +1,17 @@ +{ "id": "sudoku", + "name": "Sudoku", + "shortName":"Sudoku", + "version":"0.01", + "description": "A Sudoku game that allows you to play one of 600 pre-made puzzles", + "icon": "app.png", + "screenshots" : [ { "url":"screenshot.png" } ], + "tags": "game", + "supports" : ["BANGLEJS2"], + "storage": [ + {"name":"sudoku.app.js","url":"app.js"}, + {"name":"sudoku.img","url":"app-icon.js","evaluate":true}, + {"name":"sudoku.easy.txt","url":"sudoku.easy.txt"}, + {"name":"sudoku.medium.txt","url":"sudoku.medium.txt"}, + {"name":"sudoku.hard.txt","url":"sudoku.hard.txt"} + ] +} diff --git a/apps/sudoku/screenshot.png b/apps/sudoku/screenshot.png new file mode 100644 index 000000000..490a49cc1 Binary files /dev/null and b/apps/sudoku/screenshot.png differ diff --git a/apps/sudoku/sudoku.easy.txt b/apps/sudoku/sudoku.easy.txt new file mode 100644 index 000000000..5937edea3 --- /dev/null +++ b/apps/sudoku/sudoku.easy.txt @@ -0,0 +1,200 @@ +8----47---47----639-5--381-7--46-13219---5---4----2597--13-724--2--46--83-4-----1813694725247581963965273814758469132192735486436812597681357249529146378374928651 +6------1-----954-3--94-1----76-3-----18562-94-927-4638--4-5----9--2--1-62-3----47645378219821695473739421865476839521318562794592714638164957382987243156253186947 +9-6-----3-186239-----945---3-521--8-4--3---1---189---46--57-1---7--38-9----16-47-946781523518623947237945861365214789489357216721896354694572138172438695853169472 +---4-71---895-27637128-3-495-7-8--3--94-31--------96---5--7---------8-959-1---8-6635497128489512763712863549567284931894631257123759684358976412276148395941325876 +4-5----97-92-4-------6--2--7---68----89-14763651-324-9---17-9-8-1-4---265---9----465321897192847635873659214734968152289514763651732489326175948917483526548296371 +1---23986------154--819-----3-4--8---2-85-6-3--4-3-2-147----3-93--9--5-7-1---7-28147523986293678154568194732639412875721859643854736291476285319382941567915367428 +--6-25837-2--891-6-3-74-5-947-21---5---97--8------4---59-8-23-----4--9-2---5974-8946125837725389146138746529473218695651973284289654713594862371867431952312597468 +--5---61-4---1-------6-9-25-5349786--84-----9-17-562345--1-43----6-----23-9-68---895742613462315978731689425253497861684231759917856234578124396146973582329568147 +3--16-9--------32794-7-5---6-9-2---18-1---459--489-6-27-628319--------68---6-92-3387162945165948327942735816639524781821376459574891632756283194293417568418659273 +------1---2---3----8---6-4564--3-28-2938-4--68--6-7493---9--5--358---9729-4--2618436785129529143867781296345647539281293814756815627493162978534358461972974352618 +-96--1-521354-6-9---------6974182-632--597--1---6--7--5192---7-3--7-----6-7--4-35496871352135426897728359146974182563263597481851643729519238674342765918687914235 +-9127----------562-5-4-89--43----1791--3-98-698---6--4---89-6-582-65349--------83691275348748931562253468917436582179172349856985716234314897625827653491569124783 +58----24-----1--8-3-72--9--6435-8-2989-132-67----4--35-7----3-----8-7-9-4-8-91---589763241264915783317284956643578129895132467721649835972456318136827594458391672 +2--4-3-756--5----1----6129-3--74----8--6--143-961--82796--8-5-----2-67--1-2--7-68218493675649572381753861294321748956875629143496135827967384512584216739132957468 +---5--3-86---34-5235--27--6-49----3--6-31--4----47-28-8-67-391413----7--9----286-492561378687934152351827496749285631268319547513476289826753914134698725975142863 +76---9--5142--83--98--31-7--29-7-5-157---2-6-8-1---7--4--187--3----461-92-8-----7763429815142758396985631472629874531574312968831965724496187253357246189218593647 +--476-53-9-6-3-4-1-----9------18------295-7-359-473--21-9-2-3-47---9-1-646351---7284761539976835421315249678637182945842956713591473862159627384728394156463518297 +-3--9-----29-46--81-82-75-6--4---3---15--2---9---5-461-9--75684--1--49-7-57--921-536891742729546138148237596674918325315462879982753461293175684861324957457689213 +-863--2-9--17--6--34-----718-----3-76--47--5--751-3--81295--7-----9--82-538-471--786315249951724683342869571894652317613478952275193468129586734467931825538247196 +--13-98--7---21--4----471-692--543---4--38-6-6---9-4-----286---87-----9--34975--8451369827768521934392847156927654381145738269683192475519286743876413592234975618 +1--93-46-3-4--1----9---7-83--61493-8-21----5---3526-9-63--84-----76---3-5-271--4-178932465364851279295467183756149328921378654843526791639284517417695832582713946 +-1-7-8--5--4-65-2--59-42---9---714---7--3468--4--8--9----619---8-----21---18237-4612798345784365921359142876968271453175934682243586197427619538836457219591823764 +9132-5--4874----------439---21-98-5--45--27----9-7--2--68-3---259-6---4--32----67913285674874916235256743918721398456345162789689574321168437592597621843432859167 +-8---63--3---42--8-2--816-7--8-975---4-26--9--93-58------734-----4615-826-----4-5581976324376542918429381657268197543745263891193458276852734169934615782617829435 +81-64-379----89------7-348-69-524---48---6--75--8-7-94-45---21----371----3-4---68812645379374289156956713482697524831483196527521837694745968213268371945139452768 +9--8--4-5----641-94-6-95---713-42----4--8-25-2------3-1-2--85473974----65--726---921873465835264179476195382713542698649387251258619734162938547397451826584726913 +2-4--5---75-48---99--73--54-273-8----8-6-4-9--4----18--92-7---1--3----46861-439-2234965718756481329918732654127398465385614297649257183492876531573129846861543972 +2---------95-86--3--87-32-5-268-9--1-74------8391--62--679--358--25-79----13-87-2213495876795286143648713295526879431174632589839154627467921358382567914951348762 +--6-----2-2-6-791-57--286-4-9-7563-8--78--56---------9--34-2---1-5369--7--258-4-3316945872824637915579128634491756328237894561658213749983472156145369287762581493 +-3--27--52----6---496----233-1----788--73-5-----89--316841--3--9-2473-5-7-----412138927645275346189496518723341265978829731564567894231684152397912473856753689412 +-617--52-5-74--6--48325--7---9---4-----1--26-634-2---7----7--9-8--34-71-3-25-18-6961783524527419638483256971219867453758134269634925187145678392896342715372591846 +98-----71-1---3---5---9-42--27--8----64932-15-9517-2682-------4-5-41-------3-518-983246571412753896576891423127568349864932715395174268231687954758419632649325187 +-39-2--16-8--19---164-83---41-3-----79---6-48--8------3-1-75-9-9-62345--84--61-2-539427816287619453164583972412398765793156248658742139321875694976234581845961327 +1-8--6---526-73--4-93--18-62345--6-9--9--432---------591-43-2-84-21--57------2--1148956732526873194793241856234587619859614327671329485917435268462198573385762941 +----96--8923------6854-79--7-9--4--38---3--4-5-4-82-7-4-6---31-2-1-6-4---781---9-147396258923851764685427931769514823812739546534682179496278315251963487378145692 +---264-----1---52-2--5713-8-2--13--969-85----8--49--5---632-8---3-78-41--8-1---6-358264971471938526269571348524613789697852134813497652146325897932786415785149263 +------437--524----4--1-9258-9--7-5---1-35-8-9--79--1-476----9-29---2-3-6-4---6-15129685437385247691476139258892471563614352879537968124768513942951724386243896715 +-3-8--47-91537------7---9----6--8-34---23--162-364----569--3--27-1-8-345--4725---632859471915374628847162953176598234498237516253641897569413782721986345384725169 +-24--13-5--7--84--16-----87-891-7--24---86-9-2-----1-891286-7-------523-735--98--824971365597638421163542987389157642471286593256394178912863754648715239735429816 +248-1--65--152---9-9548---3-------579-6-3-481-3-14--96-8-6-4-12------9---2985---4248319765371526849695487123412968357956732481837145296783694512564271938129853674 +57-6321----3--5-8-4-6---3-2---3----1---9---3-13--84-9676-24---8--186-27-2-915---3578632149923415687416798352894376521657921834132584796765243918341869275289157463 +4-258-7----3---------2635----9---4-26-7--81-55--1-28---7-621-84-1--9-25-24--356-1462589713853417926791263548189356472627948135534172869975621384316894257248735691 +679-8-3-5-14-----92-549---1----7------25-6--856892--7-12-64-58--867-3-1-----1---6679182345814365729235497861391874652742536198568921473123649587986753214457218936 +-2--6-------9-34-5--4----96--9---5---4-6-2-1--13-97---4-1238-797-2---3--6-8-49251925164738186973425374825196269381547847652913513497862451238679792516384638749251 +4-5----6-2-1-397488-94265-33------7--9-14-8--68--53----4---1---9-----3-1---36--97435718269261539748879426513314682975592147836687953124743891652956274381128365497 +7-4---85-----6874---8-45--1--32------4-53-1--269---43-982--7--43-6459-1---5---379794123856521968743638745921153294687847536192269871435982317564376459218415682379 +--8---9729-6428-5-231-7---47-4---38-----3674---3-84--5162---49--4-8-95----91-----458613972976428153231975864724591386815236749693784215162357498347869521589142637 +--46-19-5-5-------2-9-481--5-84-23--6-7------1423---58--31-45-6--59-6--28-62--491784631925351729864269548173598462317637815249142397658923174586415986732876253491 +82-43---9-4--6----7-39-1-58---2---6-3--51-----5---482157--4-9824--75-13-2--1---47825437619149865273763921458914278365382516794657394821571643982498752136236189547 +------75937-2-----8-4--7321-9--4-2--52--8-1-44--9--8-7--6-7--82-3---45-6----694-3612438759379215648854697321798341265523786194461952837146573982937824516285169473 +3--25---7------6137-4-19---27-6---5-85----974-4-78--62----27-36------28-12-8637-5318256497592478613764319528273694851856132974941785362485927136637541289129863745 +6---8-3-519----2--3-529-64197--28-5-5---49-87---7--93----4-28-3-5-9-1--4-46-----9627184395194536278385297641971328456532649187468715932719452863853961724246873519 +5-------8-1-49--3-23--65----9---4------52-8-11-----45-3-197256-9-6-----27-4-51389549237618617498235238165947895314726473526891162789453381972564956843172724651389 +--9--2-84-6-5------72---6-192--6174383-4259-614-7-------4---8---9--86------95--67319672584468519372572348691925861743837425916146793258654137829791286435283954167 +-1------279---8-----69--74186-1-45--2--6------57-834-993---6-78-7---13-66--3-9154314765982792418635586932741863194527249657813157283469931546278475821396628379154 +9--8317------946-82-3---1--51---2-4--26-1-8-53-8-----114---65--892--3--763--78-1-964831752751294638283567194519782346426319875378645921147926583892153467635478219 +--8----7----3-------6-15-4-4-186-7--879--4-166--197-2491-436-8-765928------5----9598642173147389652236715948421863795879254316653197824912436587765928431384571269 +674----92-9-3-2-1---26-------8-391------87-595-9----83486-5-9--2-7943--1--3---425674815392895372614132694578728539146361487259549126783486251937257943861913768425 +---8-37--687--41---45---2-6---6-8--7--3-79-5--7-23-6-9-16-82-7---8346--1--2-579--921863745687524193345791286259618437163479852874235619516982374798346521432157968 +--4-9-57---1458-62-5273-8-418-6--4-7-7-5-46--9-----2-53--------52--671-----3857--834296571791458362652731894185629437273514689946873215367142958528967143419385726 +6-8---93--------469436-57-8---7-418248-1--69--9-8-6-7-76---38--5-2-18---------359628471935175389246943625718356794182487152693291836574769543821532918467814267359 +-6--7-5-89----523-51----7-66---1---23--82-6-4--16--9-3-9-3-6427--279----------189263179548978465231514238796647913852359827614821654973195386427482791365736542189 +4-158-9-------6----294--5-6-3-894-61---72-----4816-3---943--6-2-526----8-8-21-459461582973875936124329471586537894261916723845248165397794358612152649738683217459 +61-5-9------6--2---83--7-9-76-43-829---81-5--54897--163----8------7---63-56---7-2617529438495683271283147695761435829932816547548972316379268154824751963156394782 +2--3-4-15-1--8-32----2--6-478-4-5---624--97-------21-6-6-9--47194--2-56--5---328-276394815419586327835217694781465932624139758593872146362958471948721563157643289 +-68--9153--5-------438---2-3--15---9---9-86349-4-37815-5-78---2-----6-8-8125--7--268479153795312468143865927386154279571928634924637815659781342437296581812543796 +39-----57-26--741---4--39---1--7983-8--3-5--4-37-----1--953-1786-1--2-----3--8269398421657526987413174653982415279836862315794937864521249536178681792345753148269 +--28-3145-517--8-9-849--2----725139----6-8----253-9--7-185--9-4----9----53-4-2--1972863145651724839384915276467251398193678452825349617718536924246197583539482761 +2-3--1----89-2-613------4723---8-5-7-5-2--19----54-3-8--8--42-91-79--8-64--8----1243671985789425613615398472324189567856237194971546328568714239137952846492863751 +-4---1-2-31--28-9-2---694---7--53--2--591--6-----72-54---346---9-2187--676----1--649531728317428695258769431476853912825914367193672854581346279932187546764295183 +-7-4-9------37-9-61------4--15-6-----24813-79-879-5162-9--3----7----84-186-----95576489213248371956139256847915762384624813579387945162491537628752698431863124795 +39742-5-81-----6-3-648132-7-7-3-5-2----64--719-------4-45-----22-91-4-------5--3-397426518182579643564813297471395826853642971926781354745938162239164785618257439 +-3-689-2---54-2-631--5-7-4--5-26------71-5-8-61--48--546-----795--82----3--9--652734689521895412763126537948958263417247195386613748295462351879579826134381974652 +4-2-583-656--2--97---6--2--658-7-4-1-4-5---68------7--7168-95--23-1------842--1-3492758316561324897873691254658973421347512968129486735716839542235147689984265173 +2--68-5--17-23--8--5--7--2---97-8-6--4-91---2---42--958-2347--6---561---46----7--293684517174235689658179423329758164546913872781426395812347956937561248465892731 +9-68--4-5-----6---4--93-28-6-57--8-4--36--5-989452---3---15-----62348--77--26-3-8936812475258476931417935286625793814173684529894521763389157642562348197741269358 +-8----9-5456-237-13-95812-462-3-8-------7--5-7-3-----2--47-5-2--6------3----39-48281647935456923781379581264625398417198472356743156892834765129962814573517239648 +8-67-194337----1--9--2--8-6----142-8-6--37--4-49-----775-12--6-6--47--25-----578-826751943374896152915243876537614298268937514149582637753128469681479325492365781 +4-27-869--39-41-8--1--5---------35----4-76----6-1--379--3--712--26-1-938--1-624-7452738691639241785817659243178923564394576812265184379543897126726415938981362457 +-----95-29-2--5-368---461793-91--768-76--8--31---------415--8-75----7---2-786-35-764319582912785436835246179359124768476958213128673945641532897583497621297861354 +-2--79-5-7--1-------9--5762-4--563--3817-4---65793-4-8--------55364---172----7-83823679154765142839419385762942856371381724596657931428178263945536498217294517683 +2---75-968--9-4-13--16-8274---5-----75--83-6-----974------5--8996-84---2---769-45234175896876924513591638274489516327752483961613297458147352689965841732328769145 +6-75-8---8---9--4--34--2658---2--8------8-1---8261--791-58--2-42---359-6-639--5-7627548391851396742934172658719254863346789125582613479195867234278435916463921587 +--3--85-18-1--49-2259-613---862934--4---862-3----17------8-----9---35-265-8--21-9743928561861354972259761384186293457497586213325417698612849735974135826538672149 +---256----6-78491-7-----4-6--1--7--55---691----9-182-7--697--3--1--32--4-53-41---194256378365784912728193456481327695572469183639518247246975831917832564853641729 +31----97--7--8----4--3--6-5-4-7-5-------481-76------5--349-7216-2586374--69-1----318526974576489321492371685941735862253648197687192453834957216125863749769214538 +-4----372-1-9-346------2----547-1--6-6-4--5-3-93--8---6-9-1-73543--5--8--7539--24946185372512973468387642951254731896768429513193568247629814735431257689875396124 +6--18973-9-8---5-1-4--7---9-9-2------21-5469---69-----83-4-1--6--96-78-2--48-531-652189734978342561143576289495268173321754698786913425837421956519637842264895317 +---2-76-8------37-1-7638-257-2-8-5--3-5---249--9-238-76---75--229-4-1---------186953247618826159374147638925762984531385716249419523867631875492298461753574392186 +---6-814-1---53-296---9---8----162--46-7--9--8-1--2-67-46-8-7123--52----7---6-853293678145187453629654291378975816234462735981831942567546389712318527496729164853 +-534-8-7--94-26-1-1829-73--8---42-----9-75-26---6------1-78--49-68294----47-6----653418972794326815182957364876142593439875126521639487215783649368294751947561238 +15---84---3-4---8-89-3-6-5-78-----2426-7----83-5-2--1-416---------1-73--973-85--1157298463632451789894376152781563924269714538345829617416932875528147396973685241 +-916-25--3--49--1-5-----9-224----69---69----3-532--7-86871----9125-49--6---7---85891672534362495817574318962248537691716984253953261748687153429125849376439726185 +2-759-----3------4---3-7-9-4-3--6--917-----26----5--7-8247-196-----2-14-79-64538-217594638936812754548367291453276819179483526682159473824731965365928147791645382 +691-8-4----7-----1---2---6815983--4-3-41----6-624---835--91--24----4--7-9-83-26-5691783452827564931435291768159836247384127596762459183576918324213645879948372615 +35---29--62--7-154-1---------51-4-7-----27536-3758-412----6-2--492--1--81--2-8-9-354612987629873154718495623265134879841927536937586412583769241492351768176248395 +---8-6-4--8----3--1-654----96----21-----5--6-3-8--24--7136-9-2464-235-78----1--39537826941482791356196543782964378215271954863358162497713689524649235178825417639 +437---56859--8-123---5--7--3--16-95484539---11-9--483-------2----3-1--97--8-4----437921568596487123281536749372168954845392671169754832914673285623815497758249316 +--4267-5--7--5-24-2-------96523-18-4819----32-3-8--56-7---18-2-586-92--3-----5---394267158178953246265184379652371894819546732437829561743618925586792413921435687 +7418-356-----4-78--6397512--2------73-4-69------3-26------9-3--2-75----6-38---4-5741823569952146783863975124526481937384769251179352648415698372297534816638217495 +--26-13-884-5-91----97-8-54-2---3-----1--2----3597--212-8---5-7-9--6-2--1--285-46752641398846539172319728654927813465681452739435976821268394517594167283173285946 +8-23---19--1--7-637--1----8-5-6--38----57--414---1--52------276-13-6-8946-4--8---842356719591287463736194528159642387328579641467813952985431276213765894674928135 +------91687--21---9--5-38--4-3---782--78-456-5-86---3--------45----8569-1-54963-8352748916876921453941563827463159782217834569598672134689317245734285691125496378 +--9-18-5----3----8587-9-1---2--79-4----84-36264-1-28978-34--7292-6-8---59--------439718256162354978587296134328679541791845362645132897853461729276983415914527683 +--9-----4---465219-2--39678-18--6593-369528--5--3-8-62-5-2-------------7-8-6--43-169827354873465219425139678218746593736952841594318762357294186641583927982671435 +8-6---5-9-94------25--196489-8--3-54375-42-----2-95-8-5---6--92---35-7-1681------816234579794586123253719648968173254375842916142695387537461892429358761681927435 +---1-459---927--8-3-----4---74---8--83-54---------7-2-2934-86-75-----2-1-416259-8627184593459273186318956472974362815832541769165897324293418657586739241741625938 +-7-1-42--5--98--1-13-2-7-----9---5-2-5-692-78---345-----35-8--77----93--8--7-69-4978164253524983716136257849649871532351692478287345691493528167762419385815736924 +---375---81-649-5-9-5-----63-6-148----7--61--1---58--7--9-23-1-----9127--2-86-5--264375981813649752975182436396714825587236149142958367759423618638591274421867593 +94---16-33-1--6---625-897--4----7-29------5--7925--4-6-----21---1479-3-22-71---85948271653371456298625389741453617829186924537792538416539862174814795362267143985 +--6-957-1--2-8134--5-2746-8--------33-4--896----7--41-----37184----16-3-8-1-2--76486395721972681345153274698617942853324158967598763412269537184745816239831429576 +6-3---4--2--7-3--97-9--21-51--92-3-6------8--9--358---5-7-89-63-26537-1-39--4--7-613895427254713689789462135178924356435671892962358741547189263826537914391246578 +----78-598--961-4-2-1----6-51--946--67--5--3-982-1---4-25--6-9336--2-7--1-9-----6436278159857961342291345867513794628674852931982613574725186493368429715149537286 +3952----88-7--15-4--48-------25389-----7---8-1839624-773---92-5------7--5491-7--3395246178867391524214875639472538916956714382183962457731689245628453791549127863 +--915-8726-2--3-----1--896319----75---4--1--9-36--7-24-17---2--8---154---2-7-9-18349156872682973541751428963198342756274561389536897124917684235863215497425739618 +869-3175--45-792182---48------9-254--9-7---836----5----53--7---7-6----25---8--3--869231754345679218217548936138962547592714683674385192953427861786193425421856379 +---3-85--49----72-352--41--5--78-26--8--56--9---2-3-5--7--956---3-842-1-12--37--5716328594498561723352974186543789261281456379967213458874195632635842917129637845 +2-9---5-715--9---------3-1-42318--9556-94-378--763------2-5--------74-56-4---91-3239418567154796832876523419423187695561942378987635241692351784318274956745869123 +395---4-1-4----5-881-95-2---53----1--8743---94---------34-2-9-59-8--61235-1-8-7-6395278461742163598816954237253697814187432659469815372634721985978546123521389746 +631----5--4-57162---7--319837-2-8-----47-5-8-85----37-----9--6-4--65---776----912631829754948571623527463198376218549194735286852946371283197465419652837765384912 +---9-1-------68-945-942-1689-4--26--1-2734-----3-9641----685---43-----578---4-92-648971235321568794579423168984152673162734589753896412297685341436219857815347926 +37--98--5---1-----5-2--71-8--9-325872-8--19--7-5--42-1--475931-----26---9-7-13--4371298465896145723542367198419632587238571946765984231684759312153426879927813654 +-6-7-----8-1-6-7-972--93-6545--8-9373-79--5---8----------4--2-624-6--35--9-3-1478569714823831265749724893165456182937317946582982537614173458296248679351695321478 +--98-15--7-----6-82-45--1-98-6-592--3-----------3489---2-184-56-1--7-89--6893-4-1639821547751493628284567139846759213395216784172348965927184356413675892568932471 +6-3-98-7-7-1-6---2954-3-8--4--973--8----149-653------7-65--72-92-7-5--1-39----7--623198475781465392954732861416973528872514936539286147165347289247859613398621754 +-9146--7-7-6----4-3542---18---3-------9-15-2-51294-3--97-65-2-12-5-387-----7---5-891463572726581943354279618487326195639815427512947386978654231245138769163792854 +5----6---743-9--26619---538----8-9---25-7-3--4----------7-23689268-17--393-8--2-7582136794743598126619742538371284965825679341496351872157423689268917453934865217 +26-4-1-59-5--371-6--15------7-9-8621---7-------6-129----58-429---8---4-53-26--817267481359954237186831569742473958621129746538586312974615874293798123465342695817 +75829-1-4-6----5-39-36157-2--7-54-2--8----9-----93-67----4---5-82--69---4-9---2--758293164261748593943615782697854321382176945514932678136427859825369417479581236 +625-1-39-381---4577--3-----93468-5----659-14315---46-9-97-6---5----4---12--------625417398381926457749358216934681572876592143152734689497163825568249731213875964 +47-2-89---92-6--7-8-39-------1--5398---8-416-3-7--9----69--2-4-7185---3--34-9-58-476258913192463875853971624641725398925834167387619452569382741718546239234197586 +6-7-4-8---5-------18-5--467-2--7----5-92--7-473-4-6-28-4--69175----1-3-231--2-68-697142853453687291182593467824971536569238714731456928248369175976815342315724689 +-49--2-5--6-----138516-394---3---5-8--831---9-2486----2-7-38-9-43-7-----9---26-37349182756762954813851673942193247568678315429524869371217538694436791285985426137 +---7--9288--3596--9761-2--4---6-8-5751-----8676-5--4----32------27-6--4----913-62351746928842359671976182534234698157519437286768521493693274815127865349485913762 +-17432-8----856---8-4-----25--2--1----178---52-631-7-----14-95--9--278--4--69--1-617432589329856471854971362578269134931784625246315798762148953193527846485693217 +-9------38--5431--1-38---4-5-8--2-6--12-7638563----972-6-93-528----8----3--72---4496217853827543196153869247578392461912476385634158972761934528245681739389725614 +-9-523-611---4-------719438--839-6-236---218927-168--3----5-----8----9---2-6-43--894523761137846295652719438418395672365472189279168543743951826586237914921684357 +4---5----6-9---34---79--2-897-3-416223--6----18-52947---2----8-----756-47--4-8---428753916659812347317946258975384162234167895186529473542631789891275634763498521 +---8--3---6-23--7-37----49825----7-171--95-----6-725--134---2--6--7249-3--7--1854591847326468239175372516498259683741713495682846172539134958267685724913927361854 +17-269-38------6-1---1-892-81--2-3--36----8545--68-21--9-31--8-------792-857-4---174269538928537641653148927819425376362971854547683219796312485431856792285794163 +53--467-----1-----7-8-3-4-1--6-853478-4-1---63-7-2-1-8--2367-15----98---6-3-512--531846792469172583728539461216985347854713926397624158942367815175298634683451279 +--274-6-----29--343-4----27--7---8131-9874--68253--4--598----41-4--17-6---1--5---912743685756298134384156927467529813139874256825361479598632741243917568671485392 +---7-39-29376---45-5--8-7--7----6---1---7-----28-19-763-2--8-946-5--71-38-934-6--481753962937621845256984731794536218163872459528419376372168594645297183819345627 +7648--53--5--9---4---4-61-74----8---2---4-----19-2784-9-763---88-5--46-26-1--937-764812539158793264392456187473568921286941753519327846927635418835174692641289375 +---3----6746---21339--2-4588-9--7-4227349-8--4--81-739--4-8-69---2-7------------5528341976746958213391726458819637542273495861465812739154283697632579184987164325 +--5---9-18--7-9--36-4--38-71-983---4--2---------962--8-7--5--8991--287-6-4-69713-735286941821749563694513827169835274382174695457962318276351489913428756548697132 +8--5-6-27-7-8-14594--39--16---96-5-------3---9-328---16-1-58-4-----3--68---619-35819546327376821459452397816127964583568173294943285671631758942795432168284619735 +18----62-3-56---49--98----14--1-6-878-6---4---7-28-9---543-------1-28764--87--153187493625325617849649852371492136587816975432573284916754361298931528764268749153 +---736----7-82154---1---8-7--357-4--4--1----35--24-6-1-4-69---87---15-9-93-48----854736912379821546261954837613578429428169753597243681145692378786315294932487165 +6---384-2----2-7--8-54-1-96983-5--2------49-5--7-----3368-152--1-2--3--9-94--25-1679538412431629758825471396983157624216384975547296183368915247152743869794862531 +-35-842-----2--8-38-96---4----39-58---8--5--6--3-261-7--4571--8--1----7298-432--5135784269476219853829653741612397584798145326543826197264571938351968472987432615 +---3-----2-4--75-3-71-854--9--748-31----26---7-8-319--4-7--93-28---127545-2--3--8685394127294167583371285469956748231143926875728531946467859312839612754512473698 +783-16-2---4593--7----8-643-15---47-----7458--78-5---29---6----86-7--2-----13976-783416925624593817159287643315928476296374581478651392937862154861745239542139768 +27---1---46-83259-35-79-142--2---6------85-195--96----1-3---97---53---269----8---279451368461832597358796142892147653634285719517963284183624975745319826926578431 +-4-8-5--16------2-----29-74564-1283--9257314-7-----95-----8---51-62-7----28----1-247865391619734528385129674564912837892573146731648952973481265156297483428356719 +8-6--4--21-3--94-8--2-78163--9132-472-1-479------85---71--263-----4-----3-8--16-4876314592153269478942578163569132847281647935437985216714826359625493781398751624 +---524----5-36197-1-----3-5-2873------5-19-8--7-48---32--95-7----967-4-1--71----2793524816852361974146897325628735149435219687971486253214953768589672431367148592 +-2968--5--4-2---8---857-4---1-93---83---25-7----81--34-71---2-----749---8-5162--7129684753547293186638571492714936528386425971952817634471358269263749815895162347 +34--789-17-1-4--62---1--4--187-2-3-5--37---18------2---384--5-94-95-----2158-67--346278951751943862892165473187629345923754618564381297638417529479532186215896734 +-4------9--765--8--18-34---35------178-52143-62--47895--5--6------41-9-7-7----64-546182379237659184918734562354968721789521436621347895495876213862413957173295648 +9---8-237-5-4291-6281-----4--51-4-9--19---762-----7--15--94---3-9863-----43---8-9964581237357429186281376954725164398419853762836297541572948613198632475643715829 +95---386-6-3--8---8741-92--297-4-58-------4--5----2-97-3592-67-7-2-3--41-----73--951273864623458719874169235297641583318795426546382197135924678782536941469817352 +71-4-5-------72-4---2---8--831-67-94-74859-32---1---86-67---91-28--9-4-----5---7-716485329358972641942613857831267594674859132529134786467328915285791463193546278 +--------21--4--87-478-2--615-36-----6827-9--471-3---568-4-3-92----8----3-31-47-85396178542125463879478925361543612798682759134719384256864531927257896413931247685 +8937514-67--982--1-----4-9--841--2-7--------41624-38--6----9---218--79--4-93--6-2893751426746982531521634798384195267975826314162473859637249185218567943459318672 +5341-9--2-684-----7-16--8-4--------3--72--15-152-3-4-767--215-8-25-6-39----5----6534189672268473915791652834946715283387246159152938467679321548425867391813594726 +---5-3-12398--------2-64--89-6-271--251---4-74---9-2-6-248796-376-------8-92-6---647583912398712564512964378986427135251638497473195286124879653765341829839256741 +1-8-34---------71269--2---3-7-39--6-53-4---79---65-834------95-9-3----2775291-3-6128734695345869712697125483874392561536481279219657834461273958983546127752918346 +-3-68-1-7--72196--5--43-8--7---2-3168-1---4-23---96-----3-61----4-35-9--1-59-8--3932685147487219635516437829759824316861573492324196758293761584648352971175948263 +---4-2-36491--------658---1-659718-478-------1-98-6---5---9-6-8623---5-79-876-3--857412936491637285236589471365971824782354169149826753574293618623148597918765342 +-236------5------98---2-315-3-5--7-8-728---36--827-541-8615-4----9-8----34-7-61-2923615874157438269864927315431569728572841936698273541786152493219384657345796182 +---1238-7-27-493---4--8-2-9---465---1-6----32-98--2--5---97----5742-89-62-9-54---965123847827549361341786259732465198156897432498312675683971524574238916219654783 +291-6-53-346---1877----3-----2-51863538-42-1-61-8---25-57-2---1----8---69--------291768534346295187785413692472951863538642719619837425857326941124589376963174258 +--4---5811-3745-6-529--87---7-41---6--19-----935----178-7----42----32-78--2-746--764329581183745269529168734278413956641957823935286417857691342496532178312874695 +---8---5-4--915--21562348-7-18--29-4--------82796-81--9214--5--8-5--67-97--5-----392867451487915362156234897618352974543791628279648135921473586835126749764589213 +97-5-36148342-157---69-4-------4-2---85----6772---5-----8--7-------3679-3---5--42972583614834261579156974328619748253485312967723695481598427136241836795367159842 +--96-7---4-1---53---2--56949--4-6----3--98-7-8-417-9---9--6124-2--754-6---8-39-1-359647128461982537782315694975426381136598472824173956597861243213754869648239715 +4----69-5-92--3----3-49--2898--52-----68----9-1763---27-3-----1--832459---9178--4471286935892513476635497128984752613326841759517639842743965281168324597259178364 +----3-8---9--68-4-84----71365----4-242-5-1-----96-45----42--357287---6--9--7461-8512437869793168245846952713651893472428571936379624581164289357287315694935746128 +17----8---98-51------7---6-4---67-8----12-54---9---1--94681-3-7--5---6-221-5364-8174693825698251734532784961451967283763128549829345176946812357385479612217536498 +75-34----------9239--1-87----56-78-28-7--2--16-1---547---87-2-9-------863-829617-752349618184765923963128754435617892897452361621983547516874239279531486348296175 +-94-28--5--2-6---9-15--96------172--2479-5-1-861-------79----63-38-7--9-52-3----1694728135382561749715439628953617284247985316861243957179852463438176592526394871 +2-4--1-383----------85-3146--92--481---9--6--4-6--539--9-3-----6-31728591--4892--264791538315648972978523146539267481781934625426815397892356714643172859157489263 +4----97---18----9476---85-329-8-4--6--7-91-2---6---8-9354--29-------365-68291-4--423159768518376294769248513295834176847691325136527849354762981971483652682915437 +-----6-7---1732--85731486-9382--17----9--7---7-65--9-2--------663-8--2-12986-53--824956173961732458573148629382491765159267834746583912417329586635874291298615347 +-7-5---46-9-2-----------3--71---569856-892-7---96-72-5-2--687538-----4-----459821278531946396284517145976382712345698563892174489617235924168753851723469637459821 +--2--413--54---8-9-9--8-4-75--9--2-3--371-9-6--9-5---1------5721--82-----2-3-9618782694135654137829391582467517946283243718956869253741938461572176825394425379618 +2-65984-19-7-3----5-14-7293-1---596-35----7-44---8----1--6-4------81-34--9------6236598471947132685581467293712345968358926714469781532173654829625819347894273156 +25-3--6-78--4----2-93---84-14-8-35----294--1---5---4-3---6---756781----4531-94--8254381697816479352793526841147863529382945716965217483429638175678152934531794268 +-274359-----198---9-3-----53--68-2-----32-16--6-5-7-9---297---15-824--7-1---5--2-827435916456198732913762845391684257785329164264517398642973581538241679179856423 +-76-8934194----8561-3--69--4--86---2----3-----9-54-6137-43--2---5------43--124-7-576289341942713856183456927435861792621937485897542613714395268259678134368124579 +1792-85-----95---7435------5-28----48-137--2---7-4--8-9-8---46-6-3-9-8--72---6-5-179238546286954317435617298592861734841379625367542189958723461613495872724186953 +76-2-35---2-196--7-3-8-5-4-----62--5-9-5-48--5--93--64256--1-7-18-----36----29-5-768243591425196387931875642843762915697514823512938764256381479189457236374629158 +31927-5---58--32-92--59-83169--2---5----3---8--4------871---356542-8-19---61-----319278564458613279267594831693821745725439618184765923871942356542386197936157482 +136---------6-82--298-57-6---23----5-592-1--7-67--53---418---5-72--4---6-85----34136924578574638219298157463412376895359281647867495321941863752723549186685712934 +---975--19-812---35----------14-9--23-7--21-46-----9-8-89-514-7-4--6--19-3-79482-463975281978126543512843796851439672397682154624517938289351467745268319136794825 +43-65-182-1-7--34--86---5------2573---4-68-2-1-2----6-----9-6-3-4-2-6-97-695-7--4437659182915782346286341579698425731374168925152973468721894653543216897869537214 +74516-3-2--6----14-329----5-29-76----7--14-3--1----57-2-1-8----98-7-1--33--6-9-81745168392896235714132947865529376148678514239413892576261483957984751623357629481 +--56-3-42--72----1----7159----5-74---79-8-2--5-1--4-78--87--163--346----79-1--854815693742967245381324871596682517439479386215531924678248759163153468927796132854 +4632--1-----3--94-529-716---9----5-1-8-71--2-1-26-5--975-----16--61--8--9-85--4-3463298157871356942529471638397824561685719324142635789754983216236147895918562473 diff --git a/apps/sudoku/sudoku.hard.txt b/apps/sudoku/sudoku.hard.txt new file mode 100644 index 000000000..2a70aad75 --- /dev/null +++ b/apps/sudoku/sudoku.hard.txt @@ -0,0 +1,200 @@ +---15962-----2-89-95-86--7-8--9-634---35-81677---1295-12--845------91--6---2-----478159623361427895952863471815976342293548167746312958127684539534791286689235714 +29---3---------9456-89--213-2-8--57---7-9--36---47-82-53--6-18-8----469--4--8-3--294513768713628945658947213429836571187295436365471829532769184871354692946182357 +-9431827--------98---79-1-3-79--1--2-28---567-5--879-15-764------3-297--------314694318275731452698285796143379561482128934567456287931517643829843129756962875314 +4-7--1-322----------3-92157-8--2----1--3784--5-2614389--8-4-713----8-5--7-5--92-8457861932219753846863492157384925671196378425572614389928546713631287594745139268 +--58--937-769-------3-156845-4----6-6--3-48-5-8-15--2-9-74--2-6--25--3--35-----41145862937876943512293715684534287169621394875789156423917438256462571398358629741 +-65--84-77-8--1--61937-48-------56-46314---8---2-----13-4-57-69---8--2--9--1-35-8265938417748521396193764852879315624631472985452689731384257169517896243926143578 +-6-9-372---86-43---3-2---8-3--7-5-1-85-3-1----4--62--54--1296-3---478-----2---14-564983721278614359931257486326795814859341267147862935485129673613478592792536148 +---1-72-4-61432-79------31---837-4-1-39---758-17-4-9--------642-2-91---787-6-5---983157264561432879742896315658379421439261758217548936195783642326914587874625193 +8--3259-4--5----82---781---76-29-----8-5-46--9--61--2--9--5--7---784---9-4-93--51876325914135469782429781536763298145281574693954613827392156478517842369648937251 +--3-----7-9-16--8-----57-94936-781-2--4---5-675-6248-917------83-874---------1-6-583492617497163285612857394936578142824319576751624839179236458368745921245981763 +--789--1-5--243-6--9-61--45-8-97--3-7-43-1--99---64-----9-36---4-1----82--5-2-496647895213518243967293617845186972534754381629932564178829436751461759382375128496 +--9-568376-8--2-9-7-3--1-629-7-25--1----64-----173925-57--98--3---2-----3-6--7-28129456837658372194743981562967825341235164789481739256572698413894213675316547928 +-38-972----2-8493-9-63------9-----75-7-91--2-86-74----62---85--4-----19-5174-96-2138597264752684931946321758291863475374915826865742319629178543483256197517439682 +-7--4--36698--1-24--------97--45--9--4---2-----5-9-487362-14---9846-51-31--98-6--571249836698531724423867519736458291849172365215396487362714958984625173157983642 +-----4-----678--419--15-----2--98-65-58-4-----49561---2918-63---739-5--8-6541---2712634589536789241984152736127398465658247193349561827291876354473925618865413972 +85169-----9--73--16372-1-592---3-746--68------4--263---------3-7139--86---4-6-51-851694273492573681637281459289135746376849125145726398568417932713952864924368517 +281---------48-7--7435-6--8-7--1--6--586--1---3627--5--64----91-29-4---65-7--9-8-281937645695481732743526918972815463458693127136274859864752391329148576517369284 +8-791-4--5--8--1----146---53--72--1--2--846-----13-25----657---6-3-----8-14398-6-837915426546872193291463785365729814129584637478136259982657341653241978714398562 +9-3257-41--8-14-7------935-29--48-------3-2-735--2-1---3286--1-6--4-3----891--63-963257841528314976174689352291748563846531297357926184732865419615493728489172635 +3-6-9---75-98--162--2-------6--12-8----98-536-834-6921197-2-4-----5----92---49-7-316295847579834162842761395965312784421987536783456921197623458634578219258149673 +-8--14392192-584--3-49--8-1-61-8---4----9---37--------235---946847-3-21-6----2---586714392192358467374926851961283574458197623723465189235871946847639215619542738 +-1837---6-9--8----2-65-1-43--7----9--241-----8---4-372--27---85-8-45-637-458--21-518374926493286751276591843357628194924137568861945372632719485189452637745863219 +48--75--1--9------7--6-1-9426-------59-7-8--38173--95-3--1-7-699--4-6-8-65-8--417486975231139284675725631894263519748594768123817342956348127569971456382652893417 +--634----5--97-3-2-----2---45-23-1--89-6-47--3617-5-8--1--674-574--2----62-453---276341598584976312139582647457238169892614753361795284913867425745129836628453971 +--1-6495-5-6-931---925-----7834-92-1-4----79-12---68--26-34----3--97--1-9------38831264957576893124492517386783459261645182793129736845268341579354978612917625438 +-2-56--4----7-8-239-------8-86-----44-938---------6-5-3-----7-5-781534-25928-46-1823569147165748923947231568286915374459387216731426859314692785678153492592874631 +3--65--7----73-58--5--412----712---88--4--7--4-697-1--2-3-----4-71394-2----286---328659471164732589759841236597123648812465793436978152283517964671394825945286317 +8---2-6-1-5-9---4227-----59--7--5-686--1-4-355--7---1---18-9---------487-8--56193894523671156987342273641859417235968628194735539768214341879526965312487782456193 +4-2-----5--84-79--9-78--6-328-746-3--94-5--7-7-691-42---9164-----------1--3-892-4432691785568437912917825643281746539394258176756913428829164357645372891173589264 +42-16--75-8-5-4-6-651-79-3--943--5-7------2--2154-----9--64-3-15-69----4---75-8--423168975789534162651279438894326517367815249215497683978642351536981724142753896 +6-8--259------68-21--7-56344---------35--19--9-6-4-1538-351--292----3----74-2-3-1648132597357496812192785634481359276735261948926847153863514729219673485574928361 +-2-6-359-5--489-6---71-2-3-2---96----1-2-7-8-7-983-2----2-68---9-3---41---5--4629428673591531489762697152834284596173316247985759831246142968357963725418875314629 +-2-6-4--35--39--7-13-75------124-5---4-58-96--5--6--1----129-----6---72-2--8764-5729614853564398172138752649691247538342581967857963214475129386986435721213876495 +-5-8---973781--46--294-78-38-7--9--6--6-139-8----5-7-496--74--1----2----732-----9654832197378195462129467853817249536546713928293658714965374281481926375732581649 +-31--246--4---------67-41928--4------2-168--3-94253786---8----9-19--78-4--83--621931582467247916538586734192863479215725168943194253786652841379319627854478395621 +--7--3-29-48-----62-9-6458775-----4--2-84--5----65--93----1-9-4--24-531--143-62--167583429548729136239164587756931842923847651481652793375218964692475318814396275 +-47---3---91843-76-856-14294----5----1-----65---36-1-21--45--9--6----2--93-7-6---647529381291843576385671429426195738713284965859367142178452693564938217932716854 +--1-35-7-364-729------4----9--3---64523---79-47695-8-31----4-87--7---2---8-716-4-891635472364172958752849136918327564523468791476951823135294687647583219289716345 +-9--16--3--542--1--81-39---8---549----9--2--8--4-976-2---685----5-27349-2-----3-5492716853635428719781539264826354971379162548514897632943685127158273496267941385 +-9-2-------19------24-57-9168-7-41----91-32-6--76-5-481--469-83-7--3-9--9-6---4-5795216834361948527824357691683724159549183276217695348152469783478531962936872415 +-84---2--2-9-78------4---1----86-73--3--14-2-9-----8---687513-27-----1-619328-5-4384195267219678453675423918541862739837914625926537841468751392752349186193286574 +1893-4---6-71--49---39-6-7--64------7-5---6-939-6-2745----192-8572------9----536-189374526657128493243956871864597132725431689391682745436719258572863914918245367 +89--3--21-4-81----72194----4---------17--9----852---471-965---8-7412---32-87936--896537421543812796721946835462375189317489562985261347139654278674128953258793614 +8--3912-------68--6-1-85--4-8----5----3--21985-68493721-295---3--5------9--6134-5854391267397426851621785934289137546743562198516849372162954783435278619978613425 +416-29--3---7---547536----9--4---9-169-3-14----829--6-84-1--7-5-21---39-3--9----8416529873982713654753648219274865931695371482138294567849136725521487396367952148 +--58--743--793----86-4--9522-4--95-8-86-5--1----2-8-9---23-71-9--81--4------84-26195826743427935861863471952274619538986753214351248697542367189638192475719584326 +-691--28--17--5-9--2-9--5---9435----1-27----3-83-9-4--246-7--19875-----------274-569137284417825396328964571794356128152748963683291457246573819875419632931682745 +-16--78----3---25-4283-56-1-6743-----4-52--1--5-----481---7359-6-59-----79--541--516247839973186254428395671867431925349528716251769348184673592635912487792854163 +6-21--4-33-79---589-5-836--531--6-82-2--9-1-----3-15-71--6-----4---1-----7954--16682157493317964258945283671531476982726895134894321567158632749463719825279548316 +7--8-6----6157---3-593---76----6-41-62--1--3-19--85---9-61243-8-----9-62--5-38--4732896541461572983859341276587263419624917835193485627976124358348759162215638794 +--7-63-5-------4728-59-2----56-4---3-31---985-8--51-64---5-6-47------6-1-6271453-127463859693185472845972316756849123431627985289351764318596247574238691962714538 +32-1-----7-42--1-31967-85--567-9-4-14--5---67------9--65--2--89---6--2---42-573-6325149678784265193196738542567893421439512867218476935651324789973681254842957316 +8569-2-74------89--79---5-67-83--6-9---4-87136--7-94--9-4--5--7------265-12-37---856912374423576891179843526748321659295468713631759482964285137387194265512637948 +8----------73-89416-1--9-784-89567239--1726---2-8-------26--197---2--4--1-4--38-2893417265257368941641529378418956723935172684726834519382645197579281436164793852 +---2-4-31764------3---95-7-9-6-381--123---58---5-6-39-6-73-9----98------53-78694-859274631764813259312695478976538124123947586485162397647329815298451763531786942 +96-175---13--9----2---6317---651-----7-83-59------9---6523-7--484-6-13--71-95-2--964175823137298465285463179396512748471836592528749631652387914849621357713954286 +--8-72-59-9-615-7-3---48-2--8-5-7-----4-83-6-53-26---89----158725-----418--7-6---618372459492615873375948126186597234724183965539264718963421587257839641841756392 +4---175---7825--1--1-8-6-----7-412---3-5986--1---6283---16-5----82---49---3--9168426917583378254916519836742867341259234598671195762834941685327682173495753429168 +4-5-386--3--174--58---96-2---681--421---629-----4-3--6---3-1-6-9-7----84643--7-5-495238617362174895871596423736819542154762938289453176528341769917625384643987251 +-6-93-8----4-27-3--5368----5--74---6--62--5----716-2-92-----4-8-4-812-67---459---762931845984527631153684792529748316816293574437165289291376458345812967678459123 +1-27598--5-82--379-7--382-53--82495----391786--9-6--------4----7-------32--6-5--8132759864548216379976438215367824951425391786819567432681943527754182693293675148 +-965-8----8-----7---124---652-----9-43-8-162716-3298-5--2--4----1----4-8---98-71-796518234284693571351247986528476193439851627167329845872164359915732468643985712 +-7--85-2---69-45---35-72------863----6-4217-94------62--9-17-48--7-4--3-3---96--7974185623826934571135672894792863415563421789481759362659317248217548936348296157 +-15-37-8---3-486---6---1-3--9--25--32--81--4-----93-26-49---1-----564---3-8179--4915637482723948651864251937496725813237816549581493726649382175172564398358179264 +-4192--6-532-4---9-76-1-8--15-6--2-8-68-5--7-2-4-----6---37-1-24-5---6----34629--841927563532846719976513824157634298368259471294781356689375142425198637713462985 +6--8-2-5-----3---29256--8---14-87269---12-347-7-9-6-1---6------3-2-1-9764-72----5631892754748531692925674831514387269869125347273946518186759423352418976497263185 +-9-5-----7---8--34---67-52-9371-485--6-3-2---2-57-86135-48------89----65----3-4--892543176756281934413679528937164852168352749245798613574826391389417265621935487 +-1--89542--6--21-8-98--136--69--3-----2---7--1---9-2566-43-89-5-7--1-----31-25--4317689542456732198298541367769253481582164739143897256624378915975416823831925674 +-671-943513----9---4-8--67-----9586-7---31-5-5-4----1-21-98---7-7-51--28-----21-6867129435135647982942853671321795864786431259594268713213986547679514328458372196 +8--------5-463-9-----7583--9-7--62-3--3-256--1-----4-5-458-37-2-2-1--53--9-572-64836249157574631928219758346957416283483925671162387495645893712728164539391572864 +5--329-47----81---3-9-475--8-2--36-4---4------37-962--9---783622-3--54-86-8--4--9561329847724581936389647521852713694196452783437896215945178362213965478678234159 +--9-5241----7------72--4-9371-495--6----38-----6-71-54294-13--5-37--6-49-5---7-32869352417341769528572184693713495286425638971986271354294813765137526849658947132 +----3859-827-------9-1-6-2-27--91---4-1-------69427-8171-9-4--5953----466--7---19146238597827549163395176428278391654431685972569427381712964835953812746684753219 +29876---34-5-8-----16-4-5-8963--28-1--1-3-69---------214-6-39-53-9--427-----9---4298765413435281769716349528963572841521438697874916352147623985389154276652897134 +81-56-2--4-539-7--7-6142--3--8------97-6--18-15---4---56148-----8-75-----47-2-56-813567294425398716796142853638219475974635182152874639561483927289756341347921568 +-7-1-8924--85-276---4-9351----3-----3-9-861------59--21-582--4-----3-85----91523-573168924918542763624793518852371496349286175761459382135827649297634851486915237 +8--9--216--6-3-----2-8-6-9-691-875-435746-----4-1-9--7------9----26--75-179-4-63-834975216916234875725816493691387524357462189248159367563721948482693751179548632 +83412-5-7156-47----7-3-8--19----415-318-7-46-------3---9-4-2-3-4---6------2--3984834126597156947823279358641927634158318275469645891372591482736483769215762513984 +--8-76--44----2-8-2-5-81-6-----9843--3-62--7-9---358---862197-----547---7-9-----2318976254467352189295481367652798431831624975974135826586219743123547698749863512 +21-7---8--75-9-6---96---47---1-4--6--63-15-2--82--6--4548----------89--11396-28--214763985375894612896521473751248369463915728982376154548137296627489531139652847 +--4-53-29---1-----1-3--26-4--7-192-5----68---91-2457--5----13-6432-965--6-1--74-2784653129269174853153982674847319265325768941916245738578421396432896517691537482 +2-7--51-4-54--1---9613-78---89-5--36-----95--52-78-4-9--2--8-97------6--798-6-2-1237895164854621973961347825189254736476139582523786419642518397315972648798463251 +5--39--2--3---1-----9-2-543------2--624--731--5--3-68-7--24---6861-73---2436-98-7586394721432751968179826543318465279624987315957132684795248136861573492243619857 +96---52-----7---1--81---53-----91-2-13-27------9---6---12657-49643-18-52---3---68967135284354782916281469537476591823138276495529843671812657349643918752795324168 +4-31-297617--------69---1-4--4--631-926----------542-8-3-4-1-9--915--74-8453-7---453182976178649532269735184584276319926813457317954268732461895691528743845397621 +134-8--26698---5475----6-----1-2467884-7--2-1267-91--4-25-1-4------7-8--3--------134587926698132547572946183951324678843765291267891354725618439419273865386459712 +6--9-3-85-3--567-----84---6-9--67-5--4-381--228--946--17-----98---43--6-468-1--2-614973285832156749759842316391267854546381972287594631173625498925438167468719523 +8-574-621-125---7-67----945-2-6318---86--2-3-9-------67---69152-6--54--3----2----895743621412596378673218945524631897186972534937485216748369152269154783351827469 +---3--4-656-2---7-8-4-67--36----82--4---321-9---45--689-----31-7--8196---567438--271395486563284971894167523615978234487632159329451768948526317732819645156743892 +4-----8--5----4296--1-287-47--4-----264-9-5--935-876411-7--6982----7------9-52-67472639815583714296691528734718465329264193578935287641157346982826971453349852167 +-817--4-97-49-----5691-2--3613-5-9-8--83--16---------53-6-7-52-87--316-4---6----7281763459734985216569142783613254978458397162927816345396478521872531694145629837 +165---3----4-6-295-8-3547-193----4-664-79------843-9--8--1-3-4------21--41----572165927384374861295289354761937218456641795823528436917852173649796542138413689572 +---865---49-213-5-3-5---2--9---546----6--2--98-2-91--4----39-67-7-42---5--3-78-9-721865943498213756365947281917354628546782319832691574284539167179426835653178492 +--267-13---41--5------54-82--94--765--73-6---48-5--3292-5-3-94--48--9--1---24---3952678134864123597713954682139482765527396418486517329275831946348769251691245873 +--8136-455-38----96-15-9--7--749-618-------528-6--7-941-59-3--6------9--46---1-83798136245543872169621549837237495618914368752856217394175983426382654971469721583 +328----49-4-----36-9623--7-83----9--56-48---2--4------48--7--239-3-6--516-2--1798328756149745918236196234875837125964569487312214693587481579623973862451652341798 +5-67-2493-29-----7--3--1-5634-----2----47--61-5-29--4----8--6-2--5-2418--82-175--516782493429536817873941256347168925298475361651293748134859672765324189982617534 +-9---3-4--6--9253-4---869----9-75-1--8-63---7-74-19-----83216-9---548---3-----18-192753846867492531453186972639275418581634297274819365748321659916548723325967184 +---2-54--457-86--2923-------4-3---8--62--83---784-9-6--915----86-4-1--2--85----13816235479457986132923741856149362587562178394378459261791523648634817925285694713 +8--3-5796-----62-46-4--2-157---------95--8--11-6-7-9582----9---4-958-12--37-2-8-9812345796953716284674892315748951632395268471126473958281639547469587123537124869 +-2---1635--7245-98851----4--7--24-6-1-2-96---6-4----21---3---8-7--48-2--2-8---953429871635367245198851963742573124869182596374694738521916352487735489216248617953 +937------6--9--45----61-3-81682-4---57---126--4-5-6-7--25------79----5-64-63-5729937458612681923457254617398168274935579831264342596871825769143793142586416385729 +8634-5-915-9261-78----3--4635--92-----7---6-----5-7--94-5---1-3----2---567-1--9--863475291549261378721839546354692817917348652286517439495786123138924765672153984 +--5912-7-6-3857294-2--6--------2--3-2---7-5193-1--4-629---46351--6------1-7--59-6845912673613857294729463185598621437264378519371594862982746351456139728137285946 +-65--97-3-27--814--4871---5--3-9----28--3459---9--5---7945--61-6---8---9---9-72-4165249783927358146348716925573892461286134597419675832794523618652481379831967254 +-1---2---925---6317435--28---48-35622869-43--35--6-4-88-14----3---6----5-7-------618392754925748631743516289194873562286954317357261498861425973439687125572139846 +--6452--7-75-86-1---8-193--561-2-7---29---8-5---64-1-----56---1--4-31-9--1-8-45-3136452987975386412248719356561928734429173865387645129893567241754231698612894573 +9--2--567-4-5891-3521-----8-86---2-94--89---6-9263---------7--1--4-18-9--19---735938241567647589123521763948386174259475892316192635874263957481754318692819426735 +264-398--8-3--4-1-51---893-7------4-421-9---8-----519---6-4258-----8-7--9-25-3-61264139875893754216517268934759816342421397658638425197176942583345681729982573461 +--135-8-2--58--6--6--19---58-----9-4-9-438-51---962----6754------9-81-7--5-27-4--971356842435827619682194735823715964796438251514962387367549128249681573158273496 +-----35-662-1--8-7-------2---4639-188--47--539--51-6-2---32-165---85-2--51--9-38-147283596629145837385967421254639718861472953973518642798324165436851279512796384 +3512-4--6-89--5---74---98-5--------1-7---643-463-1-5-79-746-3-8-36-9-12------3--9351284976689175243742639815295347681178956432463812597927461358536798124814523769 +--------5-3-451----8-6-39-132--6-4-819------7-6--123--9-6124-8-24-53-19--137---2-471289635639451872582673941325967418198345267764812359956124783247538196813796524 +--1-43----78--9-5-6397-814--4-96-----85---6-1-6--7-4-38-4---2--52---7-647---24-15251643789478219356639758142347961528985432671162875493814596237523187964796324815 +----83-----6-91-2591-245-6--89--6-42-5---9-78742-18-5---4-572-1---9------97--2-84275683419436791825918245763189576342653429178742318956364857291821964537597132684 +-97---21-1-5-9---------3--5----286-18----935--7--1-----2-63----73854-1-96-198-432397854216185296743264173985543728691812469357976315824429631578738542169651987432 +-17-2-4------5-6--3--------758-92--4-2--748566-45--7-2243-6--781----8---869---145517826439492753681386419527758692314921374856634581792243165978175948263869237145 +-2------59-7-86------9-28---93---7-42-54----8----6-9------7-53--8965412-5713-948-428731695937586241156942873893215764265497318714863952642178539389654127571329486 +7-94----243-682----129376-49---1-573-8---3---1-35---9-3----526-------9---4-27938-769451832435682719812937654926814573584793126173526498398145267257368941641279385 +-41-26---------792--785-1--8-239751--------83----819-74--13-8-95-3---4611-89----5941726358685413792237859146862397514719542683354681927426135879593278461178964235 +8-695----1---32-8---9---7---18643-59-53---6---2419-378--1----92----691-73--2-----876951423145732986239486715718643259953827641624195378561374892482569137397218564 +7---3-1596-9--27-8----7---6--8------9-3--581-1---28965-7--8------5197--38-6453271782634159639512748514879326258961437963745812147328965371286594425197683896453271 +78-542-63---1---95135-98-72-56-2-7-----4----889----2-16-----5---187-4-------86--7789542163264137895135698472456821739321479658897365241672913584918754326543286917 +7462138-5932--6--78------2-6--7392--2----5-----1-285-9--738-9-6--------81-85973--746213895932856417815974623654739281289165734371428569427381956593642178168597342 +1-----57--548-71--3--25---4---6---19471-25-8-8964---5-72----8-5--85---3--137--69-162349578954867123387251964235678419471925386896413257729136845648592731513784692 +--9--48676-3---1-4--87-5-----2-183--9--5467---8--736-9-1--825--2-635---88--6-7---529134867673829154148765923762918345931546782485273619317482596296351478854697231 +-2--319---8-576--4-64-82-1--37---2-6---8-51--618--74----125-6-9-5--19-3----6-8--1725431968189576324364982517537194286942865173618327495871253649456719832293648751 +9---36-2--6572---3-3-5-4---3---47-58-8-215-4---6-93-7---8--1534-57----91--34-2---974136825865729413231584769312647958789215346546893172628971534457368291193452687 +-1--46237-36--8--1-72--58-6-21-845------69----5-271-84-67--23-8---8-----2-4-137--815946237436728951972135846621384579748569123359271684567492318193857462284613795 +--4-5---2-5---4-73--3-28-415-6----94--76--32-4---9--687--4-3219------857-2--79---194357682258164973673928541516832794987641325432795168765483219349216857821579436 +----123-73--46-5---1--38--2--2-54-7--7---62---68-294---51----6-2-4691--5---875---846512397329467581517938642932154876475386219168729453751243968284691735693875124 +-----1---5--9-76-86-----7139-7--4---8--6--27-2-63-5-8-76--2---43-279--61-89-5-327473861952521937648698542713957284136834619275216375489765123894342798561189456327 +1--697-25-6----1-7---431----3--15--2--2--63----5-296-43-8-72-----156--8-2---847--183697425469258137527431968634715892972846351815329674398172546741563289256984713 +6-597124-9-83-----1-2-84369-2--1--954--7-----31----48--9----5-----2-7-342---45---635971248948362157172584369826413795459728613317659482794836521561297834283145976 +3-51--249-12--9---------167--3--651--97-5-43--6--3-9-----68-32--2-3--78-8---1--95375168249612749853948523167483976512297851436561432978759684321126395784834217695 +6-8-12--5--15489-22--------4-21795365--6--817-7----2--8-42-7--9---4--7----78516--648912375731548962259763481482179536593624817176385294864237159315496728927851643 +1------98---479-----92185-3-648-5----9-13-6----56-7-8--5---1-4-4--9-3--5-3-5-2-17127356498583479126649218573264895731798134652315627984852761349471983265936542817 +-2-3-6-4----5-----3514-89--9---3--15638---49-51469-7-3-4----8----7241-5-2----5-74729316548486579231351428967972834615638157492514692783145763829897241356263985174 +--91687---15---6----8457-2989-2-4-----3-8-9--6-153-4--5--97-8-4-7---329-9-4--5---249168753715392648368457129897214536453786912621539487532971864176843295984625371 +25-8---9----4---3---7-------845-93--9--134-58315--8-69879-51-4-461283-----2-----1253876194698412537147395826784569312926134758315728469879651243461283975532947681 +67-3----81------458524-17-6-4----82--2--54-7-36--12-----713--94-9324---74-6--9---674325918139687245852491736745963821921854673368712459287136594593248167416579382 +9-351---27-----------947--5--56-9--12-4--1-568------93-2-49613--39-75-64-6--8-5-9943518672751362948682947315375629481294831756816754293528496137139275864467183529 +---4------1------2-8--65--7--127-8-683-6917--76---8219-2-84796-9--5--------932175357429681416783592289165347591274836832691754764358219125847963973516428648932175 +6-7-14-5-2----6-1---1-35--28---971------8129--9-56--3-3-8-----6-156483-----723---687214953253976418941835672832497165576381294194562837328159746715648329469723581 +-269-85-----7--2-8---6217-93---86951-8--924-3-5-17-6-2-----7----1784---6---21--9-726938514139754268845621739372486951681592473954173682298367145517849326463215897 +-52974--6-71-86-5-43--21-9-58---234---3------14-7-----214-37---3---15---7-5-9-21-852974136971386452436521897587162349623849571149753628214637985398215764765498213 +----8----815-936--2---14--9--7925--89-----3---2---8-9758964-71--6-1---85143---9-6394786251815293674276514839637925148958471362421368597589642713762139485143857926 +-4329--17-7--8-23--6-7315-48------436-15---72--23-7-5----913-2-9--------3-4-52-6-543296817179485236268731594857129643631548972492367158786913425925674381314852769 +------4891-3--827678-6-----3--4--81--4--3--6-96--1-32--7---359---5-8-6-1----5473-526371489193548276784629153352496817841732965967815324478163592235987641619254738 +---6----4----1---616-48-37-59---1268--67--5--3-8-26-----19-2-8798--7--322-4-6--51859637124743215896162489375597341268426798513318526749631952487985174632274863951 +82-6-4--39415-38--65--97--1---7-----5---32-79--4-69---47-956----1-34--5636--7----827614593941523867653897421192785634586432179734169285478956312219348756365271948 +-6---5---7--6-91-5-4-321--64287635-9613--42---9-----6-------9--2--93-4-197-152--3169475832732689145845321796428763519613594278597218364351846927286937451974152683 +2-7-64-3-1-68-9-5-458-13--7----9-----4-6-8-----13-298-9-4186---6-39-----5--43-16-297564831136879254458213697825791346349658712761342985974186523613925478582437169 +73--2--9-2-51----81-8----26---9-17--471-83--9659--------76---8-8-47-5-3-9-3--86--736824591245196378198357426382961745471583269659472813527639184864715932913248657 +-9-2134-7---968---3------92--714--386--79---4--43---6--6542------9-375---4-85--2-596213487472968315381574692957146238638792154214385769165429873829637541743851926 +----8--1---6-4283-9-23-76-51-----4-------395-425-9---88-7--45--35---879-264-79-8-743685219516942837982317645139856472678423951425791368897134526351268794264579183 +8--3---5-7-4-5-3--1-586--7-6-92----55-2----93-78--9-4-281-75--4463---------42-8--896347251724951368135862479619234785542786193378519642281675934463198527957423816 +--2-96--1------3824-17-3----4--158-9-19-8-6---65---174-93258-16------59----1-92-8532896741976541382481723965347615829219487653865932174793258416128364597654179238 +-23--4--7--9-7--4--75-3--969512--7-3642-----------9-21-867---1-39--2-8---1748----123964587869571342475832196951248763642317958738659421586793214394126875217485639 +23---6-8-6-9-5-7--5-7---16-8-37----17-492--3---2-1--7-4253-78-----58---2198------231476589649158723587239164853764291714925638962813475425397816376581942198642357 +5--3-4--7---78-5627-2-15384-4-1-8--9----6-8--8394---1---4-------68-7-435-258--9--586324197413789562792615384647138259251967843839452716174593628968271435325846971 +1-826-5379-3----4-2-54836-9--6----7--2--31--5-5469------2---1-6----4672--3-1-----148269537963517248275483619316854972829731465754692381492378156581946723637125894 +---539-----9--71-4-67----25---9-3-71---81----1-5-748936--3-17-84-8726---7-14--3--214539687589267134367148925846953271973812456125674893652391748438726519791485362 +--6-824-9--96-4---7-2359--1--------3--1--825--3-5219-821-86-5-4-6-1-537---5-----6156782439389614725742359681528976143691438257437521968213867594864195372975243816 +429586------9----557-132-6-2--754-811-762-4--645--1-72--6----4---2-79-1----3-----429586137361947825578132964293754681187623459645891372956218743832479516714365298 +-8--65---627-81----15-3--6245---2-78--8------76-1-----87--26-3-1-6-49-5-5-23179--384265791627981543915734862451692378298473615763158429879526134136849257542317986 +7--69-8---5-----1-83-4-5----64----3--295-7168-872634-5-7----5-96----9------35--71742691853956832714831475926564918237329547168187263495473126589615789342298354671 +6---9517213-8-----5--7--8632-5----1--7-95--4---16-27-53-82--4-14--5--6---56----29684395172137826594529714863265473918873951246941682735398267451412539687756148329 +81----9---4-95-1-----16-2-7--8--74-2-95-----64-26-9518---3---29-39-76-4---4-9137-816723954247958163953164287368517492195482736472639518781345629539276841624891375 +17243---5------3---53-1-6-25941687-36--2951-----3---9-7--9-----3-9-4-2-72156----9172436985968752314453819672594168723637295148821374596746923851389541267215687439 +-36-582-77-5-3--14---7--3--587-4-6-96--5---78------4--86-3--9-22-39-----9748-15--136458297795236814428719356587142639642593178319687425861375942253964781974821563 +------6913-251------9-47-2--27--6--4-48---532-3--82-76---27--69-7196824-------7-8754823691362519487819647325927356814648791532135482976483275169571968243296134758 +389-145----4-8569356-9--4-8-7-----------9---68-2-4---5745-6-38-136---952-2---3---389614527214785693567932418673521849451897236892346175745269381136478952928153764 +----3--2-8-47-5---35------4--8-----5-9-2-3-4----15--9798254-3-6--7---1-251-6724-9671439528824715963359826714148967235795283641236154897982541376467398152513672489 +76-53---8-----6------79-2-----987-62----6--9398--23-1---43-81273--2-9-541--67--89761532948239846571845791236513987462427165893986423715694358127378219654152674389 +--7----86--9817--334-296--12-13-96---34-5--9-----6-3-2--26-57-8--3--1--5---42-13-127543986569817423348296571271389654634152897895764312912635748483971265756428139 +-96-43-----1---83---3-21-7--679----8-4-----123821-47-67--49--519-531---761---5---896743125271569834453821679167932548549687312382154796738496251925318467614275983 +8-6-14--3-178-------3-65-8176-45----4--19--3-1-----24-9245-13-7-5-----1937---6--2896214573517839624243765981762453198485192736139678245924581367658327419371946852 +2-194---8-7--82-4-8--1-3---1-9----76--5--6183--83-4---5--461-3---2-78-9--8--39-15251947368973682541846153927139825476425796183768314259597461832312578694684239715 +-----37-6--12-4398-73--645--35-8-14994---15----8--------6--9---28--6-9-1-9741--65429853716561274398873196452635782149942631587718945623156329874284567931397418265 +2-17-------3-89-72-78-25--3-2----65--5-24--3-81-59----6459-23-19------2413---8--6291734865563189472478625913324817659759246138816593247645972381987361524132458796 +--3---21---7-25--929-1-83--5-1---8-2-8---2-7-37---146-648--9-2------4-3613925--8-453697218817325649296148357561473892984562173372981465648739521725814936139256784 +1-8--5-4372----59---3--2--72-5---4---6--293--4--5-7-26--729-654-84--1-----2--6781198675243726134598543982167275863419861429375439517826317298654684751932952346781 +15-----872-97--64---65--1--5-7-----44--1-735--3-85---6--53--291--1-85473-942-----153462987289713645746598132517639824468127359932854716875346291621985473394271568 +712-96-844--2-7--683641------51--86-627-4-31--------7-5--9-17----1-3-----9-7--251712396584459287136836415927945173862627849315183652479568921743271534698394768251 +-54--38277---9--4-8-12-7---6-27--3-43---529-8-859--2-1---3--7------7-6---7386--19954613827726598143831247596692781354317452968485936271169324785548179632273865419 +-1-6-9--8------571-8453----4--2-87-962----83489---76-----98-1-795-712-86------29-512679348369824571784531962435268719627195834891347625246983157953712486178456293 +2--4--95---1--64-84-7---6-1-7--1-2-55---891-31----7--9--215-396-9--62---------782286431957951276438437895621879314265524689173163527849742158396398762514615943782 +639---7-4-4----2-927-93---54---------2864-3---96----7-3-2--16579-72--1-8-645--9-3639852714845176239271934865413729586728645391596318472382491657957263148164587923 +------1486-1--7---29--1-675---38-9-6--6-9-4-3-3-1---279----82-1--89--7--4-72--5-9375629148681547392294813675742385916816792453539164827953478261128956734467231589 +92-64--37--8--5---5-43-1-9--5---73217-24-----1-------8--57-69132--1---656-75--2-4921648537378295146564371892459867321782413659136952478845726913293184765617539284 +--91-43--3-17-56--745--912-6--9-723-------81--32--69574-35-17----------35--27-49-869124375321785649745639128658917234974352816132846957493561782217498563586273491 +3-65--82-9--6--37-27831--9---516-98----43----16-928-5------6---6-78--23---279-1-8316579824954682371278314596425167983789435612163928457831256749697841235542793168 +4--821--7---5---1-2197645-3--------57389-52---25--78-45-1--93-88724--1--3--1-----453821697687593412219764583146382975738945261925617834561279348872436159394158726 +53----6-7---416---4--5--82-2-5--8-1-9-8375-----32-195-----29------1-42-56-285-194531982647827416539496537821245698713918375462763241958154729386389164275672853194 +-135-4--74-8-1------76-3-14-4----95-38--56----5--42-7-92546-7-86------4287-3----9213594687468217395597683214742138956389756421156942873925461738631879542874325169 +6--432-5---198--7--8-57-63---8-25---3-7---49---6-4-5838---53----9-81--2-1-32-78--679432158531986274482571639948325761357168492216749583824653917795814326163297845 +19-86--2--3--9--8-8--72---3---48-35---59-2-7--4-51-8--47------9---371---2-86497--194863527732195684856724913921487356685932471347516892473258169569371248218649735 +----718---36-54129184-32-67-----69--5-13----2-672-8----5-1-----6-8---39------3-56925671834736854129184932567842516973591347682367298415253169748678425391419783256 +231----------9--15985--17-6-16-3---7-786---92--97---3-69-1--4---42--7-5--574-3---231576984764892315985341726516239847378614592429758631693125478842967153157483269 diff --git a/apps/sudoku/sudoku.medium.txt b/apps/sudoku/sudoku.medium.txt new file mode 100644 index 000000000..bb9b3a6c6 --- /dev/null +++ b/apps/sudoku/sudoku.medium.txt @@ -0,0 +1,200 @@ +52-9647-1--4---9-5879-216-3--8---2--7--39--1-----5247------3-9--8124----23----1--523964781614837925879521643158476239742398516396152478465713892981245367237689154 +-4-9-3--57--28-9--96-5-4-----67-8-4-4----2--68--4-1-32---637----7-12548---2----57248913765753286914961574328326758149415392876897461532584637291679125483132849657 +--92---58-1---742--62---17---8-4531---1--65--6---1-89--9-18-7355---79---------964479231658815967423362458179928745316731896542654312897296184735543679281187523964 +16-2-4----5-39-6--------572--186-9-7-83---146-96-7---3-2978536--------98---9-67-5167254839852397614934618572241863957783529146596471283429785361675132498318946725 +87--541-22----975-6--127-38--9---8-7-5-27-3---16-3-2-5-875-36----4---------7415--873654192241389756695127438329415867458276319716938245187593624534862971962741583 +9---851-48-4-129-----4--68--75-----8--983---5---52--163-8----2-7----1-6969-2-8573926785134834612957517493682275164398169837245483529716358976421742351869691248573 +---83-69--7-----8---6-245-----4--2--4-8----5-75--98---62758--148-3912-65-9-----23214835697375169482986724531139456278468271359752398146627583914843912765591647823 +53-7-91--78-6--9---6-38574-37-9-85--4-5--386---------965---149-------27--1-49-635532749186784612953169385742371968524495123867826574319657231498943856271218497635 +3-4-682592----134-58----6--87-1-6--44--8-9-71----7-8-3-92----8--4-58--9----69-13-314768259267951348589243617875136924436829571921475863692317485143582796758694132 +-895-4--31--78-9--3------485269---8----6--23-943-78-5---58---1-47----8-5-314---26289564173154783962367192548526931487718645239943278651695827314472316895831459726 +82--349----529--3--9-7-8---2--95--4--1-863-7---947-18-9--3-7---48----65-1--6--798821534967745296831693718524278951346514863279369472185956387412487129653132645798 +----5--6-93----4-86-4--3----8-2-5---1596-7-344-23-1587-9---4-----1-3-6-5----18-42728459163935176428614823759387245916159687234462391587293564871841732695576918342 +91-38----6-8-----1-----6-5--3----7-54598-16-28-72531-4-9------8---7-8-43--456--1-915387426648925371273146859132694785459871632867253194791432568526718943384569217 +287-43-1---1--9-839-3--5-725---94-21----36----94271-5----9-----8-9--2-377---182-4287643915651729483943185672576894321128536749394271856432957168819462537765318294 +3-6---21-----9--6-48---25------1--436-5249-787416-3-2516-95-------8-6-5---8---4--356478219217395864489162537892517643635249178741683925163954782974826351528731496 +--958--1-2--463-7--8-71-26--5-89--3-9-63-18--8---76-----2-4-7866-1---45---8-37---769582314215463978483719265157894632926351847834276591392145786671928453548637129 +-----1-----235---43547-2-1-2-38-7--9-98-----7517--46-392-5-834--4516--9------9--5679481532182356974354792816263817459498635127517924683926578341745163298831249765 +39----24--584---6-14--9-3--9753-86--621----------69--573--51-8-86---3--25---2--3-396185247258437961147692358975318624621574893483269175732951486869743512514826739 +--869--722-1--7----7952---8-2----45-91--65----5--32-8-4352-68-16------2318-9----4548691372261387945379524618823719456914865237756432189435276891697148523182953764 +4-7----25--8-7-3-12----6---15---4---93-761-8278-25-149--1----3----6-82-48--32----467139825598472361213586497152894673934761582786253149621945738379618254845327916 +7--4-8------27-8-6--1----4-15--6----34-912-7897-8-51638---2------7--94-16-9----85765498312493271856281356947158763294346912578972845163814527639537689421629134785 +519----24--2-5-----4-23---8--64-38-----1-6-477-4----36--3---972965--74--2-1349-8-519768324832954761647231598126473859358196247794582136483615972965827413271349685 +--5-876--4--2-6--93-64-9-----47----3--81-4-27-3-8-5-4----523----7-----955--97148-925387614487216539316459278194762853658134927732895146849523761271648395563971482 +7-1-3---48-365-7--6--4--3---76--21--5-29---3-3-9---24-968-73-1----19---6154------791238564843659721625417389476382195582941637319765248968573412237194856154826973 +---91-4---8-5--37-41-3-7596----65-1-1-9-7--6-6-4---83--4812---7-617---282-----1-3735916482986542371412387596823465719159873264674291835348129657561734928297658143 +--83-15--6-529----9-------78-----91----6-9-78--3-1----32------641-98-75358-46329-248371569675298134931546827862734915154629378793815642329157486416982753587463291 +3--2-4918--2------8-71--3-22-9761534--1538-7--5---2--------5-9-9-84---255----7183365274918412389756897156342289761534641538279753942861136825497978413625524697183 +---1-62--1-3-24----6-----8--15---37-----4-1--6-87---2-8395-17-2-214879-6----3-8-5984176253153824697762359481215698374397245168648713529839561742521487936476932815 +9----4---2--9------372-8-497-865-4--4-1--925-5-3--7-68859-4--16-1-7--9------9583-986514372245973681137268549728651493461839257593427168859342716314786925672195834 +--1--9-269-2--3-47467-82--13---98-14----25----98471--36-9--4-72---9-----7---1648-531749826982163547467582931375698214146325798298471653619854372824937165753216489 +------9587---86-----81-2476-1--9---7-2--751-49----18-2-8-3--72---1-6-3-5-39---6-1162743958745986213398152476614298537823675194957431862586314729271869345439527681 +7---5832-9--2-146---63-915723-91-7------8-29----53281-----25--158--94-3----8-----741658329953271468826349157238916745615487293479532816364725981587194632192863574 +28-7----4--6------1-7428-6-8-9-3724-65-8-4-3---3-----89-127---3---9-3--1--41865-2285769314496351827137428965819537246652814739743692158961275483528943671374186592 +--5------2---159639-7--352-5-6437182--3298--7-8--5----8---7-239----8---66-9--18-5135962478248715963967843521596437182413298657782156394851674239324589716679321845 +---7-----2---6817---3-41---37-124----9-63-42-64--7----1392-6--542--179--58-4-36--918752364254368179763941258375124896891635427642879531139286745426517983587493612 +1------7--2-6-85--95-31----6-3----9-5-24963-14-812-765---9-1-272-----1-8-6--8----186549273324678519957312846613857492572496381498123765845931627239764158761285934 +7-6--5219------435-59-1-----136--7-2--7-4-5-6-4-7----1---8-49-78--5--16--9---78-3736485219128976435459213678913658742287341596645792381361824957872539164594167823 +----5-9--3-1--6--8-94---7-6----7-14--8961523-1729-468----3-98---3------19-758----726458913351796428894123756563872149489615237172934685215369874638247591947581362 +-----5---2497--3-5-759-1--21----29767635-4281----6-5--9276--1--4-----6--5-64-9--3618325497249786315375941862154832976763594281892167534927653148431278659586419723 +2-16--9--6-9---52-37---2-8---35---9-9-431--7-8-7-9---5518---------86---3436-798--241658937689731524375942681123587496954316278867294315518423769792865143436179852 +9-8-21-----567--8-1-----4--5------16---19-5-4--7--6---85-739-2136--1574872----9--948521367235674189176983452592347816683192574417856293854739621369215748721468935 +-9--------15-4-926-231--7----98-1-7-----5-1--1769----82--6-9-4----41-23543--82691894276513715348926623195784549861372382754169176923458251639847968417235437582691 +--3-89524-8--571-3-2-46-9-7-----6---64-81---9---74--5----6--7-8---9746-597-5-82--763189524489257163125463987857396412642815379391742856534621798218974635976538241 +--945--8---3----2------7---9--215-683218---598-5-932--294361---15-978-3------4--1679452183413689527582137946947215368321846759865793214294361875156978432738524691 +--3-456-9------8----5826-----46-1--55-14--3-26-9---7--94-162-3--56-7--1-1-258--96813745629267319854495826173724631985581497362639258741948162537356974218172583496 +----8--5323----87---56-----4-312--------3-7--65-8----13-8-95617----642--962-18-34796482153234951876815673492483127965129536748657849321348295617571364289962718534 +542-76--3--3--8-648-6--1-52-87523--1----69---1---87-35---8-----2---3457-4-8--5-26542976813913258764876341952687523491354169287129487635765812349291634578438795126 +---75--262-----7-9-1---9---1-3----5-4-21853-78-9-72614-2-91-4--7------6-54--37---984753126235861749617429538173694852462185397859372614328916475791548263546237981 +---418----726594--4-6----9-9-857---21--9----7--724--1-6--83-7---3--92--4---76-1-3593418276872659431416327895968571342124983567357246918641835729735192684289764153 +7-9-35264---96-587--52-4--9-4-3-6--1----8-6--1264---3-4--------68--9-42557-6--1--719835264234961587865274319948326751357189642126457938493512876681793425572648193 +1--85--6---3974-2--8-62--93--82-9----5-18--4-9-1-46--83--7--9826-9----578--4-2---192853764563974821487621593748239615256187349931546278314765982629318457875492136 +--8-7----95--2387---3--8----67-8--43-94-5-21--254-1--74827--16-6----5--8---84--92218674359956123874743598621167982543894357216325461987482739165679215438531846792 +-3-87-495-873--16---65--3-73---8-546-681-------5---2---1345---9-2--3----6-97-18-4132876495587394162946512387391287546268145973475963218713458629824639751659721834 +16-2---57-274----198457--2-841-5-2-----6---15-3------4-587-61-9--984--62----2--3-163298457527463981984571623841357296792684315635912874258736149319845762476129538 +7--6-34---1-----9-48--12---3---6-----7----1-6---1-8-79-32----8--56-71934-473852-1725693418613854792489712653391467825278539146564128379132946587856271934947385261 +94--5--611---4---857---8-4-62-4----3-15-7-2--43-82----3917--45-786-----------1-37948257361162943578573168942629415783815376294437829615391782456786534129254691837 +741--58--3-----1529-2831-6-----94-854---836--8-5----34-8-32---62--7-----197----28741265893368947152952831467673194285429583671815672934584329716236718549197456328 +8493---5----8---29231-75-4-3-54-12---6-75---3-2----51--17---4-56-21--89-4--5---6-849326751576814329231975648395481276164752983728639514917268435652143897483597162 +189-736-5--3---17-5-6-4-9--7------898--1-7-5-6-43-8----5-43-72--67--2---24-78-5--189273645423965178576841932715624389832197456694358217958436721367512894241789563 +3-4-----9--713-6--6-1-7-2-547-213-5--63--9-1-1-2-6834---6382-----------8--56-74-3354826179927135684681974235478213956563749812192568347746382591239451768815697423 +3--548--94------82-19327--5---2--9-719-6---3-5-7-392--7---624-89---5---6---7-159-326548179475196382819327645638214957192675834547839261753962418981453726264781593 +------7---8-72569---63--24--9-6-------7--13566-1-3--7--68249---21-5764-87-5-8---2532964781184725693976318245893657124427891356651432879368249517219576438745183962 +-9135----5-7----9------74--8-5643-291425-9-67-3-----48---8-523--1-----5-2--47-9--491358672527164893386927415875643129142589367639712548764895231918236754253471986 +1--64-7-24--7--5---5-13---4---352----7----3-9--3967-413---71-8-8-549------428-9--138645792496728513257139864941352678672814359583967241329571486865493127714286935 +-4-38271--2--9--6887--562-33-4--16-2--9---8-76--2-8--17-861---4--5---------835--6946382715523197468871456293384971652219563847657248931738619524165724389492835176 +6-8----147126----5---3-7--2---7-1-2---392-8--2---34-915-147-2----7163-5---428---9638592714712648935495317682849751326153926847276834591581479263927163458364285179 +5821976-37--4581-----3---8-3-8--24-66--8-----4157----81642-3--5------3---53--17-4582197643736458129941326587398512476627834951415769238164273895879645312253981764 +-63----71----51-36--1-374----89-----592----833--78---4--7---268-85372-4-129--63--263498571974251836851637492748923615592164783316785924437519268685372149129846357 +9-4--78--3-7-2-514--5----------72439-2946-175-4-5-1--25--7-6--8----3-7--178--5-6-914357826387629514265184397651872439829463175743591682532716948496238751178945263 +7--2---3--3-54---729-83--5---1-25-4----36-71-6--91-3--46------2---479----536824--745291836138546297296837154381725649529364718674918325467153982812479563953682471 +6--92-431--917-6-8--35-47-2-548-92-----4-7-1-----5-----27-91--3-----59-7---7421-5675928431249173658183564792354819276862437519791256384527691843416385927938742165 +-19-----23-8294-1----615-------3975--9--67-3-7--8-21---5--2-3---26-438----3-81--5519378462368294517247615983682139754195467238734852196851726349926543871473981625 +532-4--964-9--7--217--69--4----76--18----2---2574---6---3-14-25----8--4-6-57-31-9532148796469357812178269534394876251816592473257431968783914625921685347645723189 +-2--9-6--9----28-136--8-4-9--5-1--86-7-9--3-5---25-9-7------13271---6---8-91--764128497653954632871367581429495713286271968345683254917546879132712346598839125764 +7-18-3---95-6---8------12------793--1-6-54928493-68-71--59-----31----6-2-----6-15761823459952647183834591267528179346176354928493268571685912734317485692249736815 +----6---36-1--345--378-16-27---1-86-186--49-7--------496458---12-3-9----87--3-2-9428965713691723458537841692742319865186254937359678124964582371213497586875136249 +-8----1--3-5-47------3-8--4----5--61-43712-891596-3-42-36---2-5----7---38-12--4--284569137365147928917328654728954361643712589159683742436891275592476813871235496 +-6--43--2------716-295-7---------84----2-46-147-681-2324--1-3--38----2599---281-4768143592534892716129567438612739845893254671475681923246915387381476259957328164 +2-6---5-3-------275739-286--5-2-6--8---7-814676---12-582-3--6--------3599-461----246187593189563427573942861451236978392758146768491235825379614617824359934615782 +75-8123-9-29374---8-19----7------8----2-6-73--9-78124-2-56---8--4--2------8--5612754812369629374158831956427167243895482569731593781246215637984946128573378495612 +7-38---256-94--1-32-8-539----49-------1-4----87-21--49432--9-56---3-42-7-6--8-4--743891625659427183218653974524936718391748562876215349432179856185364297967582431 +4----9687-6--2------76-4-9---19-85--253-61---68954-1-3--------9895-1--26-7---6-35412359687968127354537684291741938562253761948689542173326875419895413726174296835 +784----------812--5123-9--889---37--35--24-9--2--7--3-2-96---8-64--1---313-----67784256319963781254512349678891563742357124896426978135279635481645817923138492567 +---61---856-39-7-------5----2-56-17--9-8-143---49-7682----5-91----17685-71--89-2-473612598562398741981745263328564179697821435154937682846253917239176854715489326 +8-3--416---2-----------6532--93-8---5-46-7--16----1-73-7--639--38-7-52-6-4-81-357853274169162539784497186532719348625534627891628951473275463918381795246946812357 +15-4-7-2-739--54--28---1--69-5-----2---3-87-13--572--45-7---2--62---9-8--912--6-7156487329739625418284931576975164832462398751318572964547816293623749185891253647 +1-967528----9-2-65------9-7--472--967-8---3429-2-6---824-1-3---------6515--89--2-139675284487932165625481937314728596768519342952364718246153879893247651571896423 +43-21-6-5-8--542--2516-97--9--42--715-2-9---4---56--8--94-7--56-------3-315-4----437218695689754213251639748968423571572891364143567982894372156726185439315946827 +7---14-583---8---6---3-672-6-7--5-39----735--23-9--8--4--15----9---3-614-23-6-975769214358312587496584396721647825139891673542235941867476159283958732614123468975 +81247--5--4---8--9---3-218---8---5----6-----893-58-2-64-51-68--3-172-9--2-9--5-17812479653543618729697352184728963541156247398934581276475196832381724965269835417 +6------7----3-21-4--271-----2915-648-78936-21-56--4---9-4----152---9-7-6--1--3---613845972897362154542719863329157648478936521156284397934678215285491736761523489 +--53--12--2---9-84---6-235-1-8--473-3---6-49---6--38--5-9-8----43-9--578------961695348127723519684814672359158294736372861495946753812569187243431926578287435961 +316-47--2-----1----9--83-7-834----272--3--16-1672-83-5-7------4--5769-1-9---1-75-316947582748521639592683471834156927259374168167298345671835294425769813983412756 +7------3--3-8-72-4-8613-79--2-76851-8----16-7-------2-6-4275-8-3---1695-5--3-9---745692138931857264286134795429768513853921647167543829694275381378416952512389476 +47219------18--69--9----4-----31--25-5---8-1631-56----745--1--392--8-167-6-937---472196538531874692896253471684319725259748316317562849745621983923485167168937254 +8-36---29-965----845129--6----3---82--7-----5185-2-6---4-15--36----6--7--129-38-4873641529296537418451298367964315782327486195185729643748152936539864271612973854 +6--284-1-58-----4-2--1756-97-65------1-3---96--5-6172--6279----3----2-6-8-4-53-7-679284315581936247243175689726549831418327596935861724162798453357412968894653172 +6-5-41--2---385--1--3------56-13-9-8-9--7--15-2-95864-8-24--1-91--5-9--4--7---5-6685741392279385461413296857564132978398674215721958643852467139136529784947813526 +6---215---3-----7----7-468--53-78---7-2----5----2--1--4-7819-653165-7-92-8-----14678321549534986271291754683153478926762193458849265137427819365316547892985632714 +3------61-9-8-1-2-57---2-894-----------174-9-16-92--5--16-49-78--8-3-91---57182-6382497561694851723571362489429685137853174692167923854216549378748236915935718246 +5-932-8---1274-6---4-58--92----6-4------1--6-6-48-351--6---7-8--53---2-69-7652--4579326841812749653346581792138965427795214368624873519261437985453198276987652134 +--2-5------32-----17-38--25248--5-69-9-1--2------428-77-4-1--869-5-2-3-48-16-45--682459731453271698179386425248735169597168243316942857724513986965827314831694572 +3---154---1-86--9-----3421-6-5-47-8-2----6-4---4-98--29-3-----6-486739-----529---392715468417862395856934217635247189289156743174398652923481576548673921761529834 +3------5----46-9-1-6-5-9----9--4----6----85-381-----97-25834-69-869-7312-37-1----349271856258463971761589234593746128672198543814325697125834769486957312937612485 +8-63172---27-----1---952----158-3-6---82-6--9-9---1-8-4--16--2--7-4-58-----7-894-856317294927684531134952678715893462348276159692541387483169725279435816561728943 +-51-3-7-279---2-8----17--3--4-3-6---9-75--123-2-7--654-1-64-3-8-7-8----5----5791-451938762793462581286175439145326897967584123328791654512649378679813245834257916 +--34--1---1-59---3-2471-5---69-----415-674-9----932---8---459----628--1----16-83-593428176718596243624713589369851724152674398487932651871345962936287415245169837 +-7384---2-8--5----4-92-61-3---7--5----416-----1---8736--891-46---76--8-9-91-8-327173849652286351974459276183862793541734165298915428736328917465547632819691584327 +46-72-1--78-1--42---25-4--------894--7--153---486935-1----4--638-6-57---9-4-6--1-463729158785136429192584637351278946679415382248693571527941863816357294934862715 +-4-6-------374-5--612---34--8--37--5----2893-93----87-168--9--342-371-5--7----491547613289893742516612895347284937165756128934931564872168459723429371658375286491 +7-643------98-26--3------5-84-----7-12-39-58669-1784-3---7-3-95--8-2----9-----3-2756439218419852637382617954843265179127394586695178423261743895538926741974581362 +7-6--8----8957-3----319-7-8-7-----4596--15----5--27--34257-163-1-----2-763-9--4--716238594289574316543196728372869145964315872851427963425781639198643257637952481 +-1--684--5247-1---76834-12--9--73--6--75-----3----6798-------6---9--7-428461---75913268457524791683768345129492873516687519234351426798275984361139657842846132975 +7-------5-91-857--4-572-86-1---------56--2--82-36581--3-42-6--78--513-96----74--3782461935691385724435729861148937652956142378273658149314296587827513496569874213 +2-5--7-----931-7-2-7142---959-1----83-----2-68642-3-9515--34----4--629---2----48-235697814489315762671428539592146378317589246864273195158934627743862951926751483 +----1-5-44815---27596------75--9---21----29--82---71-6-17--5-3-24-9-3---63-2--4--372618594481539627596724813754196382163842975829357146917465238245983761638271459 +867---------2--61-912--6-5414--38---2-5-6---337---41---2--4-8--65-8---4-49--5-27-867415932534297618912386754146938527285761493379524186723149865651872349498653271 +-6-3---9-9--51---623-49--5---7-35-1--8-27-9-----98-67-5-98431-----162---81------3165327498974518326238496751497635812681274935352981674529843167743162589816759243 +----625-7--81----6672-4-83-1-59-746--6---4----9-6-----71-32---452---137-84---62-9931862547458173926672549831185937462367284195294615783719328654526491378843756219 +---7----592---86--1-5---8-3---3---195-6987-24493-51-6835-67------2---9------25--6834716295927538641165492873278364519516987324493251768351679482682143957749825136 +1---62-5-7------8----4-------169853-987--516-6-571---8218937----69541-7----2--9--143862759792153486856479321421698537987325164635714298218937645369541872574286913 +17-----824-6--73-5--2--86---6-8-1-9-8-9-72--4-4----7-879418-2-------543-325--98--173654982486297315952318647267841593839572164541936728794183256618725439325469871 +-----5--65-86--19--6482-5-3--------14----825-8251--7-4751-92--83-6--7---24---63-7132975486578643192964821573697254831413768259825139764751392648386417925249586317 +61-28-75------56---85-6--43--1--8-25------4--258-4-19-5493-28--1-2--697--67--9---613284759724935618985167243471698325396521487258743196549372861132856974867419532 +--9--38-4-3--9---5-57-4-9-16---8-54--2-9--6-7---36-2-99-48--152------378-82--5---169573824438192765257648931691287543823954617745361289974836152516429378382715496 +-235-4-----56-24---7--98-2---7481-59---763---8-----7-4-5-8--3--3--9-7--5-9-1-58-6123574968985612473674398521237481659549763182816259734451826397368947215792135846 +32----8---8-3-5-24--4-216--5--21-4388-7--9---2----457979---3-866----2--5-52---31-325746891186395724974821653569217438847539162213684579791453286638172945452968317 +-163-52------8----4-2--15-82-1--98-45-4--8--33--6-41521-38-69-----4-7---9--213-86816345279735982641492761538271539864564128793389674152143856927628497315957213486 +2-9--17-3---3--2--3--5--986--89635---3-7-----7-28456319-5--8-67--7------6--1-7829259681743864379215371524986418963572536712498792845631925438167187296354643157829 +6---781-93----48-----65--344-6-32--753-8---2----7--6-32--4193--9-----71--532674--642378159315924876789651234496132587537846921821795643278419365964583712153267498 +--15--43-24836-1--5-39--82----73-----561829--9--65-21-8--41--62-----5---4-52--38-791528436248367195563941827124739658356182974987654213879413562632895741415276389 +9----63-882-4356--7--9-85-429-7--4-618-------654-927--36-5---42--9------4--3-98-5945176328821435697736928514293751486187643259654892731368517942579284163412369875 +2357-4--8------4---84--26-58172963-46--5812-----4---1-4-1--75-33--1-----5286----1235764198169358427784912635817296354643581279952473816491827563376145982528639741 +5----9762--7-8-----6-7-5--979235-14--1-9-23--843-71---239-1--78-------9---6--7-34581439762927186453364725819792358146615942387843671925239514678478263591156897234 +----138--12-8-7-----3----5--41---26-35--6--8----7--1--592-416-8---2--5-4-185769-3975613842124857396683429751841935267357162489269784135592341678736298514418576923 +47-3---1-6-------7---17-628--9--1856148-3-7-2---798--4-14--3--5-2591-------54-9-1472386519681259347593174628739421856148635792256798134914863275825917463367542981 +--5---7--23-7-6-5-7-9-51-68--2------87-1--6--4-1687-2-9-481-5-----9-54----64728-3165298734238746951749351268692534187873129645451687329924813576387965412516472893 +-1-76-3--9--4528----738-4-914--23--7--617-2--7--8-4---43----6-5-9-5--748-7-2-8---814769352963452871527381469149623587386175294752894136438917625291536748675248913 +-8----2--3-4-51------48---1-----5--42-87--1--94----7-341-572-8663294--17-----3-92187369245394251678526487931763195824258734169941826753419572386632948517875613492 +5--3--46--1--7-------59-8-78-7-53249145-267-3-9--48------4----6-31---97-7-6-3----579382461318674592624591837867153249145926783293748615982417356431265978756839124 +75-21936-86935-41---2---79-54-----3--83-25------4----9--8----5-6---94--3---57--26754219368869357412132648795546981237983725641217436589378162954625894173491573826 +39---74---14--936-758-36-9-475-6---9-----164--2----7--56-1-38-48---7591-----9--2-396217458214589367758436291475362189983751642621948735569123874842675913137894526 +-6-2--49-8---7-2-352----7-8-8--5---4-9--438-1--5--86-9--4-67---6--8-9147------536763281495849675213521394768187956324296743851435128679314567982652839147978412536 +23--8-4-5------2--56872---37138496-2--45138-----2---1-1-2-7-5-6--61-----3854----1237981465941635287568724193713849652624513879859267314192378546476152938385496721 +--6239-145-3---7----45769---3-49-6-29----814--42--3---46-1-2-----8-6-4---7538-2--786239514593841726214576938831495672957628143642713895469152387328967451175384269 +8932-51--641----------13--97-4-3-2--95-7---1-3-2---67-1-5--2--62-8-94-5---9-6--2-893245167641987532527613489784136295956728314312459678135872946268394751479561823 +7---9------6--83-14-8----7586-7-514223-819-6715--4----6--3-7------96-7-4--1----3-713596428526478391498123675869735142234819567157642983645387219382961754971254836 +-----3-9-78---5-6---2------348-51--65---68349-693---85914---673--7-4----625--98-4156823497783495261492617538348951726571268349269374185914582673837146952625739814 +52316---7847---------3-72--3-6----9821--9--7-9-4--3--67-16--8--6-54-2-1---2--8-6-523164987847925631169387254376241598218596473954873126731659842685432719492718365 +-64-58--1--1-43-65---6--4-723-----4--1-49--3----38-57---2--571--49---8--1-78-4293964758321871243965523619487238576149715492638496381572682935714349127856157864293 +-5-3-4217--48-1-65--2-79-38---9-----9-7-46--3----871-----738-91----9--843-841--2-859364217734821965612579438481953672927146853563287149246738591175692384398415726 +--5-9----6--2---182-3---54--624-5371-41-3-----8791265---65-8-------69-351-----8--815694723674253918293871546962485371541736289387912654436528197728169435159347862 +-7--6----63-49--8-8-52-3-4121-6--5-35--9---26-6-12-89415-3-------6-1-4599-------7471568932632491785895273641219684573548937126763125894157349268386712459924856317 +-1-8-----987---136325-9--4864827---3-93--64-22--34-869-5-----------6-9--1-4-2-3--416837295987452136325691748648279513593186472271345869859713624732564981164928357 +1--98-3--932-674--58-2-379-2598-------------56-84---79---7-9--1-936---8--6-3-8-24176984352932567418584213796259876143347192865618435279825749631493621587761358924 +-73--48-5--2--63--41-----62-69-42--73--6-1-9-7-----4-694716-2-------578-285--96--673294815592816374418357962869542137324671598751983426947168253136425789285739641 +-584----9-49----5326--5--1---63---9--12--93---976-8-2-674-92--1---1-46--831------758413269149286753263957814586321497412579386397648125674892531925134678831765942 +---4-56-119----5-465---1-8------64727652-98--4--713-5----3972-5-26-5---8--3--2---238475691197638524654921387319586472765249813482713956841397265926154738573862149 +8-62--4717-5--8-3291---4-65--9------38--91--615----7--561----97-9-----58-78-65-4-836259471745618932912734865629587314387491526154326789561843297493172658278965143 +4-------6-1-25--9-79--84----5---2------47-6-11-----24-9-153748-5-8-----73-2-41965425793816813256794796184523654912378239478651187365249961537482548629137372841965 +9--4--6314-6-1--9--7---6---3-9-5-2--56-782----426935-86----1-82-5-329-76--------9928475631436218795175936824389154267561782943742693518697541382854329176213867459 +-84-9-5-73-765-19-9----7----76--5--11-2-4-7654-------------29-32-3--9-165--8-6472684193527327654198951287634876925341192348765435761289768412953243579816519836472 +52-6-1---------9363--74--5-4-5-9---78-7---125--285--49------4-86-498357----4-5-93529631784741528936386749251435192867897364125162857349953276418614983572278415693 +-8--64-9596--57--8-259-----5-----37-7--51--8-2-674----8-2--6--3--4----511734-58-2387264195961357428425981637518629374749513286236748519852176943694832751173495862 +5-2--36-7---6----2--61--4952-79813649--546--1-6-7-----1-5--974-7----------43-7529512493687849675132376128495257981364938546271461732958125869743793254816684317529 +--256----15--72--497--4--25-----531--85--1-4--917-6---5-93184-6---9---587--6-4--3842569731153872964976143825267485319385291647491736582529318476634927158718654293 +1365----8---1---37725-48--6--7---8-2--948--5-58-6-27---42---68-6--8----997-2--1-3136579428894126537725348916417953862269487351583612794342791685651834279978265143 +-98-----5---397---1-42859-----81-36-6--5-4-9--8-67-1----194---3-5712--4--3--5--1-398461725526397481174285936745819362612534897983672154261948573857123649439756218 +-1---42786--1583-9489---5--5-7---1-4-6--157--1-4-37-----659--1----2--9--9-1---832315964278672158349489372561537629184268415793194837625826593417743281956951746832 +476-58-3--1-7-9-4-92-64-75-8--49-6-37-48--9-----57---1-893--5-7--------22679-----476258139518739246923641758851492673734816925692573481189324567345167892267985314 +4-8--61-5---5--8----53--429--96-12841--------3-4--2-91-5-1-----2--4953--8-1273956438926175792514863615387429579631284126849537384752691953168742267495318841273956 +-9-185-732--79---173--42--9--9218-3-32------41---6-----6-----9-94-5---185-283-4-7694185273285793641731642859459218736326957184178364925863471592947526318512839467 +--34-8---9--13-6---81-62--3-1-39-2----58764--3--24-8-5-28---9-7-5-7--384-3-6-4---673458129942137658581962743814395276295876431367241895428513967156729384739684512 +------7---5---6----1---2-8419-2--835--51-862-28-653--1-6-38-172---5243963-----4--942815763758436219613972584196247835435198627287653941564389172871524396329761458 +--129-6---8------7-564-8---93-8-176216-32548-42------5-1----89----58--71--2--9---341297658289653147756418923935841762167325489428976315514732896693584271872169534 +-----16--9--5--21-51-863-----9--4-5242--75---75-----84--5436--7-9--18523173--2---847291635936547218512863479369184752428375961751629384285436197694718523173952846 +-98-5-7-6---8------7--9215--3--182-5----46---8-1527--3-86-3-5-7--2-8-6-9957-61--2298154736615873924473692158734918265529346871861527493186239547342785619957461382 +1---7629-79--251--8-29------8756-----5-23--1--2-----54-18--74----6---32-5346-28-1145876293793425186862913547487561932659234718321789654218357469976148325534692871 +9-72--54--386--17-4---8736--2439-6-1---4---2-----2--9--85--42372---6---53-17-2---967231548538649172412587369724398651196475823853126794685914237279863415341752986 +92-1-6-47----3-8---4--5213-8------5-572-9---3-----179-254-693--36---5-7-1-7--396-923186547615437829748952136891374652572698413436521798254769381369815274187243965 +42-3--1788--------79--4---6---2----4614-8-5---8--54-6---7-18-3----43-2979-35-7481426395178835671942791842356579263814614789523382154769247918635158436297963527481 +----638-5-------6976-98523-5--62-3--------5873-1-74---63-8----292----143-1-39-6-8192463875853217469764985231579628314246139587381574926635841792928756143417392658 +297-5-6-----93-2--346-7--1583--924--7---8-5---145-7-----3-45-----86--15-65---37-4297451638581936247346278915835192476762384591914567382123745869478629153659813724 +39---5-8-51-2--7434-----------1---5--4--56--8875--46----3-47--29-263-574---5-2139397415286518269743426873915639128457241756398875394621153947862982631574764582139 +5-8--4-31---5-------3-7849---6-59-74----12---95-437--61-5--6-437----5-18384-91--7578924631419563782623178495236859174847612359951437826195786243762345918384291567 +-----7-14749-1-3-2618-------932---76-7-3---8--21--8--3-3458----2-71--5---65-3--4-352967814749815362618423795893251476576394281421678953134589627287146539965732148 +-3-17--45-9--46-287-4--2-319------6-61-92-5-3-2-----9---278-4193----98---49-51---236178945195346728784592631953817264617924583428635197562783419371469852849251376 +2--1659--81----5--6--798-42--82-96-7-9--3-42-7-2-8-----2647----3----62--1-58-37--274165983819342576653798142438219657591637428762584391926471835387956214145823769 +82-4-3-69-516--2-46---2----5---------32-4-9--97-5--432----7-8-64---3172578--6-39-827453169351689274694127583548392617132746958976518432213975846469831725785264391 +-93--6---1-89--367------9425---9--86-3-1--4-5---25-1-3-64-8-7-1--1--28-9-2--1-6--293476518148925367675831942517394286932168475486257193364589721751642839829713654 +--5---49----432---4--915-78-8-5--3---7-18-52---374--8-36-89-----4--57--68--26-9--215678493798432165436915278189526347674183529523749681361894752942357816857261934 +87--13--2--972--1--2-6-8-----236--84-4-851-6-7--29--3-4--5--82638-----952--1-6---874913652639725418521648379152367984943851267768294531417539826386472195295186743 +-968-7---4--9-3--7-37----69-19---852--413-9------5---1261-----3-4-392-189----6725196827534452963187837541269319674852524138976678259341261785493745392618983416725 +1247965-3968--1--4--5----9---16489----9--3---7--9-53-84--56-8-1--------55-78346--124796583968351274375482196231648957859173462746925318492567831683219745517834629 +3-67----4--438-5695-92--7-34-587-2-----13------2495-7885-64-9-------7---9-35--6-7386759124274381569519264783435876291798132456162495378857643912641927835923518647 +------593-7-25------54-8271-3-8--4-54--97-1-88---3-7--6-3---8-2--8-2-9-65----6-47284617593179253684365498271937861425456972138821534769613749852748325916592186347 +--7--4---394---587816-3-42-542-968--6---28354-835--6-272--6---8--1----------5---3257984136394612587816735429542396871679128354183547692725463918431879265968251743 +---3---4-7-6-28----83---7-----81-25--5--43-7-6-----8---182945-746578-9-32-----4-1129375648746128395583469712934817256852643179671952834318294567465781923297536481 +6--4-2-97-549--632-297634----3-----8---698753-7-32-914-4---982--6---7-----------1638452197754981632129763485593174268412698753876325914347519826261837549985246371 +---5-41--6--1739-81-592-4--3--------2-1697-3--792--6--4-----7---837-6-4-5-7-42-69798564123624173958135928476356481297241697835879235614462359781983716542517842369 diff --git a/apps/swp2clk/ChangeLog b/apps/swp2clk/ChangeLog index d6f9f6e8c..975da5800 100644 --- a/apps/swp2clk/ChangeLog +++ b/apps/swp2clk/ChangeLog @@ -2,3 +2,4 @@ 0.02: Fix deleting from white and black lists. 0.03: Adapt to availability of Bangle.showClock and Bangle.load 0.04: Fix 'Uncaught ReferenceError: "__FILE__" is not defined' error (fix #2326) +0.05: Fix settings didn't call the Storage class correctly. diff --git a/apps/swp2clk/metadata.json b/apps/swp2clk/metadata.json index b4436bd39..a4ef71488 100644 --- a/apps/swp2clk/metadata.json +++ b/apps/swp2clk/metadata.json @@ -2,7 +2,7 @@ "id": "swp2clk", "name": "Swipe back to the Clock", "shortName": "Swipe to Clock", - "version": "0.04", + "version": "0.05", "description": "Let's you swipe from left to right on any app to return back to the clock face. Please configure in the settings app after installing to activate, since its disabled by default.", "icon": "app.png", "type": "bootloader", diff --git a/apps/swp2clk/settings.js b/apps/swp2clk/settings.js index 4f8db5eb8..0503a455a 100644 --- a/apps/swp2clk/settings.js +++ b/apps/swp2clk/settings.js @@ -150,10 +150,10 @@ }; var getAppList = () => { - var appList = storage + var appList = require("Storage") .list(/\.info$/) .map((appInfoFileName) => { - var appInfo = storage.readJSON(appInfoFileName, 1); + var appInfo = require("Storage").readJSON(appInfoFileName, 1); return ( appInfo && { name: appInfo.name, @@ -180,4 +180,4 @@ settings = readSettings(); showMainMenu(); -}); +}) diff --git a/apps/taglaunch/settings.js b/apps/taglaunch/settings.js index 94ec34a36..b85ff65f6 100644 --- a/apps/taglaunch/settings.js +++ b/apps/taglaunch/settings.js @@ -34,4 +34,4 @@ } }; E.showMenu(appMenu); -}); +}) diff --git a/apps/thunder/settings.js b/apps/thunder/settings.js index 1a2959227..90e3c4231 100644 --- a/apps/thunder/settings.js +++ b/apps/thunder/settings.js @@ -37,4 +37,4 @@ loadSettings(); showMenu(); -}); +}) diff --git a/apps/timecal/timecal.settings.js b/apps/timecal/timecal.settings.js index 3a38eed09..d7824815a 100644 --- a/apps/timecal/timecal.settings.js +++ b/apps/timecal/timecal.settings.js @@ -104,4 +104,4 @@ }; showMainMenu(); -}); +}) diff --git a/apps/timerclk/settings.js b/apps/timerclk/settings.js index 1a8500add..0f4acfc70 100644 --- a/apps/timerclk/settings.js +++ b/apps/timerclk/settings.js @@ -312,4 +312,4 @@ } }; E.showMenu(mainMenu); -}); +}) diff --git a/apps/timestamplog/settings.js b/apps/timestamplog/settings.js index 137ed31db..ffdaa4665 100644 --- a/apps/timestamplog/settings.js +++ b/apps/timestamplog/settings.js @@ -4,4 +4,4 @@ const tsl = require('timestamplog'); function(backCb) { tsl.launchSettingsMenu(backCb); } -); +) diff --git a/apps/tinyheads/ChangeLog b/apps/tinyheads/ChangeLog index 1a3bc1757..581b18b40 100644 --- a/apps/tinyheads/ChangeLog +++ b/apps/tinyheads/ChangeLog @@ -1 +1,2 @@ 0.01: New app! +0.02: Make the widget bar the same colour as the hair. \ No newline at end of file diff --git a/apps/tinyheads/app.js b/apps/tinyheads/app.js index a3f79c081..fb5c1a9cb 100644 --- a/apps/tinyheads/app.js +++ b/apps/tinyheads/app.js @@ -5,8 +5,7 @@ // Read 12/24 from system settings const is12Hour=(require("Storage").readJSON("setting.json",1)||{})["12hour"] || false; - // Tinyhead features are stored at a resolution of 18x21, this scales them to the best fit for the Banglejs2 screen - const scale=9; + const scale=lib.appScale; const closedEyes = 25; const scaredEyes = 26; @@ -22,7 +21,8 @@ let helpShown = false; let tapCount = 0; let centerX, centerY, minuteHandLength, hourHandLength, handOutline; - + let originalTheme = Object.assign({}, g.theme); + // Open the eyes and schedule the next blink let blinkOpen = function blinkOpen() { if (blinkTimeout) clearTimeout(blinkTimeout); @@ -214,6 +214,9 @@ }; let init = function init() { + // change the system theme, so that the widget bar blends in with the clock face + g.setTheme({bg:lib.settings.hairColour,fg:lib.settings.faceColour,dark:true}).clear(); + Bangle.on('lock', lockHandler); Bangle.on('charging', chargingHandler); if (lib.settings.btStatusEyes) { @@ -234,6 +237,7 @@ // Go direct to feature select in settings on long screen press if (xy.type == 2) { eval(require("Storage").read("tinyheads.settings.js"))(()=> { + g.setTheme(originalTheme); E.showMenu(); init(); }, true, helpShown); @@ -251,6 +255,7 @@ } }, remove: function() { + g.setTheme(originalTheme); // Clear timeouts and listeners for fast loading if (drawTimeout) clearTimeout(drawTimeout); if (blinkTimeout) clearTimeout(blinkTimeout); diff --git a/apps/tinyheads/lib.js b/apps/tinyheads/lib.js index cbb139d8c..6ed42f2f4 100644 --- a/apps/tinyheads/lib.js +++ b/apps/tinyheads/lib.js @@ -5,6 +5,14 @@ exports.maxEyes = 25; exports.faceW = 18; exports.faceH = 21; +// Scale used when showing the main clock screen. +// Tinyhead features are stored at a resolution of 18x21, this scales them to the best fit for the Banglejs2 screen +exports.appScale=9; + +// Scale used when showing the face on the settings page. +// It's smaller than on the clock itself, so that selection arrows can be shown down the sides +exports.settingsScale=6; + exports.settingsFile = 'tinyheads.json'; let faceCanvas; @@ -135,7 +143,14 @@ exports.drawFace = function(scale, eyesNum, mouthNum, peek, offset) { // Draw face let xOffset = (g.getWidth() - (exports.faceW * scale)) / 2; let yOffset = (offset ? offset : 0) + ((g.getHeight() - (exports.faceH * scale)) / 2); - g.setBgColor(0, 0, 0); + + // On the main screen, if the widgets are displayed, the background color matches the color of the hair and widget bar + if (scale == exports.appScale && (exports.settings.showWidgets == 'on' || (exports.settings.showWidgets == 'unlock' && !Bangle.isLocked()))) { + g.setBgColor(exports.settings.hairColour); + } else { + g.setBgColor(0, 0, 0); + } + g.clearRect(Bangle.appRect); g.setClipRect(Bangle.appRect.x, Bangle.appRect.y, Bangle.appRect.x2, Bangle.appRect.y2); diff --git a/apps/tinyheads/metadata.json b/apps/tinyheads/metadata.json index 3f0e7abf9..6ab060416 100644 --- a/apps/tinyheads/metadata.json +++ b/apps/tinyheads/metadata.json @@ -4,7 +4,7 @@ "shortName":"Tinyheads", "icon": "app.png", "screenshots" : [ { "url":"tinyhead1.png" }, {"url":"tinyhead2.png"}, {"url":"tinyhead3.png"}, {"url":"tinyhead4.png"}, {"url":"editing.png"} ], - "version":"0.01", + "version":"0.02", "description": "Choose from a variety of hairstyles, eyes, noses, and mouths to customize your pixel art style Tinyhead.", "readme":"README.md", "type": "clock", diff --git a/apps/tinyheads/settings.js b/apps/tinyheads/settings.js index 38558bbc2..e9f8bdb2f 100644 --- a/apps/tinyheads/settings.js +++ b/apps/tinyheads/settings.js @@ -6,7 +6,7 @@ let featureColour = 'faceColour'; let colourSelectTimeout; - let scale = 6; // Smaller scale than on the clock itself, so that selection arrows can be shown down the sides + const scale = lib.settingsScale; // 27 colours let colours = [ diff --git a/apps/toucher/settings.js b/apps/toucher/settings.js index f3004000a..a8a17125c 100644 --- a/apps/toucher/settings.js +++ b/apps/toucher/settings.js @@ -54,4 +54,4 @@ }, '< Back': back }); -}); +}) diff --git a/apps/touchtimer/settings.js b/apps/touchtimer/settings.js index d3de4e6d3..4a7464f06 100644 --- a/apps/touchtimer/settings.js +++ b/apps/touchtimer/settings.js @@ -81,4 +81,4 @@ settings = readSettings(); showMainMenu(); -}); +}) diff --git a/apps/trail/ChangeLog b/apps/trail/ChangeLog index 097869de3..ae1c44012 100644 --- a/apps/trail/ChangeLog +++ b/apps/trail/ChangeLog @@ -1,3 +1,4 @@ 0.01: New App! 0.10: Redesign to make screen updates fast +0.11: bugfix (demo mode was enabled by default) diff --git a/apps/trail/metadata.json b/apps/trail/metadata.json index 3cb9672c7..0981d6dbe 100644 --- a/apps/trail/metadata.json +++ b/apps/trail/metadata.json @@ -1,6 +1,6 @@ { "id": "trail", "name": "Trail Rail", - "version":"0.10", + "version":"0.11", "description": "Follow a GPX track in car or on bike", "icon": "app.png", "readme": "README.md", diff --git a/apps/trail/trail.app.js b/apps/trail/trail.app.js index aa06d9e1d..601a9f089 100644 --- a/apps/trail/trail.app.js +++ b/apps/trail/trail.app.js @@ -134,7 +134,6 @@ let gps = { init: function(x) { this.emulator = (process.env.BOARD=="EMSCRIPTEN" || process.env.BOARD=="EMSCRIPTEN2")?1:0; - this.emulator = 1; // FIXME }, state: {}, on_gps: function(f) { @@ -602,7 +601,7 @@ function step_to(pp, pass_all) { return quiet; } -var demo_mode = 0; //fixme +var demo_mode = 0; function step() { const fast = 0; @@ -666,7 +665,7 @@ function step() { drop_last(); let v2 = getTime(); print("Step took", (v2-v1), "seconds"); - setTimeout(step, 10); /* FIXME! */ + setTimeout(step, 1000); } function recover() { diff --git a/apps/trex/settings.js b/apps/trex/settings.js index 67aa9a518..f435b0ba4 100644 --- a/apps/trex/settings.js +++ b/apps/trex/settings.js @@ -16,4 +16,4 @@ } }; E.showMenu(menu); -}); +}) diff --git a/apps/usgs/settings.js b/apps/usgs/settings.js index 8a12197dc..710d071ea 100644 --- a/apps/usgs/settings.js +++ b/apps/usgs/settings.js @@ -19,6 +19,9 @@ } function popSubMenu() { + // FIXME: Linter complains that `data` is not defined. When I defined it + // instead complained that it's not used (no-unused-vars). + // Also looking at `getDataStreams` it doesn't return anything so this seems wrong. data = getDataStreams(); } function popSubMenuData(data) { @@ -78,4 +81,4 @@ function getDataStreams() { // Show the menu E.showMenu(menu); -}); +}) diff --git a/apps/weatherClock/settings.js b/apps/weatherClock/settings.js index 0aa7330c1..8a2127ba7 100644 --- a/apps/weatherClock/settings.js +++ b/apps/weatherClock/settings.js @@ -55,4 +55,4 @@ }, } }); -}); +}) diff --git a/apps/wid_edit/settings.js b/apps/wid_edit/settings.js index a632850d6..cf445fe48 100644 --- a/apps/wid_edit/settings.js +++ b/apps/wid_edit/settings.js @@ -194,4 +194,4 @@ E.showMenu(menu); } mainMenu(); -}); +}) diff --git a/apps/widalarmeta/settings.js b/apps/widalarmeta/settings.js index 8dd0d81fa..5e83c6f3a 100644 --- a/apps/widalarmeta/settings.js +++ b/apps/widalarmeta/settings.js @@ -60,4 +60,4 @@ } }, }); -}); +}) diff --git a/apps/widbaroalarm/settings.js b/apps/widbaroalarm/settings.js index 68e7bab6b..4762e1e80 100644 --- a/apps/widbaroalarm/settings.js +++ b/apps/widbaroalarm/settings.js @@ -155,4 +155,4 @@ } showMainMenu(); -}); +}) diff --git a/apps/widbatpc/ChangeLog b/apps/widbatpc/ChangeLog index e9326fd2c..34873124c 100644 --- a/apps/widbatpc/ChangeLog +++ b/apps/widbatpc/ChangeLog @@ -17,3 +17,4 @@ Add option to disable vibration when charger connects 0.18: Only redraw when values change 0.19: Match draw() API e.g. to allow wid_edit to alter this widget +0.20: Remove clear of the screen on reload (will break currently running app) \ No newline at end of file diff --git a/apps/widbatpc/metadata.json b/apps/widbatpc/metadata.json index 4c364a864..5e160b5d4 100644 --- a/apps/widbatpc/metadata.json +++ b/apps/widbatpc/metadata.json @@ -2,7 +2,7 @@ "id": "widbatpc", "name": "Battery Level Widget (with percentage)", "shortName": "Battery Widget", - "version": "0.19", + "version": "0.20", "description": "Show the current battery level and charging status in the top right of the clock, with charge percentage", "icon": "widget.png", "type": "widget", diff --git a/apps/widbatpc/widget.js b/apps/widbatpc/widget.js index 7ba060d6d..163c0f541 100644 --- a/apps/widbatpc/widget.js +++ b/apps/widbatpc/widget.js @@ -156,7 +156,6 @@ // need to redraw all widgets, because changing the "charger" setting // can affect the width and mess with the whole widget layout setWidth(); - g.clear(); Bangle.drawWidgets(); } diff --git a/apps/widbatwarn/settings.js b/apps/widbatwarn/settings.js index c3464a82b..352dca467 100644 --- a/apps/widbatwarn/settings.js +++ b/apps/widbatwarn/settings.js @@ -42,4 +42,4 @@ }, }; E.showMenu(menu); -}); \ No newline at end of file +}) diff --git a/apps/widbgjs/settings.js b/apps/widbgjs/settings.js index c599183dc..077efaf8a 100644 --- a/apps/widbgjs/settings.js +++ b/apps/widbgjs/settings.js @@ -50,4 +50,4 @@ }, '< Back': back, }); -}); +}) diff --git a/apps/widbt_notify/ChangeLog b/apps/widbt_notify/ChangeLog index d48cdca63..74f8515db 100644 --- a/apps/widbt_notify/ChangeLog +++ b/apps/widbt_notify/ChangeLog @@ -13,4 +13,5 @@ 0.14: Added configuration option 0.15: Added option to hide widget when connected 0.16: Simplify code, add option to disable displaying a message -0.17: Minor display fix \ No newline at end of file +0.17: Minor display fix +0.18: Use notify lib to stop this widget clearing the screen of the running clock/app \ No newline at end of file diff --git a/apps/widbt_notify/metadata.json b/apps/widbt_notify/metadata.json index 5e3f15af2..4eda86941 100644 --- a/apps/widbt_notify/metadata.json +++ b/apps/widbt_notify/metadata.json @@ -1,13 +1,14 @@ { "id": "widbt_notify", "name": "Bluetooth Widget with Notification", - "version": "0.17", + "version": "0.18", "description": "Show the current Bluetooth connection status with some optional features: show message, buzz on connect/loss, hide always/if connected.", "icon": "widget.png", "type": "widget", "tags": "widget,bluetooth", "provides_widgets" : ["bluetooth"], "supports": ["BANGLEJS","BANGLEJS2"], + "dependencies" : { "notify":"module" }, "storage": [ {"name":"widbt_notify.wid.js","url":"widget.js"}, {"name":"widbt_notify.settings.js","url":"settings.js"} diff --git a/apps/widbt_notify/widget.js b/apps/widbt_notify/widget.js index 4730db5b5..bde218236 100644 --- a/apps/widbt_notify/widget.js +++ b/apps/widbt_notify/widget.js @@ -1,6 +1,6 @@ -(function() { +{ // load settings - var settings = Object.assign({ + let settings = Object.assign({ showWidget: true, buzzOnConnect: true, buzzOnLoss: true, @@ -10,7 +10,7 @@ }, require("Storage").readJSON("widbt_notify.json", true) || {}); // setup widget with to hide if connected and option set - var widWidth = settings.hideConnected && NRF.getSecurityStatus().connected ? 0 : 15; + let widWidth = settings.hideConnected && NRF.getSecurityStatus().connected ? 0 : 15; // write widget with loaded settings WIDGETS.bluetooth_notify = Object.assign(settings, { @@ -31,24 +31,13 @@ g.drawImage(atob("CxQBBgDgFgJgR4jZMawfAcA4D4NYybEYIwTAsBwDAA=="), 2 + this.x, 2 + this.y); } } else { - // g.setColor(g.theme.dark ? "#666" : "#999"); + // g.setColor(g.theme.dark ? "#666" : "#999"); g.setColor("#f00"); // red is easier to distinguish from blue g.drawImage(atob("CxQBBgDgFgJgR4jZMawfAcA4D4NYybEYIwTAsBwDAA=="), 2 + this.x, 2 + this.y); } } }, - redrawCurrentApp: function() { - if (typeof(draw) == 'function') { - g.reset().clear(); - draw(); - Bangle.loadWidgets(); - Bangle.drawWidgets(); - } else { - load(); // fallback. This might reset some variables - } - }, - onNRF: function(connect) { // setup widget with and reload widgets to show/hide if hideConnected is enabled if (this.hideConnected) { @@ -61,10 +50,10 @@ if (this.warningEnabled) { if (this.showMessage) { - E.showMessage( /*LANG*/ 'Connection\n' + (connect ? /*LANG*/ 'restored.' : /*LANG*/ 'lost.'), 'Bluetooth'); + require("notify").show({id:"widbtnotify", title:"Bluetooth", body:/*LANG*/ 'Connection\n' + (connect ? /*LANG*/ 'restored.' : /*LANG*/ 'lost.')}); setTimeout(() => { - WIDGETS.bluetooth_notify.redrawCurrentApp(); - }, 3000); // clear message - this will reload the widget, resetting 'warningEnabled'. + require("notify").hide({id:"widbtnotify"}); + }, 3000); } this.warningEnabled = 0; @@ -87,4 +76,4 @@ NRF.on('connect', (addr) => WIDGETS.bluetooth_notify.onNRF(addr)); NRF.on('disconnect', () => WIDGETS.bluetooth_notify.onNRF()); -})() \ No newline at end of file +} \ No newline at end of file diff --git a/apps/widdst/settings.js b/apps/widdst/settings.js index 0017cc499..5ed8b0a37 100644 --- a/apps/widdst/settings.js +++ b/apps/widdst/settings.js @@ -190,4 +190,4 @@ E.showMenu(dstMenu); -}); +}) diff --git a/apps/widgps/settings.js b/apps/widgps/settings.js index 7a1c186c9..8f1d99f06 100644 --- a/apps/widgps/settings.js +++ b/apps/widgps/settings.js @@ -29,4 +29,4 @@ var mainmenu = { }, }; E.showMenu(mainmenu); -}); \ No newline at end of file +}) diff --git a/apps/widhrm/settings.js b/apps/widhrm/settings.js index 0b8c989ac..1c696939b 100644 --- a/apps/widhrm/settings.js +++ b/apps/widhrm/settings.js @@ -33,4 +33,4 @@ }, }; E.showMenu(menu); -}); +}) diff --git a/apps/widmessages/ChangeLog b/apps/widmessages/ChangeLog index 314b1490c..35bde7bbd 100644 --- a/apps/widmessages/ChangeLog +++ b/apps/widmessages/ChangeLog @@ -4,4 +4,5 @@ 0.03: Fix messages not showing if UI auto-open is disabled 0.04: Now shows message icons again (#2416) 0.05: Match draw() API e.g. to allow wid_edit to alter this widget -0.06: Fix bug that meant that only one widget was shown (now 3 unless changed in Settings->Apps->Messages->Widget messages) \ No newline at end of file +0.06: Fix bug that meant that only one widget was shown (now 3 unless changed in Settings->Apps->Messages->Widget messages) +0.07: Only load messages module if we have messages (30ms speed improvement) \ No newline at end of file diff --git a/apps/widmessages/metadata.json b/apps/widmessages/metadata.json index 436b77d3e..95a34e892 100644 --- a/apps/widmessages/metadata.json +++ b/apps/widmessages/metadata.json @@ -1,7 +1,7 @@ { "id": "widmessages", "name": "Message Widget", - "version": "0.06", + "version": "0.07", "description": "Widget showing new messages", "icon": "app.png", "type": "widget", diff --git a/apps/widmessages/widget.js b/apps/widmessages/widget.js index 0351fbead..3212ee649 100644 --- a/apps/widmessages/widget.js +++ b/apps/widmessages/widget.js @@ -65,7 +65,7 @@ if ((require("Storage").readJSON("messages.settings.json", true) || {}).maxMessa this.onMsg("show", {}); // reload messages+redraw } }; - Bangle.on("message", WIDGETS["messages"].onMsg.bind(WIDGETS["messages"])); - WIDGETS["messages"].onMsg("init", {}); // abuse type="init" to prevent Bangle.drawWidgets(); + if (require("Storage").read("messages.json")!==undefined) // only call init if we've got messages - otherwise we can avoid loading messages lib (saves 30ms) + WIDGETS["messages"].onMsg("init", {}); // abuse type="init" to prevent Bangle.drawWidgets(); } \ No newline at end of file diff --git a/apps/widmp/settings.js b/apps/widmp/settings.js index a389f7918..e671d9900 100644 --- a/apps/widmp/settings.js +++ b/apps/widmp/settings.js @@ -69,4 +69,4 @@ E.showMenu(mainmenu); -}); +}) diff --git a/apps/widshipbell/settings.js b/apps/widshipbell/settings.js index bb47e9b20..fa1ada630 100644 --- a/apps/widshipbell/settings.js +++ b/apps/widshipbell/settings.js @@ -24,4 +24,3 @@ }, }); }) - diff --git a/apps/widsleepstatus/settings.js b/apps/widsleepstatus/settings.js index da402e08e..e758a1760 100644 --- a/apps/widsleepstatus/settings.js +++ b/apps/widsleepstatus/settings.js @@ -29,4 +29,4 @@ }, }; E.showMenu(menu); -}); +}) diff --git a/bin/sanitycheck.js b/bin/sanitycheck.js index 82856b639..61a251bb5 100755 --- a/bin/sanitycheck.js +++ b/bin/sanitycheck.js @@ -5,6 +5,12 @@ var fs = require("fs"); var vm = require("vm"); var heatshrink = require("../webtools/heatshrink"); +/*var apploader = require("../core/lib/apploader.js"); +apploader.init({ + DEVICEID : "BANGLEJS2" +});*/ + + var acorn; try { acorn = require("acorn"); @@ -48,54 +54,56 @@ function WARN(msg, opt) { } /* These are errors that we temporarily allow */ var KNOWN_ERRORS = [ - "In locale en_CA, long date output must be shorter than 15 characters", - "In locale fr_FR, long date output must be shorter than 15 characters", - "In locale en_SE, long date output must be shorter than 15 characters", - "In locale en_NZ, long date output must be shorter than 15 characters", - "In locale en_AU, long date output must be shorter than 15 characters", - "In locale de_AT, long date output must be shorter than 15 characters", - "In locale en_IL, long date output must be shorter than 15 characters", - "In locale es_ES, long date output must be shorter than 15 characters", - "In locale fr_BE, long date output must be shorter than 15 characters", - "In locale fi_FI, long date output must be shorter than 15 characters", - "In locale de_CH, long date output must be shorter than 15 characters", - "In locale fr_CH, long date output must be shorter than 15 characters", - "In locale wae_CH, long date output must be shorter than 15 characters", - "In locale tr_TR, long date output must be shorter than 15 characters", - "In locale hu_HU, long date output must be shorter than 15 characters", - "In locale oc_FR, long date output must be shorter than 15 characters", - "In locale ca_ES, long date output must be shorter than 15 characters", - "In locale fr_BE, short month must be shorter than 5 characters", - "In locale fi_FI, short month must be shorter than 5 characters", - "In locale fr_CH, short month must be shorter than 5 characters", - "In locale oc_FR, short month must be shorter than 5 characters", - "In locale hr_HR, short month must be shorter than 5 characters", - "In locale ca_ES, short month must be shorter than 5 characters", - "In locale de_DE, meridian must be longer than 0 characters", - "In locale en_JP, meridian must be longer than 0 characters", - "In locale nl_NL, meridian must be longer than 0 characters", - "In locale fr_FR, meridian must be longer than 0 characters", - "In locale se_SE, meridian must be longer than 0 characters", - "In locale en_SE, meridian must be longer than 0 characters", - "In locale da_DK, meridian must be longer than 0 characters", - "In locale en_DK, meridian must be longer than 0 characters", - "In locale de_AT, meridian must be longer than 0 characters", - "In locale es_ES, meridian must be longer than 0 characters", - "In locale fr_BE, meridian must be longer than 0 characters", - "In locale it_CH, meridian must be longer than 0 characters", - "In locale it_IT, meridian must be longer than 0 characters", - "In locale wae_CH, meridian must be longer than 0 characters", - "In locale oc_FR, meridian must be longer than 0 characters", - "In locale pl_PL, meridian must be longer than 0 characters", - "In locale lv_LV, meridian must be longer than 0 characters", - "In locale nn_NO, meridian must be longer than 0 characters", - "In locale nb_NO, meridian must be longer than 0 characters", - "In locale ca_ES, meridian must be longer than 0 characters", - "In locale de_CH, meridian must be shorter than 4 characters", - "In locale hr_HR, meridian must be shorter than 4 characters", - "In locale sl_SI, meridian must be shorter than 4 characters", + "In locale en_CA, long date output must be shorter than 15 characters (Wednesday, September 10, 2024 -> 29)", + "In locale fr_FR, long date output must be shorter than 15 characters (10 septembre 2024 -> 17)", "In locale fr_FR, short month must be shorter than 5 characters", "In locale sv_SE, speed must be shorter than 5 characters", + "In locale en_SE, long date output must be shorter than 15 characters (September 10 2024 -> 17)", + "In locale en_NZ, long date output must be shorter than 15 characters (Wednesday, September 10, 2024 -> 29)", + "In locale en_AU, long date output must be shorter than 15 characters (Wednesday, September 10, 2024 -> 29)", + "In locale de_AT, long date output must be shorter than 15 characters (Donnerstag, 10. September 2024 -> 30)", + "In locale en_IL, long date output must be shorter than 15 characters (Wednesday, September 10, 2024 -> 29)", + "In locale es_ES, long date output must be shorter than 15 characters (miércoles, 10 de septiembre de 2024 -> 35)", + "In locale fr_BE, long date output must be shorter than 15 characters (dimanche septembre 10 2024 -> 26)", + "In locale fr_BE, short month must be shorter than 5 characters", + "In locale fr_BE, short month must be shorter than 5 characters", + "In locale fr_BE, short month must be shorter than 5 characters", + "In locale fr_BE, short month must be shorter than 5 characters", + "In locale fr_BE, short month must be shorter than 5 characters", + "In locale fi_FI, long date output must be shorter than 15 characters (keskiviikkona 10. maaliskuuta 2024 -> 34)", + "In locale fi_FI, short month must be shorter than 5 characters", + "In locale fi_FI, short month must be shorter than 5 characters", + "In locale fi_FI, short month must be shorter than 5 characters", + "In locale fi_FI, short month must be shorter than 5 characters", + "In locale fi_FI, short month must be shorter than 5 characters", + "In locale fi_FI, short month must be shorter than 5 characters", + "In locale fi_FI, short month must be shorter than 5 characters", + "In locale fi_FI, short month must be shorter than 5 characters", + "In locale fi_FI, short month must be shorter than 5 characters", + "In locale fi_FI, short month must be shorter than 5 characters", + "In locale fi_FI, short month must be shorter than 5 characters", + "In locale de_CH, meridian must be shorter than 4 characters", + "In locale de_CH, meridian must be shorter than 4 characters", + "In locale de_CH, long date output must be shorter than 15 characters (Donnerstag, 10. September 2024 -> 30)", + "In locale fr_CH, long date output must be shorter than 15 characters (dimanche 10 septembre 2024 -> 26)", + "In locale fr_CH, short month must be shorter than 5 characters", + "In locale fr_CH, short month must be shorter than 5 characters", + "In locale fr_CH, short month must be shorter than 5 characters", + "In locale fr_CH, short month must be shorter than 5 characters", + "In locale fr_CH, short month must be shorter than 5 characters", + "In locale wae_CH, long date output must be shorter than 15 characters (Sunntag, 10. Herbštmánet 2024 -> 29)", + "In locale tr_TR, long date output must be shorter than 15 characters (10 Haziran 2024 Pazartesi -> 25)", + "In locale hu_HU, long date output must be shorter than 15 characters (2024 Szep 10, Csütörtök -> 23)", + "In locale oc_FR, long date output must be shorter than 15 characters (divendres 10 setembre de 2024 -> 29)", + "In locale oc_FR, short month must be shorter than 5 characters", + "In locale oc_FR, short month must be shorter than 5 characters", + "In locale hr_HR, meridian must be shorter than 4 characters", + "In locale hr_HR, meridian must be shorter than 4 characters", + "In locale hr_HR, short month must be shorter than 5 characters", + "In locale sl_SI, meridian must be shorter than 4 characters", + "In locale sl_SI, meridian must be shorter than 4 characters", + "In locale ca_ES, long date output must be shorter than 15 characters (10 setembre 2024 -> 16)", + "In locale ca_ES, short month must be shorter than 5 characters", ]; /* These are warnings we know about but don't want in our output */ var KNOWN_WARNINGS = [ @@ -112,6 +120,104 @@ var KNOWN_WARNINGS = [ `In locale test, long time format might not work in some apps if it is not "%HH:%MM:%SS"`, `In locale wae_CH, short time format might not work in some apps if it is not "%HH:%MM"`, `In locale test, short time format might not work in some apps if it is not "%HH:%MM"`, + "App a_dndtoggle file a_dndtoggle.settings.js should be evaluated as a function but doesn't end in ')'", + "App activepedom file activepedom.settings.js should be evaluated as a function but doesn't end in ')'", + "App agpsdata file agpsdata.settings.js should be evaluated as a function but doesn't end in ')'", + "App alarm file alarm.settings.js should be evaluated as a function but doesn't end in ')'", + "App andark file andark.settings.js should be evaluated as a function but doesn't end in ')'", + "App antonclkplus file antonclkplus.settings.js should be evaluated as a function but doesn't end in ')'", + "App banglexercise file banglexercise.settings.js should be evaluated as a function but doesn't end in ')'", + "App barclock file barclock.settings.js should be evaluated as a function but doesn't end in ')'", + "App berlinc file berlinc.settings.js should be evaluated as a function but doesn't end in ')'", + "App bikespeedo file bikespeedo.settings.js should be evaluated as a function but doesn't end in ')'", + "App blc file blc.settings.js should be evaluated as a function but doesn't end in ')'", + "App boxclk file boxclk.settings.js should be evaluated as a function but doesn't end in ')'", + "App bthome file bthome.clkinfo.js should be evaluated as a function but doesn't end in ')'", + "App bthrm file bthrm.settings.js should be evaluated as a function but doesn't end in ')'", + "App carcrazy file carcrazy.settings.js should be evaluated as a function but doesn't end in ')'", + "App chimer file chimer.settings.js should be evaluated as a function but doesn't end in ')'", + "App circlesclock file circlesclock.settings.js should be evaluated as a function but doesn't end in ')'", + "App clicompleteclk file clicompleteclk.settings.js should be evaluated as a function but doesn't end in ')'", + "App clkinfocal file clkinfocal.settings.js should be evaluated as a function but doesn't end in ')'", + "App clkinfogps file gps.clkinfo.js should be evaluated as a function but doesn't end in ')'", + "App clkinfogpsspeed file clkinfogpsspeed.clkinfo.js should be evaluated as a function but doesn't end in ')'", + "App clkinfom file ram.clkinfo.js should be evaluated as a function but doesn't end in ')'", + "App clkinfomag file clkinfomag.clkinfo.js should be evaluated as a function but doesn't end in ')'", + "App clkinfostopw file stopw.clkinfo.js should be evaluated as a function but doesn't end in ')'", + "App clockcal file clockcal.settings.js should be evaluated as a function but doesn't end in ')'", + "App cogclock file cogclock.settings.js should be evaluated as a function but doesn't end in ')'", + "App counter2 file counter2.settings.js should be evaluated as a function but doesn't end in ')'", + "App cprassist file cprassist.settings.js should be evaluated as a function but doesn't end in ')'", + "App dane_tcr file dane_tcr.settings.js should be evaluated as a function but doesn't end in ')'", + "App dragboard file dragboard.settings.js should be evaluated as a function but doesn't end in ')'", + "App draguboard file draguboard.settings.js should be evaluated as a function but doesn't end in ')'", + "App drained file drained.settings.js should be evaluated as a function but doesn't end in ')'", + "App drinkcounter file drinkcounter.settings.js should be evaluated as a function but doesn't end in ')'", + "App dtlaunch file dtlaunch.settings.js should be evaluated as a function but doesn't end in ')'", + "App ffcniftyb file ffcniftyb.settings.js should be evaluated as a function but doesn't end in ')'", + "App folderlaunch file folderlaunch.settings.js should be evaluated as a function but doesn't end in ')'", + "App gassist file gassist.settings.js should be evaluated as a function but doesn't end in ')'", + "App gbmusic file gbmusic.settings.js should be evaluated as a function but doesn't end in ')'", + "App getup file getup.settings.js should be evaluated as a function but doesn't end in ')'", + "App gipy file gipy.settings.js should be evaluated as a function but doesn't end in ')'", + "App gpsrec file gpsrec.settings.js should be evaluated as a function but doesn't end in ')'", + "App gpssetup file gpssetup.settings.js should be evaluated as a function but doesn't end in ')'", + "App iconlaunch file iconlaunch.settings.js should be evaluated as a function but doesn't end in ')'", + "App infoclk file infoclk.settings.js should be evaluated as a function but doesn't end in ')'", + "App largeclock file largeclock.settings.js should be evaluated as a function but doesn't end in ')'", + "App launch file launch.settings.js should be evaluated as a function but doesn't end in ')'", + "App messagelist file messagelist.settings.js should be evaluated as a function but doesn't end in ')'", + "App messages file messages.settings.js should be evaluated as a function but doesn't end in ')'", + "App messages_light file messages_light.settings.js should be evaluated as a function but doesn't end in ')'", + "App messagesoverlay file messagesoverlay.settings.js should be evaluated as a function but doesn't end in ')'", + "App metronome file metronome.settings.js should be evaluated as a function but doesn't end in ')'", + "App multitimer file multitimer.settings.js should be evaluated as a function but doesn't end in ')'", + "App nesclock file nesclock.settings.js should be evaluated as a function but doesn't end in ')'", + "App nightwatch file nightwatch.settings.js should be evaluated as a function but doesn't end in ')'", + "App owmweather file owmweather.settings.js should be evaluated as a function but doesn't end in ')'", + "App pebble file pebble.settings.js should be evaluated as a function but doesn't end in ')'", + "App pebbled file pebbled.settings.js should be evaluated as a function but doesn't end in ')'", + "App pongclock file pongclock.settings.js should be evaluated as a function but doesn't end in ')'", + "App popconlaunch file popcon.settings.js should be evaluated as a function but doesn't end in ')'", + "App poweroff file poweroff.settings.js should be evaluated as a function but doesn't end in ')'", + "App puzzle15 file puzzle15.settings.js should be evaluated as a function but doesn't end in ')'", + "App qcenter file qcenter.settings.js should be evaluated as a function but doesn't end in ')'", + "App rebbleagenda file rebbleagenda.settings.js should be evaluated as a function but doesn't end in ')'", + "App recorder file recorder.clkinfo.js should be evaluated as a function but doesn't end in ')'", + "App rep file rep.settings.js should be evaluated as a function but doesn't end in ')'", + "App saclock file saclock.settings.js should be evaluated as a function but doesn't end in ')'", + "App sched file sched.settings.js should be evaluated as a function but doesn't end in ')'", + "App score file score.settings.js should be evaluated as a function but doesn't end in ')'", + "App sensortools file sensortools.settings.js should be evaluated as a function but doesn't end in ')'", + "App shadowclk file shadowclk.settings.js should be evaluated as a function but doesn't end in ')'", + "App shortcuts file shortcuts.settings.js should be evaluated as a function but doesn't end in ')'", + "App simplebgclock file simplebgclock.settings.js should be evaluated as a function but doesn't end in ')'", + "App slomoclock file slomoclock.settings.js should be evaluated as a function but doesn't end in ')'", + "App slopeclockpp file slopeclockpp.settings.js should be evaluated as a function but doesn't end in ')'", + "App smclock file smclock.settings.js should be evaluated as a function but doesn't end in ')'", + "App speedalt file speedalt.settings.js should be evaluated as a function but doesn't end in ')'", + "App speedalt2 file speedalt2.settings.js should be evaluated as a function but doesn't end in ')'", + "App swp2clk file swp2clk.settings.js should be evaluated as a function but doesn't end in ')'", + "App taglaunch file taglaunch.settings.js should be evaluated as a function but doesn't end in ')'", + "App thunder file thunder.settings.js should be evaluated as a function but doesn't end in ')'", + "App timecal file timecal.settings.js should be evaluated as a function but doesn't end in ')'", + "App timerclk file timerclk.settings.js should be evaluated as a function but doesn't end in ')'", + "App timestamplog file timestamplog.settings.js should be evaluated as a function but doesn't end in ')'", + "App toucher file toucher.settings.js should be evaluated as a function but doesn't end in ')'", + "App touchtimer file touchtimer.settings.js should be evaluated as a function but doesn't end in ')'", + "App trex file trex.settings.js should be evaluated as a function but doesn't end in ')'", + "App usgs file usgs.settings.js should be evaluated as a function but doesn't end in ')'", + "App weatherClock file weatherClock.settings.js should be evaluated as a function but doesn't end in ')'", + "App wid_edit file wid_edit.settings.js should be evaluated as a function but doesn't end in ')'", + "App widalarmeta file widalarmeta.settings.js should be evaluated as a function but doesn't end in ')'", + "App widbaroalarm file widbaroalarm.settings.js should be evaluated as a function but doesn't end in ')'", + "App widbatwarn file widbatwarn.settings.js should be evaluated as a function but doesn't end in ')'", + "App widbgjs file widbgjs.settings.js should be evaluated as a function but doesn't end in ')'", + "App widdst file widdst.settings.js should be evaluated as a function but doesn't end in ')'", + "App widgps file widgps.settings.js should be evaluated as a function but doesn't end in ')'", + "App widhrm file widhrm.settings.js should be evaluated as a function but doesn't end in ')'", + "App widmp file widmp.settings.js should be evaluated as a function but doesn't end in ')'", + "App widsleepstatus file widsleepstatus.settings.js should be evaluated as a function but doesn't end in ')'", ]; var apps = []; @@ -181,6 +287,7 @@ const isGlob = f => /[?*]/.test(f) // All storage+data files in all apps: {app:,[file: | data:]} let allFiles = []; let existingApps = []; +let promise = Promise.resolve(); apps.forEach((app,appIdx) => { if (!app.id) ERROR(`App ${appIdx} has no id`); var appDirRelative = APPSDIR_RELATIVE+app.id+"/"; @@ -306,8 +413,9 @@ apps.forEach((app,appIdx) => { } if (file.name.endsWith(".js")) { // TODO: actual lint? + var ast; try { - acorn.parse(fileContents); + ast = acorn.parse(fileContents); } catch(e) { console.log("====================================================="); console.log(" PARSE OF "+appDir+file.url+" failed."); @@ -337,6 +445,18 @@ apps.forEach((app,appIdx) => { WARN(`Settings for ${app.id} has a boolean formatter - this is handled automatically, the line can be removed`, {file:appDirRelative+file.url, line: fileContents.substr(0, m.index).split("\n").length}); } } + // something that needs to be evaluated with 'eval(require("Storage").read(fn))' + if (/\.clkinfo\.js$/.test(file.name) || + /\.settings\.js$/.test(file.name)) { + if (!fileContents.trim().endsWith(")")) + WARN(`App ${app.id} file ${file.name} should be evaluated as a function but doesn't end in ')'`, {file:appDirRelative+file.url}); + } + if (/\.clkinfo\.js$/.test(file.name) || + /\.wid\.js$/.test(file.name)) { + if (fileContents.indexOf("g.clear(")>=0 || + fileContents.indexOf("g.reset().clear()")>=0) + ERROR(`App ${app.id} widget/clkinfo ${file.name} should never totally clear the screen`, {file:appDirRelative+file.url}); + } } for (const key in file) { if (!STORAGE_KEYS.includes(key)) ERROR(`App ${app.id} file ${file.name} has unknown key ${key}`, {file:appDirRelative+file.url}); @@ -436,6 +556,18 @@ apps.forEach((app,appIdx) => { ERROR(`App ${app.id} has provides_modules ${modulename} but it doesn't provide that filename`, {file:metadataFile}); }); } + /* + // We could try to create the files we need to upload for this app to check it all works ok... + promise = promise.then(() => apploader.getAppFiles(app).then(files => { + files.forEach(file => { + if (/\.clkinfo?\.js$/.test(file.name) || + /\.settings?\.js$/.test(file.name)) { + if (!file.content.startsWith("(")) { + ERROR(`App ${app.id} file ${file.name} should evaluate to a simple fn and doesn't (starts: ${JSON.stringify(file.content.substr(0,30))})`, {file:appDirRelative+file.url}); + } + } + }); + }));*/ }); @@ -481,12 +613,14 @@ function sanityCheckLocales(){ } } -console.log("=================================="); -console.log(`${errorCount} errors, ${warningCount} warnings (and ${knownErrorCount} known errors, ${knownWarningCount} known warnings)`); -console.log("=================================="); -if (errorCount) { - process.exit(1); -} else if ("CI" in process.env && warningCount) { - console.log("Running in CI, raising an error from warnings"); - process.exit(1); -} +promise.then(function() { + console.log("=================================="); + console.log(`${errorCount} errors, ${warningCount} warnings (and ${knownErrorCount} known errors, ${knownWarningCount} known warnings)`); + console.log("=================================="); + if (errorCount) { + process.exit(1); + } else if ("CI" in process.env && warningCount) { + console.log("Running in CI, raising an error from warnings"); + process.exit(1); + } +}); diff --git a/core b/core index 7e5ac0271..cbf53ec34 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 7e5ac0271f794bcacda3a5a692cfa479457eb4dd +Subproject commit cbf53ec34fee9b7d9234a7bbfc276592d9e076a5 diff --git a/modules/Layout.js b/modules/Layout.js index 639173f4d..5c66fc313 100644 --- a/modules/Layout.js +++ b/modules/Layout.js @@ -235,7 +235,7 @@ Layout.prototype.forgetLazyState = function () { Layout.prototype.layout = function (l) { // l = current layout element - var cb = { + var floor = Math.floor, cb = { "h" : function(l) {"ram"; var acc_w = l.x + (0|l.pad), accfillx = 0, @@ -246,7 +246,7 @@ Layout.prototype.layout = function (l) { c.x = 0|x; acc_w += c._w; accfillx += 0|c.fillx; - x = acc_w + 0|(accfillx*(l.w-l._w)/fillx); + x = acc_w + floor(accfillx*(l.w-l._w)/fillx); c.w = 0|(x - c.x); c.h = 0|(c.filly ? l.h - (l.pad<<1) : c._h); c.y = 0|(l.y + (0|l.pad) + ((1+(0|c.valign))*(l.h-(l.pad<<1)-c.h)>>1)); @@ -263,7 +263,7 @@ Layout.prototype.layout = function (l) { c.y = 0|y; acc_h += c._h; accfilly += 0|c.filly; - y = acc_h + 0|(accfilly*(l.h-l._h)/filly); + y = acc_h + floor(accfilly*(l.h-l._h)/filly); c.h = 0|(y - c.y); c.w = 0|(c.fillx ? l.w - (l.pad<<1) : c._w); c.x = 0|(l.x + (0|l.pad) + ((1+(0|c.halign))*(l.w-(l.pad<<1)-c.w)>>1)); @@ -363,4 +363,4 @@ Layout.prototype.clear = function(l) { g.clearRect(l.x,l.y,l.x+l.w-1,l.y+l.h-1); }; -exports = Layout; \ No newline at end of file +exports = Layout; diff --git a/modules/Layout.min.js b/modules/Layout.min.js index 8dd7f8c87..4c64d078a 100644 --- a/modules/Layout.min.js +++ b/modules/Layout.min.js @@ -1,14 +1,14 @@ -function p(d,k){function c(f){"ram";f.id&&(l[f.id]=f);f.type||(f.type="");f.c&&f.c.forEach(c)}this._l=this.l=d;this.options=k||{};this.lazy=this.options.lazy||!1;this.physBtns=1;let h;if(2!=process.env.HWVERSION){this.physBtns=3;h=[];function f(a){"ram";"btn"==a.type&&h.push(a);a.c&&a.c.forEach(f)}f(d);h.length&&(this.physBtns=0,this.buttons=h,this.selectedButton=-1)}if(this.options.btns)if(d=this.options.btns,this.physBtns>=d.length){this.b=d;let f=Math.floor(Bangle.appRect.h/ -this.physBtns);for(2d.length;)d.push({label:""});this._l.width=g.getWidth()-8;this._l={type:"h",filly:1,c:[this._l,{type:"v",pad:1,filly:1,c:d.map(a=>(a.type="txt",a.font="6x8",a.height=f,a.r=1,a))}]}}else this._l.width=g.getWidth()-32,this._l={type:"h",c:[this._l,{type:"v",c:d.map(f=>(f.type="btn",f.filly=1,f.width=32,f.r=1,f))}]},h&&h.push.apply(h,this._l.c[1].c);this.setUI();var l=this;c(this._l);this.updateNeeded=!0}function t(d, -k,c,h,l){var f=null==d.bgCol?l:g.toColor(d.bgCol);if(f!=l||"txt"==d.type||"btn"==d.type||"img"==d.type||"custom"==d.type){var a=d.c;delete d.c;var e="H"+E.CRC32(E.toJS(d));a&&(d.c=a);delete k[e]||((h[e]=[d.x,d.y,d.x+d.w-1,d.y+d.h-1]).bg=null==l?g.theme.bg:l,c&&(c.push(d),c=null))}if(d.c)for(var b of d.c)t(b,k,c,h,f)}p.prototype.setUI=function(){Bangle.setUI();let d;this.buttons&&(Bangle.setUI({mode:"updown",back:this.options.back,remove:this.options.remove},k=>{var c=this.selectedButton,h=this.buttons.length; -if(void 0===k&&this.buttons[c])return this.buttons[c].cb();this.buttons[c]&&(delete this.buttons[c].selected,this.render(this.buttons[c]));c=(c+h+k)%h;this.buttons[c]&&(this.buttons[c].selected=1,this.render(this.buttons[c]));this.selectedButton=c}),d=!0);!this.options.back&&!this.options.remove||d||Bangle.setUI({mode:"custom",back:this.options.back,remove:this.options.remove});if(this.b){function k(c,h){.75=c.x&&h.y>=c.y&&h.x<=c.x+c.w&&h.y<=c.y+c.h&&(2==h.type&&c.cbl?c.cbl(h):c.cb&&c.cb(h));c.c&&c.c.forEach(l=>k(l,h))}Bangle.touchHandler= -(c,h)=>k(this._l,h);Bangle.on("touch",Bangle.touchHandler)}};p.prototype.render=function(d){function k(b){"ram";c.reset();void 0!==b.col&&c.setColor(b.col);void 0!==b.bgCol&&c.setBgColor(b.bgCol).clearRect(b.x,b.y,b.x+b.w-1,b.y+b.h-1);h[b.type](b)}d||(d=this._l);this.updateNeeded&&this.update();var c=g,h={"":function(){},txt:function(b){"ram";if(b.wrap){var m=c.setFont(b.font).setFontAlign(0,-1).wrapString(b.label,b.w),n=b.y+(b.h-c.getFontHeight()*m.length>>1);c.drawString(m.join("\n"),b.x+(b.w>> -1),n)}else c.setFont(b.font).setFontAlign(0,0,b.r).drawString(b.label,b.x+(b.w>>1),b.y+(b.h>>1))},btn:function(b){"ram";var m=b.x+(0|b.pad),n=b.y+(0|b.pad),q=b.w-(b.pad<<1),r=b.h-(b.pad<<1);m=[m,n+4,m+4,n,m+q-5,n,m+q-1,n+4,m+q-1,n+r-5,m+q-5,n+r-1,m+4,n+r-1,m,n+r-5,m,n+4];n=void 0!==b.btnBorderCol?b.btnBorderCol:c.theme.fg2;q=void 0!==b.btnFaceCol?b.btnFaceCol:c.theme.bg2;b.selected&&(q=c.theme.bgH,n=c.theme.fgH);c.setColor(q).fillPoly(m).setColor(n).drawPoly(m);void 0!==b.col&&c.setColor(b.col);b.src? -c.setBgColor(q).drawImage("function"==typeof b.src?b.src():b.src,b.x+b.w/2,b.y+b.h/2,{scale:b.scale||void 0,rotate:.5*Math.PI*(b.r||0)}):c.setFont(b.font||"6x8:2").setFontAlign(0,0,b.r).drawString(b.label,b.x+b.w/2,b.y+b.h/2)},img:function(b){"ram";c.drawImage("function"==typeof b.src?b.src():b.src,b.x+b.w/2,b.y+b.h/2,{scale:b.scale||void 0,rotate:.5*Math.PI*(b.r||0)})},custom:function(b){"ram";b.render(b)},h:function(b){"ram";b.c.forEach(k)},v:function(b){"ram";b.c.forEach(k)}};if(this.lazy){this.rects|| -(this.rects={});var l=this.rects.clone(),f=[];t(d,l,f,this.rects,null);for(var a in l)delete this.rects[a];d=Object.keys(l).map(b=>l[b]).reverse();for(var e of d)c.setBgColor(e.bg).clearRect.apply(g,e);f.forEach(k)}else k(d)};p.prototype.forgetLazyState=function(){this.rects={}};p.prototype.layout=function(d){var k={h:function(c){"ram";var h=c.x+(0|c.pad),l=0,f=c.c&&c.c.reduce((e,b)=>e+(0|b.fillx),0);f||(h+=c.w-c._w>>1,f=1);var a=h;c.c.forEach(e=>{e.x=0|a;h+=e._w;l+=0|e.fillx;a=h+0|l*(c.w-c._w)/f; -e.w=0|a-e.x;e.h=0|(e.filly?c.h-(c.pad<<1):e._h);e.y=0|c.y+(0|c.pad)+((1+(0|e.valign))*(c.h-(c.pad<<1)-e.h)>>1);if(e.c)k[e.type](e)})},v:function(c){"ram";var h=c.y+(0|c.pad),l=0,f=c.c&&c.c.reduce((e,b)=>e+(0|b.filly),0);f||(h+=c.h-c._h>>1,f=1);var a=h;c.c.forEach(e=>{e.y=0|a;h+=e._h;l+=0|e.filly;a=h+0|l*(c.h-c._h)/f;e.h=0|a-e.y;e.w=0|(e.fillx?c.w-(c.pad<<1):e._w);e.x=0|c.x+(0|c.pad)+((1+(0|e.halign))*(c.w-(c.pad<<1)-e.w)>>1);if(e.c)k[e.type](e)})}};if(k[d.type])k[d.type](d)};p.prototype.debug=function(d, -k){d||(d=this._l);k=k||1;g.setColor(k&1,k&2,k&4).drawRect(d.x+k-1,d.y+k-1,d.x+d.w-k,d.y+d.h-k);d.pad&&g.drawRect(d.x+d.pad-1,d.y+d.pad-1,d.x+d.w-d.pad,d.y+d.h-d.pad);k++;d.c&&d.c.forEach(c=>this.debug(c,k))};p.prototype.update=function(){function d(a){"ram";l[a.type](a);if(a.r&1){var e=a._w;a._w=a._h;a._h=e}a._w=c(a._w+(a.pad<<1),0|a.width);a._h=c(a._h+(a.pad<<1),0|a.height)}delete this.updateNeeded;var k=g,c=Math.max,h=Math.round,l={txt:function(a){"ram";a.font.endsWith("%")&&(a.font="Vector"+h(k.getHeight()* -a.font.slice(0,-1)/100));if(a.wrap)a._h=a._w=0;else{var e=k.setFont(a.font).stringMetrics(a.label);a._w=e.width;a._h=e.height}},btn:function(a){"ram";a.font&&a.font.endsWith("%")&&(a.font="Vector"+h(k.getHeight()*a.font.slice(0,-1)/100));var e=a.src?k.imageMetrics("function"==typeof a.src?a.src():a.src):k.setFont(a.font||"6x8:2").stringMetrics(a.label);a._h=16+e.height;a._w=20+e.width},img:function(a){"ram";var e=k.imageMetrics("function"==typeof a.src?a.src():a.src),b=a.scale||1;a._w=e.width*b;a._h= -e.height*b},"":function(a){"ram";a._w=0;a._h=0},custom:function(a){"ram";a._w=0;a._h=0},h:function(a){"ram";a.c.forEach(d);a._h=a.c.reduce((e,b)=>c(e,b._h),0);a._w=a.c.reduce((e,b)=>e+b._w,0);null==a.fillx&&a.c.some(e=>e.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(e=>e.filly)&&(a.filly=1)},v:function(a){"ram";a.c.forEach(d);a._h=a.c.reduce((e,b)=>e+b._h,0);a._w=a.c.reduce((e,b)=>c(e,b._w),0);null==a.fillx&&a.c.some(e=>e.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(e=>e.filly)&&(a.filly=1)}},f=this._l; -d(f);delete l;f.fillx||f.filly?(f.w=Bangle.appRect.w,f.h=Bangle.appRect.h,f.x=Bangle.appRect.x,f.y=Bangle.appRect.y):(f.w=f._w,f.h=f._h,f.x=Bangle.appRect.w-f.w>>1,f.y=Bangle.appRect.y+(Bangle.appRect.h-f.h>>1));this.layout(f)};p.prototype.clear=function(d){d||(d=this._l);g.reset();void 0!==d.bgCol&&g.setBgColor(d.bgCol);g.clearRect(d.x,d.y,d.x+d.w-1,d.y+d.h-1)};exports=p \ No newline at end of file +function p(c,h){function d(f){"ram";f.id&&(l[f.id]=f);f.type||(f.type="");f.c&&f.c.forEach(d)}this._l=this.l=c;this.options=h||{};this.lazy=this.options.lazy||!1;this.physBtns=1;let e;if(2!=process.env.HWVERSION){this.physBtns=3;e=[];function f(b){"ram";"btn"==b.type&&e.push(b);b.c&&b.c.forEach(f)}f(c);e.length&&(this.physBtns=0,this.buttons=e,this.selectedButton=-1)}if(this.options.btns)if(c=this.options.btns,this.physBtns>=c.length){this.b=c;let f=Math.floor(Bangle.appRect.h/ +this.physBtns);for(2c.length;)c.push({label:""});this._l.width=g.getWidth()-8;this._l={type:"h",filly:1,c:[this._l,{type:"v",pad:1,filly:1,c:c.map(b=>(b.type="txt",b.font="6x8",b.height=f,b.r=1,b))}]}}else this._l.width=g.getWidth()-32,this._l={type:"h",c:[this._l,{type:"v",c:c.map(f=>(f.type="btn",f.filly=1,f.width=32,f.r=1,f))}]},e&&e.push.apply(e,this._l.c[1].c);this.setUI();var l=this;d(this._l);this.updateNeeded=!0}function t(c, +h,d,e,l){var f=null==c.bgCol?l:g.toColor(c.bgCol);if(f!=l||"txt"==c.type||"btn"==c.type||"img"==c.type||"custom"==c.type){var b=c.c;delete c.c;var k="H"+E.CRC32(E.toJS(c));b&&(c.c=b);delete h[k]||((e[k]=[c.x,c.y,c.x+c.w-1,c.y+c.h-1]).bg=null==l?g.theme.bg:l,d&&(d.push(c),d=null))}if(c.c)for(var a of c.c)t(a,h,d,e,f)}p.prototype.setUI=function(){Bangle.setUI();let c;this.buttons&&(Bangle.setUI({mode:"updown",back:this.options.back,remove:this.options.remove},h=>{var d=this.selectedButton,e=this.buttons.length; +if(void 0===h&&this.buttons[d])return this.buttons[d].cb();this.buttons[d]&&(delete this.buttons[d].selected,this.render(this.buttons[d]));d=(d+e+h)%e;this.buttons[d]&&(this.buttons[d].selected=1,this.render(this.buttons[d]));this.selectedButton=d}),c=!0);!this.options.back&&!this.options.remove||c||Bangle.setUI({mode:"custom",back:this.options.back,remove:this.options.remove});if(this.b){function h(d,e){.75=d.x&&e.y>=d.y&&e.x<=d.x+d.w&&e.y<=d.y+d.h&&(2==e.type&&d.cbl?d.cbl(e):d.cb&&d.cb(e));d.c&&d.c.forEach(l=>h(l,e))}Bangle.touchHandler= +(d,e)=>h(this._l,e);Bangle.on("touch",Bangle.touchHandler)}};p.prototype.render=function(c){function h(a){"ram";d.reset();void 0!==a.col&&d.setColor(a.col);void 0!==a.bgCol&&d.setBgColor(a.bgCol).clearRect(a.x,a.y,a.x+a.w-1,a.y+a.h-1);e[a.type](a)}c||(c=this._l);this.updateNeeded&&this.update();var d=g,e={"":function(){},txt:function(a){"ram";if(a.wrap){var m=d.setFont(a.font).setFontAlign(0,-1).wrapString(a.label,a.w),n=a.y+(a.h-d.getFontHeight()*m.length>>1);d.drawString(m.join("\n"),a.x+(a.w>> +1),n)}else d.setFont(a.font).setFontAlign(0,0,a.r).drawString(a.label,a.x+(a.w>>1),a.y+(a.h>>1))},btn:function(a){"ram";var m=a.x+(0|a.pad),n=a.y+(0|a.pad),q=a.w-(a.pad<<1),r=a.h-(a.pad<<1);m=[m,n+4,m+4,n,m+q-5,n,m+q-1,n+4,m+q-1,n+r-5,m+q-5,n+r-1,m+4,n+r-1,m,n+r-5,m,n+4];n=void 0!==a.btnBorderCol?a.btnBorderCol:d.theme.fg2;q=void 0!==a.btnFaceCol?a.btnFaceCol:d.theme.bg2;a.selected&&(q=d.theme.bgH,n=d.theme.fgH);d.setColor(q).fillPoly(m).setColor(n).drawPoly(m);void 0!==a.col&&d.setColor(a.col);a.src? +d.setBgColor(q).drawImage("function"==typeof a.src?a.src():a.src,a.x+a.w/2,a.y+a.h/2,{scale:a.scale||void 0,rotate:.5*Math.PI*(a.r||0)}):d.setFont(a.font||"6x8:2").setFontAlign(0,0,a.r).drawString(a.label,a.x+a.w/2,a.y+a.h/2)},img:function(a){"ram";d.drawImage("function"==typeof a.src?a.src():a.src,a.x+a.w/2,a.y+a.h/2,{scale:a.scale||void 0,rotate:.5*Math.PI*(a.r||0)})},custom:function(a){"ram";a.render(a)},h:function(a){"ram";a.c.forEach(h)},v:function(a){"ram";a.c.forEach(h)}};if(this.lazy){this.rects|| +(this.rects={});var l=this.rects.clone(),f=[];t(c,l,f,this.rects,null);for(var b in l)delete this.rects[b];c=Object.keys(l).map(a=>l[a]).reverse();for(var k of c)d.setBgColor(k.bg).clearRect.apply(g,k);f.forEach(h)}else h(c)};p.prototype.forgetLazyState=function(){this.rects={}};p.prototype.layout=function(c){var h=Math.floor,d={h:function(e){"ram";var l=e.x+(0|e.pad),f=0,b=e.c&&e.c.reduce((a,m)=>a+(0|m.fillx),0);b||(l+=e.w-e._w>>1,b=1);var k=l;e.c.forEach(a=>{a.x=0|k;l+=a._w;f+=0|a.fillx;k=l+h(f* +(e.w-e._w)/b);a.w=0|k-a.x;a.h=0|(a.filly?e.h-(e.pad<<1):a._h);a.y=0|e.y+(0|e.pad)+((1+(0|a.valign))*(e.h-(e.pad<<1)-a.h)>>1);if(a.c)d[a.type](a)})},v:function(e){"ram";var l=e.y+(0|e.pad),f=0,b=e.c&&e.c.reduce((a,m)=>a+(0|m.filly),0);b||(l+=e.h-e._h>>1,b=1);var k=l;e.c.forEach(a=>{a.y=0|k;l+=a._h;f+=0|a.filly;k=l+h(f*(e.h-e._h)/b);a.h=0|k-a.y;a.w=0|(a.fillx?e.w-(e.pad<<1):a._w);a.x=0|e.x+(0|e.pad)+((1+(0|a.halign))*(e.w-(e.pad<<1)-a.w)>>1);if(a.c)d[a.type](a)})}};if(d[c.type])d[c.type](c)};p.prototype.debug= +function(c,h){c||(c=this._l);h=h||1;g.setColor(h&1,h&2,h&4).drawRect(c.x+h-1,c.y+h-1,c.x+c.w-h,c.y+c.h-h);c.pad&&g.drawRect(c.x+c.pad-1,c.y+c.pad-1,c.x+c.w-c.pad,c.y+c.h-c.pad);h++;c.c&&c.c.forEach(d=>this.debug(d,h))};p.prototype.update=function(){function c(b){"ram";l[b.type](b);if(b.r&1){var k=b._w;b._w=b._h;b._h=k}b._w=d(b._w+(b.pad<<1),0|b.width);b._h=d(b._h+(b.pad<<1),0|b.height)}delete this.updateNeeded;var h=g,d=Math.max,e=Math.round,l={txt:function(b){"ram";b.font.endsWith("%")&&(b.font= +"Vector"+e(h.getHeight()*b.font.slice(0,-1)/100));if(b.wrap)b._h=b._w=0;else{var k=h.setFont(b.font).stringMetrics(b.label);b._w=k.width;b._h=k.height}},btn:function(b){"ram";b.font&&b.font.endsWith("%")&&(b.font="Vector"+e(h.getHeight()*b.font.slice(0,-1)/100));var k=b.src?h.imageMetrics("function"==typeof b.src?b.src():b.src):h.setFont(b.font||"6x8:2").stringMetrics(b.label);b._h=16+k.height;b._w=20+k.width},img:function(b){"ram";var k=h.imageMetrics("function"==typeof b.src?b.src():b.src),a=b.scale|| +1;b._w=k.width*a;b._h=k.height*a},"":function(b){"ram";b._w=0;b._h=0},custom:function(b){"ram";b._w=0;b._h=0},h:function(b){"ram";b.c.forEach(c);b._h=b.c.reduce((k,a)=>d(k,a._h),0);b._w=b.c.reduce((k,a)=>k+a._w,0);null==b.fillx&&b.c.some(k=>k.fillx)&&(b.fillx=1);null==b.filly&&b.c.some(k=>k.filly)&&(b.filly=1)},v:function(b){"ram";b.c.forEach(c);b._h=b.c.reduce((k,a)=>k+a._h,0);b._w=b.c.reduce((k,a)=>d(k,a._w),0);null==b.fillx&&b.c.some(k=>k.fillx)&&(b.fillx=1);null==b.filly&&b.c.some(k=>k.filly)&& +(b.filly=1)}},f=this._l;c(f);delete l;f.fillx||f.filly?(f.w=Bangle.appRect.w,f.h=Bangle.appRect.h,f.x=Bangle.appRect.x,f.y=Bangle.appRect.y):(f.w=f._w,f.h=f._h,f.x=Bangle.appRect.w-f.w>>1,f.y=Bangle.appRect.y+(Bangle.appRect.h-f.h>>1));this.layout(f)};p.prototype.clear=function(c){c||(c=this._l);g.reset();void 0!==c.bgCol&&g.setBgColor(c.bgCol);g.clearRect(c.x,c.y,c.x+c.w-1,c.y+c.h-1)};exports=p \ No newline at end of file diff --git a/modules/time_utils.js b/modules/time_utils.js index 6a3ed6faf..0f503ad37 100644 --- a/modules/time_utils.js +++ b/modules/time_utils.js @@ -13,18 +13,13 @@ // Note that if a field is undefined then its value is zero. // -const ONE_SECOND = 1000; -const ONE_MINUTE = 60 * ONE_SECOND; -const ONE_HOUR = 60 * ONE_MINUTE; -const ONE_DAY = 24 * ONE_HOUR; - /** * @param {object} time {d, h, m, s} * @returns the milliseconds contained in the passed time object */ exports.encodeTime = (time) => { time = safeTime(time); - return time.d * ONE_DAY + time.h * ONE_HOUR + time.m * ONE_MINUTE + time.s * ONE_SECOND; + return time.d * 86400000 + time.h * 3600000 + time.m * 60000 + time.s * 1000; } // internal use, set to zero all the undefined fields @@ -38,26 +33,26 @@ function safeTime(time) { */ exports.decodeTime = (millis) => { if (typeof millis !== "number") throw "Only a number can be decoded"; - var d = Math.floor(millis / ONE_DAY); - millis -= d * ONE_DAY; - var h = Math.floor(millis / ONE_HOUR); - millis -= h * ONE_HOUR; - var m = Math.floor(millis / ONE_MINUTE); - millis -= m * ONE_MINUTE; - var s = Math.floor(millis / ONE_SECOND); + var d = Math.floor(millis / 86400000); + millis -= d * 86400000; + var h = Math.floor(millis / 3600000); + millis -= h * 3600000; + var m = Math.floor(millis / 60000); + millis -= m * 60000; + var s = Math.floor(millis / 1000); return { d: d, h: h, m: m, s: s }; } -/** +/** * @param {object|int} value {h, m} object or milliseconds * @returns an human-readable time string like "10:25" * @throws an exception if d != 0 or h > 23 or m > 59 */ exports.formatTime = (value) => { var time = safeTime(typeof value === "object" ? value : exports.decodeTime(value)); - if (time.d != 0) throw "days not supported here"; - if (time.h < 0 || time.h > 23) throw "Invalid value: must be 0 <= h <= 23"; - if (time.m < 0 || time.m > 59) throw "Invalid value: must be 0 <= m <= 59"; + time.h += time.d*24; + /*if (time.h < 0 || time.h > 23) throw "Invalid value: must be 0 <= h <= 23"; + if (time.m < 0 || time.m > 59) throw "Invalid value: must be 0 <= m <= 59";*/ return time.h + ":" + ("0" + time.m).substr(-2); } diff --git a/typescript/build.sh b/typescript/build.sh new file mode 100755 index 000000000..bf5493745 --- /dev/null +++ b/typescript/build.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +set -e + +usage(){ + echo >&2 "Usage: $0" + exit 2 +} + +if test $# -ne 0 +then usage +fi + +cd "$(dirname "$0")" + +npm run build + +find ../apps -iname '*.ts' | + sed 's/\.ts$/.js/' | + grep -E 'clkinfo|setting' | + xargs perl -i -pe 's/;$// if eof'