diff --git a/apps/authentiwatch/interface.html b/apps/authentiwatch/interface.html
index 7d567d34f..d7cd59f1a 100644
--- a/apps/authentiwatch/interface.html
+++ b/apps/authentiwatch/interface.html
@@ -12,8 +12,8 @@ body.select div.select,body.export div.export{display:block}
body.select div.export,body.export div.select{display:none}
body.select div#tokens,body.editing div#edit,body.scanning div#scan,body.showqr div#showqr,body.export div#tokens{display:block}
#tokens th,#tokens td{padding:5px}
-#tokens tr:nth-child(odd){background-color:#ccc}
-#tokens tr:nth-child(even){background-color:#eee}
+#tokens tr:nth-child(odd){background-color:#f1f1fc}
+#tokens tr:nth-child(even){background-color:#fff}
#qr-canvas{margin:auto;width:calc(100%-20px);max-width:400px}
#advbtn,#scan,#tokenqr table{text-align:center}
#edittoken tbody#adv{display:none}
@@ -226,15 +226,18 @@ function editToken(id) {
markup += selectMarkup('algorithm', otpAlgos, tokens[id].algorithm);
markup += '';
markup += '
';
- markup += 'Advanced ';
+ markup += 'Advanced ';
markup += ' ';
- markup += 'Cancel Edit ';
- markup += 'Save Changes ';
+ markup += 'Cancel Edit ';
+ markup += ' ';
+ markup += 'Save Changes ';
+ markup += ' ';
if (tokens[id].isnew) {
- markup += 'Scan QR ';
+ markup += 'Scan QR ';
} else {
- markup += 'Show QR ';
- markup += 'Forget Token ';
+ markup += 'Show QR ';
+ markup += ' ';
+ markup += 'Forget Token ';
}
document.getElementById('edit').innerHTML = markup;
document.body.className = 'editing';
@@ -304,9 +307,23 @@ function updateTokens() {
return ' ';
};
const tokenButton = function(fn, id, label, dir) {
- return '' + label + ' ';
+ return '' + label + ' ';
};
- var markup = '';
+ var markup = '';
+ markup += '';
+ markup += 'Add Token ';
+ markup += ' ';
+ markup += 'Save to watch ';
+ markup += ' ';
+ markup += 'Import ';
+ markup += ' ';
+ markup += 'Export ';
+ markup += '
';
+ markup += 'Cancel ';
+ markup += ' ';
+ markup += 'Show QR ';
+ markup += '
';
+ markup += '';
markup += tokenSelect('all');
markup += ' Token Order ';
/* any tokens marked new are cancelled new additions and must be removed */
@@ -331,15 +348,6 @@ function updateTokens() {
markup += '';
}
markup += '
';
- markup += '';
- markup += 'Add Token ';
- markup += 'Save to watch ';
- markup += 'Import ';
- markup += 'Export ';
- markup += '
';
- markup += 'Cancel ';
- markup += 'Show QR ';
- markup += '
';
document.getElementById('tokens').innerHTML = markup;
document.body.className = 'select';
}
@@ -604,7 +612,7 @@ function qrBack() {
@@ -613,7 +621,7 @@ function qrBack() {
diff --git a/apps/bikespeedo/ChangeLog b/apps/bikespeedo/ChangeLog
index 5fb78710b..2a3023750 100644
--- a/apps/bikespeedo/ChangeLog
+++ b/apps/bikespeedo/ChangeLog
@@ -1 +1,2 @@
0.01: New App!
+0.02: Barometer altitude adjustment setting
diff --git a/apps/bikespeedo/README.md b/apps/bikespeedo/README.md
index 7d271a022..e4ce5ea5c 100644
--- a/apps/bikespeedo/README.md
+++ b/apps/bikespeedo/README.md
@@ -6,6 +6,10 @@
#### If "CALIB!" is shown on the display or the compass heading differs too much from GPS heading, compass calibration should be done with the ["Navigation Compass" App](https://banglejs.com/apps/?id=magnav)
+Permanently diverging Barometer Altitude values can be compensated in the settings menu.
+
+Please report bugs to https://github.com/espruino/BangleApps/issues/new?assignees=&labels=bug&template=bangle-bug-report-custom-form.yaml&title=%5BBike+Speedometer%5D+Short+description+of+bug
+
**Credits:**
Bike Speedometer App by github.com/HilmarSt
Big parts of the software are based on github.com/espruino/BangleApps/tree/master/apps/speedalt
diff --git a/apps/bikespeedo/app-icon.js b/apps/bikespeedo/app-icon.js
index c34f52cfb..411d644fd 100644
--- a/apps/bikespeedo/app-icon.js
+++ b/apps/bikespeedo/app-icon.js
@@ -1 +1 @@
-require("heatshrink").decompress(atob("mEwxH+64A/AC+sF1uBgAwsq1W1krGEmswIFDlcAFoMrqyGjlcrGAQDB1guBBQJghKYZZCMYhqBlYugFAesgAuFYgQIHAE2sYMZDfwIABbgIuowMAqwABb4wAjFVQAEqyMrF4cAlYABqwypR4RgBwIyplYnF1hnBGIo8BAAQvhGIj6C1hpBgChBGCqGBqwdCRQQnCB4gJBGAgtWc4WBPoi9JH4ILBGYQATPoRHJRYoACwLFBLi4tGLIyLEA5QuPCoYpEMhBBBGDIuFgArIYQIUHA4b+GABLUBAwoQIXorDGI5RNGCB9WRQ0AJwwHGDxChOH4oDCRI4/GXpAaB1gyLEwlWKgTrBT46ALCogQKZoryFCwzgGBgz/NZpaQHHBCdEF5hKBBxWBUwoGBgEAEoIyHHYesBg7aBJQ7SBBAIvEIIJCBD4IFBgBIGEAcAUA8rGAIWHS4QvDCAJAHG4JfRCgKCFeAovCdRIiBDYq/NABi0Cfo5IEBgjUGACZ6BqwcGwLxBFYRsEHIKBIJwLkBNoIHDF468GYgIBBXY4EDE4IHDYwSwCN4IGBCIp5CJYtWgBZBHAgFEMoRjEE4QDCLYJUEUoaCBPYoQCgA4FGozxFLYwfEQgqrGexIYFBoxbDS4YHCIAYVEEAZcCYwwvGfoQHEcwQHHIg9WIAS9BIoYYESoowIABQuBUgg1DVwwACEpIwBChDLFDQ5JLlZnHJAajBQwgLEO4LDBHKAhBFxQxFCIIACAwadLHgJJBAAUrQJxYFAAbKPCwRGCCqAAm"))
+require("heatshrink").decompress(atob("mEwgP/ABO/AokfAgf+r4FD3lPBQcZw4FC/nD+4FC/Pn+YFCBIP7GQ4aDEIMDAol/ApQRFuAFEv0/BoQXBx0HAoPgh/nn40C4fwEoP+n/4/BWC/weBBYP5BAM/C4Pz7/7z+f//n7/z5/f//vA4Pv5//AIPv8/n//d//Ou5yBDIOfu58Bz42B+Z8Bz/8AoPgv+/AoP7w0f3IFBnc/5+bL4Oyv/nEYP/+X/mYFC+n8mff8ln+v4vfd7tfsvzvfN7tPtv2vPn6H35vg/f36vX7vj/fz9vvznH+Z3B/0+5/3/l//iDBMwMf+KEBOAPBUoOCj///CNBUQQAEA="))
diff --git a/apps/bikespeedo/app.js b/apps/bikespeedo/app.js
index 0c5680c9d..a62a429e5 100644
--- a/apps/bikespeedo/app.js
+++ b/apps/bikespeedo/app.js
@@ -460,9 +460,11 @@ function updateClock() {
}
+// =Main Prog
+
+// Read settings.
+let cfg = require('Storage').readJSON('bikespeedo.json',1)||{};
-//###
-let cfg = {};
cfg.spd = 1; // Multiplier for speed unit conversions. 0 = use the locale values for speed
cfg.spd_unit = 'km/h'; // Displayed speed unit
cfg.alt = 1; // Multiplier for altitude unit conversions. (feet:'0.3048')
@@ -472,8 +474,12 @@ cfg.dist_unit = 'km'; // Displayed distnce units
cfg.modeA = 1;
cfg.primSpd = 1; // 1 = Spd in primary, 0 = Spd in secondary
-cfg.spdFilt = false;
-cfg.altFilt = false;
+cfg.altDiff = cfg.altDiff==undefined?100:cfg.altDiff;
+cfg.spdFilt = cfg.spdFilt==undefined?true:cfg.spdFilt;
+cfg.altFilt = cfg.altFilt==undefined?false:cfg.altFilt;
+// console.log("cfg.altDiff: " + cfg.altDiff);
+// console.log("cfg.spdFilt: " + cfg.spdFilt);
+// console.log("cfg.altFilt: " + cfg.altFilt);
if ( cfg.spdFilt ) var spdFilter = new KalmanFilter({R: 0.1 , Q: 1 });
if ( cfg.altFilt ) var altFilter = new KalmanFilter({R: 0.01, Q: 2 });
@@ -489,7 +495,9 @@ function onGPSraw(nmea) {
} }
if(BANGLEJS2) Bangle.on('GPS-raw', onGPSraw);
-function onPressure(dat) { altiBaro = dat.altitude.toFixed(0); }
+function onPressure(dat) {
+ altiBaro = Number(dat.altitude.toFixed(0)) + Number(cfg.altDiff);
+}
Bangle.setBarometerPower(1); // needs some time...
g.clearRect(0,screenYstart,screenW,screenH);
@@ -520,10 +528,10 @@ function Compass_tiltfixread(O,S){
return psi;
}
var Compass_heading = 0;
-function Compass_newHeading(m,h){
+function Compass_newHeading(m,h){
var s = Math.abs(m - h);
var delta = (m>h)?1:-1;
- if (s>=180){s=360-s; delta = -delta;}
+ if (s>=180){s=360-s; delta = -delta;}
if (s<2) return h;
var hd = h + delta*(1 + Math.round(s/5));
if (hd<0) hd+=360;
diff --git a/apps/bikespeedo/metadata.json b/apps/bikespeedo/metadata.json
index 7dea28649..c3de0487c 100644
--- a/apps/bikespeedo/metadata.json
+++ b/apps/bikespeedo/metadata.json
@@ -1,8 +1,8 @@
{
"id": "bikespeedo",
"name": "Bike Speedometer (beta)",
- "shortName": "Bike Speedomet.",
- "version": "0.01",
+ "shortName": "Bike Speedometer",
+ "version": "0.02",
"description": "Shows GPS speed, GPS heading, Compass heading, GPS altitude and Barometer altitude from internal sources",
"icon": "app.png",
"screenshots": [{"url":"Screenshot.png"}],
@@ -13,6 +13,8 @@
"allow_emulator": true,
"storage": [
{"name":"bikespeedo.app.js","url":"app.js"},
- {"name":"bikespeedo.img","url":"app-icon.js","evaluate":true}
- ]
+ {"name":"bikespeedo.img","url":"app-icon.js","evaluate":true},
+ {"name":"bikespeedo.settings.js","url":"settings.js"}
+ ],
+ "data": [{"name":"bikespeedo.json"}]
}
diff --git a/apps/bikespeedo/settings.js b/apps/bikespeedo/settings.js
new file mode 100644
index 000000000..a3921f4a3
--- /dev/null
+++ b/apps/bikespeedo/settings.js
@@ -0,0 +1,48 @@
+(function(back) {
+
+ let settings = require('Storage').readJSON('bikespeedo.json',1)||{};
+
+ function writeSettings() {
+ require('Storage').write('bikespeedo.json',settings);
+ }
+
+ const appMenu = {
+ '': {'title': 'Bike Speedometer'},
+ '< Back': back,
+ '< Load Bike Speedometer': ()=>{load('bikespeedo.app.js');},
+ 'Barometer Altitude adjustment' : function() { E.showMenu(altdiffMenu); },
+ 'Kalman Filters' : function() { E.showMenu(kalMenu); }
+ };
+
+ const altdiffMenu = {
+ '': { 'title': 'Altitude adjustment' },
+ '< Back': function() { E.showMenu(appMenu); },
+ 'Altitude delta': {
+ value: settings.altDiff || 100,
+ min: -200,
+ max: 200,
+ step: 10,
+ onchange: v => {
+ settings.altDiff = v;
+ writeSettings(); }
+ }
+ };
+
+ const kalMenu = {
+ '': {'title': 'Kalman Filters'},
+ '< Back': function() { E.showMenu(appMenu); },
+ 'Speed' : {
+ value : settings.spdFilt,
+ format : v => v?"On":"Off",
+ onchange : () => { settings.spdFilt = !settings.spdFilt; writeSettings(); }
+ },
+ 'Altitude' : {
+ value : settings.altFilt,
+ format : v => v?"On":"Off",
+ onchange : () => { settings.altFilt = !settings.altFilt; writeSettings(); }
+ }
+ };
+
+ E.showMenu(appMenu);
+
+});
diff --git a/apps/boot/ChangeLog b/apps/boot/ChangeLog
index 87b5f7def..e3f492d3b 100644
--- a/apps/boot/ChangeLog
+++ b/apps/boot/ChangeLog
@@ -50,3 +50,4 @@
0.44: Write .boot0 without ever having it all in RAM (fix Bangle.js 1 issues with BTHRM)
0.45: Fix 0.44 regression (auto-add semi-colon between each boot code chunk)
0.46: Fix no clock found error on Bangle.js 2
+0.47: Add polyfill for setUI with an object as an argument (fix regression for 2v12 devices after Layout module changed)
diff --git a/apps/boot/bootupdate.js b/apps/boot/bootupdate.js
index c2ed5458d..46391d874 100644
--- a/apps/boot/bootupdate.js
+++ b/apps/boot/bootupdate.js
@@ -97,52 +97,20 @@ delete g.theme; // deleting stops us getting confused by our own decl. builtins
if (!g.theme) {
boot += `g.theme={fg:-1,bg:0,fg2:-1,bg2:7,fgH:-1,bgH:0x02F7,dark:true};\n`;
}
-delete Bangle.setUI; // deleting stops us getting confused by our own decl. builtins can't be deleted
-if (!Bangle.setUI) { // assume this is just for F18 - Q3 should already have it
- boot += `Bangle.setUI=function(mode, cb) {
-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;
-}
-if (!mode) return;
-else if (mode=="updown") {
- Bangle.btnWatches = [
- setWatch(function() { cb(-1); }, BTN1, {repeat:1}),
- setWatch(function() { cb(1); }, BTN3, {repeat:1}),
- setWatch(function() { cb(); }, BTN2, {repeat:1})
- ];
-} else if (mode=="leftright") {
- Bangle.btnWatches = [
- setWatch(function() { cb(-1); }, BTN1, {repeat:1}),
- setWatch(function() { cb(1); }, BTN3, {repeat:1}),
- setWatch(function() { cb(); }, BTN2, {repeat:1})
- ];
- 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:"falling"})
- ];
-} else if (mode=="clockupdown") {
- Bangle.CLOCK=1;
- Bangle.btnWatches = [
- setWatch(function() { cb(-1); }, BTN1, {repeat:1}),
- setWatch(function() { cb(1); }, BTN3, {repeat:1}),
- setWatch(Bangle.showLauncher, BTN2, {repeat:1,edge:"falling"})
- ];
-} else
- throw new Error("Unknown UI mode");
+try {
+ Bangle.setUI({}); // In 2v12.xx we added the option for mode to be an object - for 2v12 and earlier, add a fix if it fails with an object supplied
+} catch(e) {
+ boot += `Bangle._setUI = Bangle.setUI;
+Bangle.setUI=function(mode, cb) {
+ if (Bangle.uiRemove) {
+ Bangle.uiRemove();
+ delete Bangle.uiRemove;
+ }
+ if ("object"==typeof mode) {
+ // TODO: handle mode.back?
+ mode = mode.mode;
+ }
+ Bangle._setUI(mode, cb);
};\n`;
}
delete E.showScroller; // deleting stops us getting confused by our own decl. builtins can't be deleted
diff --git a/apps/boot/metadata.json b/apps/boot/metadata.json
index 11884576f..d1bf2edde 100644
--- a/apps/boot/metadata.json
+++ b/apps/boot/metadata.json
@@ -1,7 +1,7 @@
{
"id": "boot",
"name": "Bootloader",
- "version": "0.46",
+ "version": "0.47",
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
"icon": "bootloader.png",
"type": "bootloader",
diff --git a/apps/calendar/ChangeLog b/apps/calendar/ChangeLog
index beba4ed95..cc8bb6306 100644
--- a/apps/calendar/ChangeLog
+++ b/apps/calendar/ChangeLog
@@ -4,3 +4,4 @@
0.04: Add setting to switch color schemes. On Bangle 2 non-dithering colors will be used by default. Use localized names for months and days of the week (Language app needed).
0.05: Update calendar weekend colors for start on Sunday
0.06: Use larger font for dates
+0.07: Fix off-by-one-error on previous month
diff --git a/apps/calendar/calendar.js b/apps/calendar/calendar.js
index 62702e349..3f4315811 100644
--- a/apps/calendar/calendar.js
+++ b/apps/calendar/calendar.js
@@ -171,7 +171,7 @@ function drawCalendar(date) {
let days = [];
let nextMonthDay = 1;
let thisMonthDay = 51;
- let prevMonthDay = monthMaxDayMap[month > 0 ? month - 1 : 11] - dowNorm;
+ let prevMonthDay = monthMaxDayMap[month > 0 ? month - 1 : 11] - dowNorm + 1;
for (let i = 0; i < colN * (rowN - 1) + 1; i++) {
if (i < dowNorm) {
days.push(prevMonthDay);
diff --git a/apps/calendar/metadata.json b/apps/calendar/metadata.json
index 5531c03c3..62d2513ae 100644
--- a/apps/calendar/metadata.json
+++ b/apps/calendar/metadata.json
@@ -1,7 +1,7 @@
{
"id": "calendar",
"name": "Calendar",
- "version": "0.06",
+ "version": "0.07",
"description": "Simple calendar",
"icon": "calendar.png",
"screenshots": [{"url":"screenshot_calendar.png"}],
diff --git a/apps/doztime/metadata.json b/apps/doztime/metadata.json
index 6933487ab..a05bf1470 100644
--- a/apps/doztime/metadata.json
+++ b/apps/doztime/metadata.json
@@ -1,9 +1,9 @@
{
"id": "doztime",
- "name": "Dozenal Time",
- "shortName": "Dozenal Time",
+ "name": "Dozenal Digital Time",
+ "shortName": "Dozenal Digital",
"version": "0.05",
- "description": "A dozenal Holocene calendar and dozenal diurnal clock",
+ "description": "A dozenal Holocene calendar and dozenal diurnal digital clock",
"icon": "app.png",
"type": "clock",
"tags": "clock",
diff --git a/apps/ftclock/.gitignore b/apps/ftclock/.gitignore
index b384cf1f2..304792757 100644
--- a/apps/ftclock/.gitignore
+++ b/apps/ftclock/.gitignore
@@ -1,4 +1,5 @@
-timezonedb.csv.zip
+TimeZoneDB.csv.zip
country.csv
-zone.csv
-timezone.csv
+time_zone.csv
+database.sql
+readme.txt
diff --git a/apps/ftclock/ChangeLog b/apps/ftclock/ChangeLog
index c944dd9ac..83ec21ee6 100644
--- a/apps/ftclock/ChangeLog
+++ b/apps/ftclock/ChangeLog
@@ -1,2 +1,3 @@
0.01: first release
0.02: RAM efficient version of `fourTwentyTz.js` (as suggested by @gfwilliams).
+0.03: `mkFourTwentyTz.js` now handles new timezonedb.com CSV format
diff --git a/apps/ftclock/fourTwentyTz.js b/apps/ftclock/fourTwentyTz.js
index 5fa6cdab7..7ef7418f6 100644
--- a/apps/ftclock/fourTwentyTz.js
+++ b/apps/ftclock/fourTwentyTz.js
@@ -1,33 +1,33 @@
// Generated by mkFourTwentyTz.js
-// Wed Jan 12 2022 19:35:36 GMT+0200 (Israel Standard Time)
-// Data source: https://timezonedb.com/files/timezonedb.csv.zip
+// Sun Mar 27 2022 14:10:08 GMT+0300 (Israel Daylight Time)
+// Data source: https://timezonedb.com/files/TimeZoneDB.csv.zip
exports.offsets = [1380,1320,1260,1200,1140,1080,1020,960,900,840,780,720,660,600,540,480,420,360,300,240,180,120,60,0];
exports.timezones = function(offs) {
switch (offs) {
- case 1380: return ["Cape Verde, Cabo Verde","Scoresbysund, Greenland","Azores, Portugal"];
- case 1320: return ["Noronha, Brazil","South Georgia, South Georgia and the South Sandwich Islands"];
- case 1260: return ["Palmer, Antarctica","Rothera, Antarctica","Buenos Aires, Argentina","Cordoba, Argentina","Salta, Argentina","Jujuy, Argentina","Tucuman, Argentina","Catamarca, Argentina","La Rioja, Argentina","San Juan, Argentina","Mendoza, Argentina","San Luis, Argentina","Rio Gallegos, Argentina","Ushuaia, Argentina","Belem, Brazil","Fortaleza, Brazil","Recife, Brazil","Araguaina, Brazil","Maceio, Brazil","Bahia, Brazil","Sao Paulo, Brazil","Santarem, Brazil","Santiago, Chile","Punta Arenas, Chile","Stanley, Falkland Islands (Malvinas)","Cayenne, French Guiana","Nuuk, Greenland","Miquelon, Saint Pierre and Miquelon","Asuncion, Paraguay","Paramaribo, Suriname","Montevideo, Uruguay"];
- case 1200: return ["Antigua, Antigua and Barbuda","Anguilla, Anguilla","Aruba, Aruba","Barbados, Barbados","St Barthelemy, Saint Barthélemy","Bermuda, Bermuda","La Paz, Bolivia (Plurinational State of)","Kralendijk, Bonaire, Sint Eustatius and Saba","Campo Grande, Brazil","Cuiaba, Brazil","Porto Velho, Brazil","Boa Vista, Brazil","Manaus, Brazil","Halifax, Canada","Glace Bay, Canada","Moncton, Canada","Goose Bay, Canada","Blanc-Sablon, Canada","Curacao, Curaçao","Dominica, Dominica","Santo Domingo, Dominican Republic","Grenada, Grenada","Thule, Greenland","Guadeloupe, Guadeloupe","Guyana, Guyana","St Kitts, Saint Kitts and Nevis","St Lucia, Saint Lucia","Marigot, Saint Martin (French part)","Martinique, Martinique","Montserrat, Montserrat","Puerto Rico, Puerto Rico","Lower Princes, Sint Maarten (Dutch part)","Port of_Spain, Trinidad and Tobago","St Vincent, Saint Vincent and the Grenadines","Caracas, Venezuela (Bolivarian Republic of)","Tortola, Virgin Islands (British)","St Thomas, Virgin Islands (U.S.)"];
- case 1140: return ["Eirunepe, Brazil","Rio Branco, Brazil","Nassau, Bahamas","Toronto, Canada","Nipigon, Canada","Thunder Bay, Canada","Iqaluit, Canada","Pangnirtung, Canada","Atikokan, Canada","Easter, Chile","Bogota, Colombia","Havana, Cuba","Guayaquil, Ecuador","Port-au-Prince, Haiti","Jamaica, Jamaica","Cayman, Cayman Islands","Cancun, Mexico","Panama, Panama","Lima, Peru","Grand Turk, Turks and Caicos Islands","New York, United States of America","Detroit, United States of America","Louisville, Kentucky","Monticello, Kentucky","Indianapolis, Indiana","Vincennes, Indiana","Winamac, Indiana","Marengo, Indiana","Petersburg, Indiana","Vevay, Indiana"];
- case 1080: return ["Belize, Belize","Winnipeg, Canada","Rainy River, Canada","Resolute, Canada","Rankin Inlet, Canada","Regina, Canada","Swift Current, Canada","Costa Rica, Costa Rica","Galapagos, Ecuador","Guatemala, Guatemala","Tegucigalpa, Honduras","Mexico City, Mexico","Merida, Mexico","Monterrey, Mexico","Matamoros, Mexico","Bahia Banderas, Mexico","Managua, Nicaragua","El Salvador, El Salvador","Chicago, United States of America","Tell City, Indiana","Knox, Indiana","Menominee, United States of America","Center, North Dakota","New_Salem, North Dakota","Beulah, North Dakota"];
- case 1020: return ["Edmonton, Canada","Cambridge Bay, Canada","Yellowknife, Canada","Inuvik, Canada","Creston, Canada","Dawson Creek, Canada","Fort Nelson, Canada","Whitehorse, Canada","Dawson, Canada","Mazatlan, Mexico","Chihuahua, Mexico","Ojinaga, Mexico","Hermosillo, Mexico","Denver, United States of America","Boise, United States of America","Phoenix, United States of America"];
- case 960: return ["Vancouver, Canada","Tijuana, Mexico","Pitcairn, Pitcairn","Los Angeles, United States of America"];
- case 900: return ["Gambier, French Polynesia","Anchorage, United States of America","Juneau, United States of America","Sitka, United States of America","Metlakatla, United States of America","Yakutat, United States of America","Nome, United States of America"];
- case 840: return ["Rarotonga, Cook Islands","Kiritimati, Kiribati","Tahiti, French Polynesia","Adak, United States of America","Honolulu, United States of America"];
- case 780: return ["McMurdo, Antarctica","Pago Pago, American Samoa","Fiji, Fiji","Kanton, Kiribati","Niue, Niue","Auckland, New Zealand","Fakaofo, Tokelau","Tongatapu, Tonga","Midway, United States Minor Outlying Islands","Apia, Samoa"];
- case 720: return ["Tarawa, Kiribati","Majuro, Marshall Islands","Kwajalein, Marshall Islands","Norfolk, Norfolk Island","Nauru, Nauru","Kamchatka, Russian Federation","Anadyr, Russian Federation","Funafuti, Tuvalu","Wake, United States Minor Outlying Islands","Wallis, Wallis and Futuna"];
- case 660: return ["Casey, Antarctica","Lord Howe, Australia","Macquarie, Australia","Hobart, Australia","Melbourne, Australia","Sydney, Australia","Pohnpei, Micronesia (Federated States of)","Kosrae, Micronesia (Federated States of)","Noumea, New Caledonia","Bougainville, Papua New Guinea","Magadan, Russian Federation","Sakhalin, Russian Federation","Srednekolymsk, Russian Federation","Guadalcanal, Solomon Islands","Efate, Vanuatu"];
- case 600: return ["DumontDUrville, Antarctica","Brisbane, Australia","Lindeman, Australia","Chuuk, Micronesia (Federated States of)","Guam, Guam","Saipan, Northern Mariana Islands","Port Moresby, Papua New Guinea","Vladivostok, Russian Federation","Ust-Nera, Russian Federation"];
- case 540: return ["Jayapura, Indonesia","Tokyo, Japan","Pyongyang, Korea (Democratic People's Republic of)","Seoul, Korea, Republic of","Palau, Palau","Chita, Russian Federation","Yakutsk, Russian Federation","Khandyga, Russian Federation","Dili, Timor-Leste"];
+ case 1380: return ["Cape Verde, Cape Verde"];
+ case 1320: return ["Noronha, Brazil","Nuuk, Greenland","South Georgia, South Georgia and the South Sandwich Islands","Miquelon, Saint Pierre and Miquelon"];
+ case 1260: return ["Palmer, Antarctica","Rothera, Antarctica","Buenos Aires, Argentina","Cordoba, Argentina","Salta, Argentina","Jujuy, Argentina","Tucuman, Argentina","Catamarca, Argentina","La Rioja, Argentina","San Juan, Argentina","Mendoza, Argentina","San Luis, Argentina","Rio Gallegos, Argentina","Ushuaia, Argentina","Bermuda, Bermuda","Belem, Brazil","Fortaleza, Brazil","Recife, Brazil","Araguaina, Brazil","Maceio, Brazil","Bahia, Brazil","Sao Paulo, Brazil","Santarem, Brazil","Halifax, Canada","Glace Bay, Canada","Moncton, Canada","Goose Bay, Canada","Santiago, Chile","Punta Arenas, Chile","Stanley, Falkland Islands (Malvinas)","Cayenne, French Guiana","Thule, Greenland","Paramaribo, Suriname","Montevideo, Uruguay"];
+ case 1200: return ["Antigua, Antigua and Barbuda","Anguilla, Anguilla","Aruba, Aruba","Barbados, Barbados","St Barthelemy, Saint Barthélemy","La Paz, Bolivia, Plurinational State of","Kralendijk, Bonaire, Sint Eustatius and Saba","Campo Grande, Brazil","Cuiaba, Brazil","Porto Velho, Brazil","Boa Vista, Brazil","Manaus, Brazil","Nassau, Bahamas","Blanc-Sablon, Canada","Toronto, Canada","Nipigon, Canada","Thunder Bay, Canada","Iqaluit, Canada","Pangnirtung, Canada","Havana, Cuba","Curacao, Curaçao","Dominica, Dominica","Santo Domingo, Dominican Republic","Grenada, Grenada","Guadeloupe, Guadeloupe","Guyana, Guyana","Port-au-Prince, Haiti","St Kitts, Saint Kitts and Nevis","St Lucia, Saint Lucia","Marigot, Saint Martin (French part)","Martinique, Martinique","Montserrat, Montserrat","Puerto Rico, Puerto Rico","Asuncion, Paraguay","Lower Princes, Sint Maarten (Dutch part)","Grand Turk, Turks and Caicos Islands","Port of_Spain, Trinidad and Tobago","New York, United States","Detroit, United States","Louisville, Kentucky","Monticello, Kentucky","Indianapolis, Indiana","Vincennes, Indiana","Winamac, Indiana","Marengo, Indiana","Petersburg, Indiana","Vevay, Indiana","St Vincent, Saint Vincent and the Grenadines","Caracas, Venezuela, Bolivarian Republic of","Tortola, Virgin Islands, British","St Thomas, Virgin Islands, U.S."];
+ case 1140: return ["Eirunepe, Brazil","Rio Branco, Brazil","Atikokan, Canada","Winnipeg, Canada","Rainy River, Canada","Resolute, Canada","Rankin Inlet, Canada","Easter, Chile","Bogota, Colombia","Guayaquil, Ecuador","Jamaica, Jamaica","Cayman, Cayman Islands","Cancun, Mexico","Matamoros, Mexico","Panama, Panama","Lima, Peru","Chicago, United States","Tell City, Indiana","Knox, Indiana","Menominee, United States","Center, North Dakota","New_Salem, North Dakota","Beulah, North Dakota"];
+ case 1080: return ["Belize, Belize","Regina, Canada","Swift Current, Canada","Edmonton, Canada","Cambridge Bay, Canada","Yellowknife, Canada","Inuvik, Canada","Costa Rica, Costa Rica","Galapagos, Ecuador","Guatemala, Guatemala","Tegucigalpa, Honduras","Mexico City, Mexico","Merida, Mexico","Monterrey, Mexico","Ojinaga, Mexico","Bahia Banderas, Mexico","Managua, Nicaragua","El Salvador, El Salvador","Denver, United States","Boise, United States"];
+ case 1020: return ["Creston, Canada","Dawson Creek, Canada","Fort Nelson, Canada","Whitehorse, Canada","Dawson, Canada","Vancouver, Canada","Mazatlan, Mexico","Chihuahua, Mexico","Hermosillo, Mexico","Tijuana, Mexico","Phoenix, United States","Los Angeles, United States"];
+ case 960: return ["Pitcairn, Pitcairn","Anchorage, United States","Juneau, United States","Sitka, United States","Metlakatla, United States","Yakutat, United States","Nome, United States"];
+ case 900: return ["Gambier, French Polynesia","Adak, United States"];
+ case 840: return ["Rarotonga, Cook Islands","Kiritimati, Kiribati","Tahiti, French Polynesia","Honolulu, United States"];
+ case 780: return ["McMurdo, Antarctica","Pago Pago, American Samoa","Kanton, Kiribati","Niue, Niue","Auckland, New Zealand","Fakaofo, Tokelau","Tongatapu, Tonga","Midway, United States Minor Outlying Islands","Apia, Samoa"];
+ case 720: return ["Fiji, Fiji","Tarawa, Kiribati","Majuro, Marshall Islands","Kwajalein, Marshall Islands","Norfolk, Norfolk Island","Nauru, Nauru","Kamchatka, Russian Federation","Anadyr, Russian Federation","Funafuti, Tuvalu","Wake, United States Minor Outlying Islands","Wallis, Wallis and Futuna"];
+ case 660: return ["Casey, Antarctica","Lord Howe, Australia","Macquarie, Australia","Hobart, Australia","Melbourne, Australia","Sydney, Australia","Pohnpei, Micronesia, Federated States of","Kosrae, Micronesia, Federated States of","Noumea, New Caledonia","Bougainville, Papua New Guinea","Magadan, Russian Federation","Sakhalin, Russian Federation","Srednekolymsk, Russian Federation","Guadalcanal, Solomon Islands","Efate, Vanuatu"];
+ case 600: return ["DumontDUrville, Antarctica","Brisbane, Australia","Lindeman, Australia","Chuuk, Micronesia, Federated States of","Guam, Guam","Saipan, Northern Mariana Islands","Port Moresby, Papua New Guinea","Vladivostok, Russian Federation","Ust-Nera, Russian Federation"];
+ case 540: return ["Jayapura, Indonesia","Tokyo, Japan","Pyongyang, Korea, Democratic People's Republic of","Seoul, Korea, Republic of","Palau, Palau","Chita, Russian Federation","Yakutsk, Russian Federation","Khandyga, Russian Federation","Dili, Timor-Leste"];
case 480: return ["Perth, Australia","Brunei, Brunei Darussalam","Shanghai, China","Hong Kong, Hong Kong","Makassar, Indonesia","Ulaanbaatar, Mongolia","Choibalsan, Mongolia","Macau, Macao","Kuala Lumpur, Malaysia","Kuching, Malaysia","Manila, Philippines","Irkutsk, Russian Federation","Singapore, Singapore","Taipei, Taiwan, Province of China"];
case 420: return ["Davis, Antarctica","Christmas, Christmas Island","Jakarta, Indonesia","Pontianak, Indonesia","Phnom Penh, Cambodia","Vientiane, Lao People's Democratic Republic","Hovd, Mongolia","Novosibirsk, Russian Federation","Barnaul, Russian Federation","Tomsk, Russian Federation","Novokuznetsk, Russian Federation","Krasnoyarsk, Russian Federation","Bangkok, Thailand","Ho Chi_Minh, Viet Nam"];
case 360: return ["Vostok, Antarctica","Dhaka, Bangladesh","Thimphu, Bhutan","Urumqi, China","Chagos, British Indian Ocean Territory","Bishkek, Kyrgyzstan","Almaty, Kazakhstan","Qostanay, Kazakhstan","Omsk, Russian Federation"];
case 300: return ["Mawson, Antarctica","Qyzylorda, Kazakhstan","Aqtobe, Kazakhstan","Aqtau, Kazakhstan","Atyrau, Kazakhstan","Oral, Kazakhstan","Maldives, Maldives","Karachi, Pakistan","Yekaterinburg, Russian Federation","Kerguelen, French Southern Territories","Dushanbe, Tajikistan","Ashgabat, Turkmenistan","Samarkand, Uzbekistan","Tashkent, Uzbekistan"];
case 240: return ["Dubai, United Arab Emirates","Yerevan, Armenia","Baku, Azerbaijan","Tbilisi, Georgia","Mauritius, Mauritius","Muscat, Oman","Reunion, Réunion","Astrakhan, Russian Federation","Saratov, Russian Federation","Ulyanovsk, Russian Federation","Samara, Russian Federation","Mahe, Seychelles"];
- case 180: return ["Syowa, Antarctica","Bahrain, Bahrain","Minsk, Belarus","Djibouti, Djibouti","Asmara, Eritrea","Addis Ababa, Ethiopia","Baghdad, Iraq","Nairobi, Kenya","Comoro, Comoros","Kuwait, Kuwait","Antananarivo, Madagascar","Qatar, Qatar","Moscow, Russian Federation","Simferopol, Ukraine","Kirov, Russian Federation","Volgograd, Russian Federation","Riyadh, Saudi Arabia","Mogadishu, Somalia","Istanbul, Turkey","Dar es_Salaam, Tanzania, United Republic of","Kampala, Uganda","Aden, Yemen","Mayotte, Mayotte"];
- case 120: return ["Mariehamn, Åland Islands","Sofia, Bulgaria","Bujumbura, Burundi","Gaborone, Botswana","Lubumbashi, Congo, Democratic Republic of the","Nicosia, Cyprus","Famagusta, Cyprus","Tallinn, Estonia","Cairo, Egypt","Helsinki, Finland","Athens, Greece","Jerusalem, Israel","Amman, Jordan","Beirut, Lebanon","Maseru, Lesotho","Vilnius, Lithuania","Riga, Latvia","Tripoli, Libya","Chisinau, Moldova, Republic of","Blantyre, Malawi","Maputo, Mozambique","Windhoek, Namibia","Gaza, Palestine, State of","Hebron, Palestine, State of","Bucharest, Romania","Kaliningrad, Russian Federation","Kigali, Rwanda","Khartoum, Sudan","Juba, South Sudan","Damascus, Syrian Arab Republic","Mbabane, Eswatini","Kiev, Ukraine","Uzhgorod, Ukraine","Zaporozhye, Ukraine","Johannesburg, South Africa","Lusaka, Zambia","Harare, Zimbabwe"];
- case 60: return ["Andorra, Andorra","Tirane, Albania","Luanda, Angola","Vienna, Austria","Sarajevo, Bosnia and Herzegovina","Brussels, Belgium","Porto-Novo, Benin","Kinshasa, Congo, Democratic Republic of the","Bangui, Central African Republic","Brazzaville, Congo","Zurich, Switzerland","Douala, Cameroon","Prague, Czechia","Berlin, Germany","Busingen, Germany","Copenhagen, Denmark","Algiers, Algeria","El Aaiun, Western Sahara","Madrid, Spain","Ceuta, Spain","Paris, France","Libreville, Gabon","Gibraltar, Gibraltar","Malabo, Equatorial Guinea","Zagreb, Croatia","Budapest, Hungary","Rome, Italy","Vaduz, Liechtenstein","Luxembourg, Luxembourg","Casablanca, Morocco","Monaco, Monaco","Podgorica, Montenegro","Skopje, North Macedonia","Malta, Malta","Niamey, Niger","Lagos, Nigeria","Amsterdam, Netherlands","Oslo, Norway","Warsaw, Poland","Belgrade, Serbia","Stockholm, Sweden","Ljubljana, Slovenia","Longyearbyen, Svalbard and Jan Mayen","Bratislava, Slovakia","San Marino, San Marino","Ndjamena, Chad","Tunis, Tunisia","Vatican, Holy See"];
- case 0: return ["Troll, Antarctica","Ouagadougou, Burkina Faso","Abidjan, Côte d'Ivoire","Canary, Spain","Faroe, Faroe Islands","London, United Kingdom of Great Britain and Northern Ireland","Guernsey, Guernsey","Accra, Ghana","Danmarkshavn, Greenland","Banjul, Gambia","Conakry, Guinea","Bissau, Guinea-Bissau","Dublin, Ireland","Isle of_Man, Isle of Man","Reykjavik, Iceland","Jersey, Jersey","Monrovia, Liberia","Bamako, Mali","Nouakchott, Mauritania","Lisbon, Portugal","Madeira, Portugal","St Helena, Saint Helena, Ascension and Tristan da Cunha","Freetown, Sierra Leone","Dakar, Senegal","Sao Tome, Sao Tome and Principe","Lome, Togo"];
+ case 180: return ["Syowa, Antarctica","Mariehamn, Åland Islands","Sofia, Bulgaria","Bahrain, Bahrain","Minsk, Belarus","Nicosia, Cyprus","Famagusta, Cyprus","Djibouti, Djibouti","Tallinn, Estonia","Asmara, Eritrea","Addis Ababa, Ethiopia","Helsinki, Finland","Athens, Greece","Jerusalem, Israel","Baghdad, Iraq","Amman, Jordan","Nairobi, Kenya","Comoro, Comoros","Kuwait, Kuwait","Beirut, Lebanon","Vilnius, Lithuania","Riga, Latvia","Chisinau, Moldova, Republic of","Antananarivo, Madagascar","Gaza, Palestine, State of","Hebron, Palestine, State of","Qatar, Qatar","Bucharest, Romania","Moscow, Russian Federation","Simferopol, Ukraine","Kirov, Russian Federation","Volgograd, Russian Federation","Riyadh, Saudi Arabia","Mogadishu, Somalia","Damascus, Syrian Arab Republic","Istanbul, Turkey","Dar es_Salaam, Tanzania, United Republic of","Kiev, Ukraine","Uzhgorod, Ukraine","Zaporozhye, Ukraine","Kampala, Uganda","Aden, Yemen","Mayotte, Mayotte"];
+ case 120: return ["Andorra, Andorra","Tirane, Albania","Troll, Antarctica","Vienna, Austria","Sarajevo, Bosnia and Herzegovina","Brussels, Belgium","Bujumbura, Burundi","Gaborone, Botswana","Lubumbashi, Congo, the Democratic Republic of the","Zurich, Switzerland","Prague, Czech Republic","Berlin, Germany","Busingen, Germany","Copenhagen, Denmark","Cairo, Egypt","Madrid, Spain","Ceuta, Spain","Paris, France","Gibraltar, Gibraltar","Zagreb, Croatia","Budapest, Hungary","Rome, Italy","Vaduz, Liechtenstein","Maseru, Lesotho","Luxembourg, Luxembourg","Tripoli, Libya","Monaco, Monaco","Podgorica, Montenegro","Skopje, Macedonia, the Former Yugoslav Republic of","Malta, Malta","Blantyre, Malawi","Maputo, Mozambique","Windhoek, Namibia","Amsterdam, Netherlands","Oslo, Norway","Warsaw, Poland","Belgrade, Serbia","Kaliningrad, Russian Federation","Kigali, Rwanda","Khartoum, Sudan","Stockholm, Sweden","Ljubljana, Slovenia","Longyearbyen, Svalbard and Jan Mayen","Bratislava, Slovakia","San Marino, San Marino","Juba, South Sudan","Mbabane, Swaziland","Vatican, Holy See (Vatican City State)","Johannesburg, South Africa","Lusaka, Zambia","Harare, Zimbabwe"];
+ case 60: return ["Luanda, Angola","Porto-Novo, Benin","Kinshasa, Congo, the Democratic Republic of the","Bangui, Central African Republic","Brazzaville, Congo","Douala, Cameroon","Algiers, Algeria","Canary, Spain","Faroe, Faroe Islands","Libreville, Gabon","London, United Kingdom","Guernsey, Guernsey","Malabo, Equatorial Guinea","Dublin, Ireland","Isle of_Man, Isle of Man","Jersey, Jersey","Niamey, Niger","Lagos, Nigeria","Lisbon, Portugal","Madeira, Portugal","Ndjamena, Chad","Tunis, Tunisia"];
+ case 0: return ["Ouagadougou, Burkina Faso","Abidjan, Côte d'Ivoire","El Aaiun, Western Sahara","Accra, Ghana","Danmarkshavn, Greenland","Scoresbysund, Greenland","Banjul, Gambia","Conakry, Guinea","Bissau, Guinea-Bissau","Reykjavik, Iceland","Monrovia, Liberia","Casablanca, Morocco","Bamako, Mali","Nouakchott, Mauritania","Azores, Portugal","St Helena, Saint Helena, Ascension and Tristan da Cunha","Freetown, Sierra Leone","Dakar, Senegal","Sao Tome, Sao Tome and Principe","Lome, Togo"];
default: return ["Houston, we have a bug."];
}
};
diff --git a/apps/ftclock/metadata.json b/apps/ftclock/metadata.json
index 3cbf5ce66..876feb1bb 100644
--- a/apps/ftclock/metadata.json
+++ b/apps/ftclock/metadata.json
@@ -1,7 +1,7 @@
{
"id": "ftclock",
"name": "Four Twenty Clock",
- "version": "0.02",
+ "version": "0.03",
"description": "A clock that tells when and where it's going to be 4:20 next",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}, {"url":"screenshot1.png"}],
diff --git a/apps/ftclock/mkFourTwentyTz.js b/apps/ftclock/mkFourTwentyTz.js
index 4571c15f7..4e7829aa3 100644
--- a/apps/ftclock/mkFourTwentyTz.js
+++ b/apps/ftclock/mkFourTwentyTz.js
@@ -4,7 +4,7 @@ let csv = require('csv');
let countries = {},
zones = {},
offsdict = {},
- now = Date.now(); // we need this to find zone's current DST state
+ now = Date.now()/1000; // we need this to find zone's current DST state
function handleWrite(err,bytes) {
if (err) {
@@ -19,10 +19,10 @@ fs.createReadStream(__dirname+'/country.csv')
countries[r[0]] = r[1];
})
.on('end', () => {
- fs.createReadStream(__dirname+'/zone.csv')
+ fs.createReadStream(__dirname+'/time_zone.csv')
.pipe(csv.parse())
.on('data', (r) => {
- let parts = r[2].replace('_',' ').split('/');
+ let parts = r[0].replace('_',' ').split('/');
let city = parts[parts.length-1];
let country ='';
if (parts.length>2) { // e.g. America/North_Dakota/New_Salem
@@ -30,59 +30,51 @@ fs.createReadStream(__dirname+'/country.csv')
} else {
country = countries[r[1]]; // e.g. United States
}
- zones[parseInt(r[0])] = {"name": `${city}, ${country}`};
+ zone = zones[r[0]] || { "name": `${city}, ${country}` };
+ let starttime = parseInt(r[3] || "0"), // Bugger. They're feeding us blanks for UTC now
+ offs = parseInt(r[4]);
+ if (offs<0) {
+ offs += 60*60*24;
+ }
+ if (starttime {
- fs.createReadStream(__dirname+'/timezone.csv')
- .pipe(csv.parse())
- .on('data', (r) => {
- code = parseInt(r[0]);
- if (!(code in zones)) return;
- starttime = parseInt(r[2] || "0"); // Bugger. They're feeding us blanks for UTC now
- offs = parseInt(r[3]);
- if (offs<0) {
- offs += 60*60*24;
- }
- zone = zones[code];
- if (starttime {
- for (z in zones) {
- zone = zones[z];
- if (zone.offs%60) continue; // One a dem funky timezones. Ignore.
- zonelist = offsdict[zone.offs] || [];
- zonelist.push(zone.name);
- offsdict[zone.offs] = zonelist;
- }
- offsets = [];
- for (o in offsdict) {
- offsets.unshift(parseInt(o));
- }
- fs.open("fourTwentyTz.js","w", (err, fd) => {
- if (err) {
- console.log("Can't open output file");
- return;
- }
- fs.write(fd, "// Generated by mkFourTwentyTz.js\n", handleWrite);
- fs.write(fd, `// ${Date()}\n`, handleWrite);
- fs.write(fd, "// Data source: https://timezonedb.com/files/timezonedb.csv.zip\n", handleWrite);
- fs.write(fd, "exports.offsets = ", handleWrite);
- fs.write(fd, JSON.stringify(offsets), handleWrite);
- fs.write(fd, ";\n", handleWrite);
- fs.write(fd, "exports.timezones = function(offs) {\n", handleWrite);
- fs.write(fd, " switch (offs) {\n", handleWrite);
- for (i=0; i {
+ if (err) {
+ console.log("Can't open output file");
+ return;
+ }
+ fs.write(fd, "// Generated by mkFourTwentyTz.js\n", handleWrite);
+ fs.write(fd, `// ${Date()}\n`, handleWrite);
+ fs.write(fd, "// Data source: https://timezonedb.com/files/TimeZoneDB.csv.zip\n", handleWrite);
+ fs.write(fd, "exports.offsets = ", handleWrite);
+ fs.write(fd, JSON.stringify(offsets), handleWrite);
+ fs.write(fd, ";\n", handleWrite);
+ fs.write(fd, "exports.timezones = function(offs) {\n", handleWrite);
+ fs.write(fd, " switch (offs) {\n", handleWrite);
+ for (i=0; i 0 ? (m-1)*(cols) + n : -1;}
+ sqIndex: function (i,o) {return i*(cols) + o;}, sqNextIndex: function (i,o) {return i > 0 ? (i-1)*(cols) + o : -1;}
},
left: {name: 'left', step: 1, innerBegin: 0, innerEnd: cols-1, outerBegin: 0, outerEnd: rows-1, iter: cols -1,
- sqIndex: function (m,n) {return n*(rows) + m;}, sqNextIndex: function (m,n) {return m < cols -1 ? n*(rows) + m +1 : -1;}
+ sqIndex: function (i,o) {return o*(rows) + i;}, sqNextIndex: function (i,o) {return i < cols -1 ? o*(rows) + i +1 : -1;}
},
right: {name: 'right', step:-1, innerBegin: cols-1, innerEnd: 0, outerBegin: rows-1, outerEnd: 0, iter: cols -1,
- sqIndex: function (m,n) {return n*(rows) + m;}, sqNextIndex: function (m,n) {return m > 0 ? n*(rows) + m -1: -1;}
+ sqIndex: function (i,o) {return o*(rows) + i;}, sqNextIndex: function (i,o) {return i > 0 ? o*(rows) + i -1: -1;}
}
},
anyLeft: function() {
@@ -200,49 +207,39 @@ const mover = {
});
return canContinue;
},
- nonEmptyCells: function (dir) {
- debug(() => console.log("Move: ", dir.name));
+ moveAndMerge: function (dir) {
const step = dir.step;
// outer loop for all colums/rows
- for (let n = dir.outerBegin; step*n <= step*dir.outerEnd; n=n+step) {
- // let rowStr = '| ';
-
- // Move a number of iteration with the squares to move them all to one side
- for (let iter = 0; iter < dir.iter; iter++) {
-
- // lets move squares one position in a row or column, counting backwards starting from the and where the squares will end up
- for (let m = dir.innerBegin; step*m <= step*dir.innerEnd; m=m+step) {
- // get the array of squares index for current cell
- const idx = dir.sqIndex(m,n);
- const nextIdx = dir.sqNextIndex(m,n);
-
- if (allSquares[idx].expVal == 0 && nextIdx >= 0) {
- allSquares[idx].setExpVal(allSquares[nextIdx].expVal);
- allSquares[nextIdx].setExpVal(0);
- }
- }
- }
- }
- },
- // add up the conjacent squares with identical values en set next square to empty in the process
- mergeEqlCells: function(dir) {
- const step = dir.step;
- // outer loop for all colums/rows
- for (let n = dir.outerBegin; step*n <= step*dir.outerEnd; n=n+step) {
- // lets move squares one position in a row or column, counting backwards starting from the and where the squares will end up
+ for (let o = dir.outerBegin; step*o <= step*dir.outerEnd; o=o+step) {
+
+ let allVals = allSquares.map(sq=>{return sq.getExpVal()});
+ let allLineVals = [];
for (let m = dir.innerBegin; step*m <= step*dir.innerEnd; m=m+step) {
- const idx = dir.sqIndex(m,n);
- const nextIdx = dir.sqNextIndex(m,n);
-
- if ((allSquares[idx].expVal > 0) && nextIdx >= 0) {
- if (allSquares[idx].expVal == allSquares[nextIdx].expVal) {
- let expVal = allSquares[idx].expVal;
- allSquares[idx].setExpVal(++expVal);
- allSquares[idx].addToScore();
- allSquares[nextIdx].setExpVal(0);
- }
- }
+ allLineVals.push(allVals[dir.sqIndex(m,o)]);
}
+
+ let sortedLineVals = allLineVals.filter((val)=>{return val>0;});
+ let zeroLineVals = allLineVals.filter((val)=>{return val==0;});
+ // merge the equally valued adjacent cells
+ let r=0;
+ while (r{return val>0;});
+ sortedLineVals.filter((val)=>{return val==0;}).forEach((zero)=>{mergedLineVals.push(zero);});
+ zeroLineVals.forEach((zero)=>{mergedLineVals.push(zero);});
+
+ let i = 0;
+ for (let m = dir.innerBegin; step*m <= step*dir.innerEnd; m=m+step) {
+ let idx = dir.sqIndex(m,o);
+ allSquares[idx].setExpVal(mergedLineVals[i++]);
+ }
+ debug(()=>console.log("new allSquares values:", allSquares.map(sq=>{return sq.expVal})));
}
}
};
@@ -253,7 +250,6 @@ const dragThreshold = 10;
const clickThreshold = 3;
let allSquares = [];
-// let buttons = [];
class Button {
constructor(name, x0, y0, width, height, text, bg, fg, cb, enabled) {
@@ -295,7 +291,7 @@ class Button {
}
class Cell {
- constructor(x0, y0, width, idx, cb) {
+ constructor(x0, y0, width, idx) {
this.x0 = x0;
this.y0 = y0;
this.x1 = x0 + width;
@@ -303,7 +299,7 @@ class Cell {
this.expVal = 0;
this.previousExpVals=[];
this.idx = idx;
- this.cb = cb;
+ // this.cb = cb;
this.isRndm = false;
this.ax = x0;
this.ay = Math.floor(0.2*width+y0);
@@ -339,6 +335,9 @@ class Cell {
.drawString(char, strX, strY);
}
}
+ getExpVal() {
+ return this.expVal;
+ }
setExpVal(val) {
this.expVal = val;
}
@@ -358,10 +357,6 @@ class Cell {
removeUndo() {
this.previousExpVals=[0];
}
- addToScore() {if (typeof this.cb === 'function') {
- this.cb(this.expVal);
- }
- }
setRndmFalse() {
this.isRndm = false;
}
@@ -410,7 +405,7 @@ function createGrid () {
for (let c = 0; c < cols; c++) {
let x0 = borderWidth + c*(borderWidth + sqWidth) - (rows/2)*(2*borderWidth + sqWidth) + middle.x + Math.floor(sqWidth/3);
let y0 = borderWidth + r*(borderWidth + sqWidth) - (cols/2)*(2*borderWidth + sqWidth) + middle.y + Math.floor(sqWidth/3);
- let cell = new Cell(x0, y0, sqWidth, c + r*cols, addToScore);
+ let cell = new Cell(x0, y0, sqWidth, c + r*cols);
allSquares.push(cell);
}
}
@@ -448,7 +443,7 @@ function addRandomNumber() {
let randomIdx = Math.floor( emptySquaresIdxs.length * Math.random() );
allSquares[emptySquaresIdxs[randomIdx]].setExpVal(makeRandomNumber());
allSquares[emptySquaresIdxs[randomIdx]].setRndmTrue();
- }
+ }
}
function drawGrid() {
allSquares.forEach(sq => {
@@ -483,6 +478,7 @@ function initGame() {
Bangle.drawWidgets();
}
function drawPopUp(message,cb) {
+ buttons.activatePopUp();
g.setColor('#FFFFFF');
let rDims = Bangle.appRect;
g.fillPoly([rDims.x+10, rDims.y+20,
@@ -505,6 +501,7 @@ function drawPopUp(message,cb) {
g.drawString(message, rDims.x+20, rDims.y+20);
buttons.add(btnYes);
buttons.add(btnNo);
+
}
function handlePopUpClicks(btn) {
const name = btn.name;
@@ -512,6 +509,7 @@ function handlePopUpClicks(btn) {
buttons.all.pop(); // remove the yes button
buttons.all.forEach(b => {b.enable();}); // enable the remaining buttons again
debug(() => console.log("Button name =", name));
+ buttons.deActivatePopUp();
switch (name) {
case 'yes':
resetGame();
@@ -568,14 +566,13 @@ function handleclick(e) {
// Handle a drag event (moving the stones around)
function handledrag(e) {
- /*debug(Math.abs(e.dx) > Math.abs(e.dy) ?
- (e.dx > 0 ? e => console.log('To the right') : e => console.log('To the left') ) :
- (e.dy > 0 ? e => console.log('Move down') : e => console.log('Move up') ));
- */
- // [move.right, move.left, move.up, move.down]
- runGame((Math.abs(e.dx) > Math.abs(e.dy) ?
- (e.dx > 0 ? mover.direction.right : mover.direction.left ) :
- (e.dy > 0 ? mover.direction.down : mover.direction.up )));
+ // Stop moving things around when the popup message is active
+ // Bangleapps issue #1609
+ if (!(buttons.isPopUpActive)) {
+ runGame((Math.abs(e.dx) > Math.abs(e.dy) ?
+ (e.dx > 0 ? mover.direction.right : mover.direction.left ) :
+ (e.dy > 0 ? mover.direction.down : mover.direction.up )));
+ }
}
// Evaluate "drag" events from the UI and call handlers for drags or clicks
// The UI sends a drag as a series of events indicating partial movements
@@ -643,9 +640,7 @@ dragger.attach();
function runGame(dir){
addToUndo();
updUndoLvlIndex();
- mover.nonEmptyCells(dir);
- mover.mergeEqlCells(dir);
- mover.nonEmptyCells(dir);
+ mover.moveAndMerge(dir);
allSquares.forEach(sq => {sq.setRndmFalse();});
addRandomNumber();
drawGrid();
diff --git a/apps/game1024/metadata.json b/apps/game1024/metadata.json
index 557d77b89..14e64347a 100644
--- a/apps/game1024/metadata.json
+++ b/apps/game1024/metadata.json
@@ -1,7 +1,7 @@
{ "id": "game1024",
"name": "1024 Game",
"shortName" : "1024 Game",
- "version": "0.05",
+ "version": "0.07",
"icon": "game1024.png",
"screenshots": [ {"url":"screenshot.png" } ],
"readme":"README.md",
diff --git a/apps/glbasic/ChangeLog b/apps/glbasic/ChangeLog
new file mode 100644
index 000000000..89aee01f8
--- /dev/null
+++ b/apps/glbasic/ChangeLog
@@ -0,0 +1,2 @@
+0.20: New App!
+
diff --git a/apps/glbasic/GLBasic-Watchface-Bangle.JS2.js b/apps/glbasic/GLBasic-Watchface-Bangle.JS2.js
new file mode 100644
index 000000000..d51d02a10
--- /dev/null
+++ b/apps/glbasic/GLBasic-Watchface-Bangle.JS2.js
@@ -0,0 +1,114 @@
+
+Graphics.prototype.setFontLECO1976Regular42 = function(scale) {
+ // Actual height 42 (41 - 0)
+ g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAA/AAAAAAAAH/AAAAAAAA//AAAAAAAP//AAAAAAB///AAAAAAP///AAAAAB////AAAAAf////AAAAD////4AAAAf////AAAAH////4AAAA////+AAAAA////wAAAAA///+AAAAAA///gAAAAAA//8AAAAAAA//gAAAAAAA/4AAAAAAAA/AAAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////gD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4B/gH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAH+AAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), 46, atob("ERkmHyYmJiYmJCYmEQ=="), 60+(scale<<8)+(1<<16));
+};
+
+Graphics.prototype.setFontLECO1976Regular22 = function(scale) {
+ // Actual height 22 (21 - 0)
+g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/nA/+cD/5wP/nAAAAAAAAPwAA/gAD+AAPwAAAAAD+AAP4AA/gAAAAAAAAAAAAAcOAP//A//8D//wP//AHDgAcOAP//A//8D//wP//AHDgAAAAAAAAH/jgf+OB/44H/jj8OP/w4//Dj/8OPxw/4HD/gcP+Bw/4AAAAAAAP+AA/8AD/wQOHHA4c8D//wP/8A//gAD4AAfAAH/8A//wP//A84cDjhwIP/AA/8AB/wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8ABwAAAAAAAAD8AAP4AA/gAD8AAAAAAAAAAAEAAD+AB//A///v/D//gB/wABwAAAAAADgAA/wAf/4P8///wf/4AP8AAOAAAAAAAAAyAAHcAAPwAD/gAP/AA/8AA/AAH8AAMwAAAAAAAAAAAAADgAAOAAA4AAf8AD/wAP/AA/8AAOAAA4AADgAAAAAAAAAAD8AAfwAB/AAD8AAAAAAAADgAAOAAA4AADgAAOAAA4AADgAAAAAAAAAADgAAOAAA4AADgAAAAAAAAABwAB/AA/8A//gP/gA/wADwAAIAAAAAAD//wP//A//8D//wOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA4AcDgBwOAHA//8D//wP//A//8AABwAAHAAAcAAAAAAAA+f8D5/wPn/A+f8DhxwOHHA4ccDhxwP/HA/8cD/xwP/HAAAAAAAAOAHA4AcDhxwOHHA4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/wAP/AA/8AD/wAAHAAAcAABwAAHAA//8D//wP//A//8AAAAAAAA/98D/3wP/fA/98DhxwOHHA4ccDhxwOH/A4f8Dh/wOH/AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccDh/wOH/A4f8Dh/wAAAAAAAD4AAPgAA+AADgAAOAAA4AADgAAP//A//8D//wP//AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA//8D//wP//A//8AAAAAAAAOA4A4DgDgOAOA4AAAAAAAAOA/A4H8DgfwOA/AAAAAAAAB4AAPwAA/AAD8AAf4ABzgAPPAA8cAHh4AAAAAAAAAAAAHHAAccABxwAHHAAccABxwAHHAAccABxwAHHAAAAAAAAAOHAA4cADzwAPPAAf4AB/gAD8AAPwAAeAAB4AAAAAAAAA+AAD4AAPgAA+ecDh9wOH3A4fcDhwAP/AA/8AD/wAP/AAAAAAAAAP//4///j//+P//44ADjn/OOf845/zjnHOP8c4//zj//OP/84AAAAAAAP//A//8D//wP//A4cADhwAOHAA4cAD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA//8D//wP9/A/j8AAAAAAAA//8D//wP//A//8DgBwOAHA4AcDgBwOAHA4AcDgBwOAHAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA8A8D//wH/+AP/wAf+AAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4ccDhxwOAHA4AcAAAAAAAA//8D//wP//A//8DhwAOHAA4cADhwAOHAA4cADgAAOAAAAAAD//wP//A//8D//wOAHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA//8D//wP//A//8ABwAAHAAAcAABwAP//A//8D//wP//AAAAAAAAP//A//8D//wP//AAAAAAAAOAHA4AcDgBwOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA//8D//wP//A//8AHwAA/AAP8AB/wAPn/A8f8DB/wIH/AAAAAAAAP//A//8D//wP//AAAcAABwAAHAAAcAABwAAHAAAAAAAAP//A//8D//wP//Af8AAP+AAH/AAD8AAHwAD/AB/wAf8AP+AA//8D//wP//AAAAAAAAP//A//8D//wP//AfwAAfwAAfwAAfwAAfwP//A//8D//wAAAAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHAA4cADhwAOHAA/8AD/wAP/AA/8AAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//+P//4///j//+AAA4AADgAAAP//A//8D//wP//A4eADh+AOH8A4f4D/3wP/HA/8MD/wQAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA4AADgAAOAAA//8D//wP//A//8DgAAOAAA4AADgAAAAAA//8D//wP//A//8AABwAAHAAAcAABwP//A//8D//wP//AAAADAAAPgAA/wAD/4AB/8AA/8AAfwAB/AA/8Af+AP/AA/wAD4AAMAAA4AAD+AAP/gA//8AH/wAB/AAf8Af/wP/4A/4AD/gAP/4AH/8AB/wAB/AB/8D//wP/gA/gADgAAIABA4AcDwDwPw/Afn4Af+AA/wAD/AA//AH5+A/D8DwDwOAHAgAEAAAAP/AA/8AD/wAP/AAAf8AB/wAH/AAf8D/wAP/AA/8AD/wAAAAAAAADh/wOH/A4f8Dh/wOHHA4ccDhxwOHHA/8cD/xwP/HA/8cAAAAAAAAf//9///3///f//9wAA3AADcAAMAAAOAAA/gAD/wAH/8AB/8AA/wAAPAAAEAAAAHAADcAANwAB3///f//9///wAA"), 32, atob("BwYLDg4UDwYJCQwMBgkGCQ4MDg4ODg4NDg4GBgwMDA4PDg4ODg4NDg4GDQ4MEg8ODQ8ODgwODhQODg4ICQg="), 22+(scale<<8)+(1<<16));
+};
+
+
+require("Font7x11Numeric7Seg").add(Graphics);
+
+
+var temperature = 13;
+
+//the following 2 sections are used from waveclk to schedule minutely updates
+// timeout used to update every minute
+var drawTimeout;
+
+// schedule a draw for the next minute
+function queueDraw() {
+ if (drawTimeout) clearTimeout(drawTimeout);
+ drawTimeout = setTimeout(function() {
+ drawTimeout = undefined;
+ draw();
+ }, 60000 - (Date.now() % 60000));
+}
+
+function drawBackground() {
+ g.setBgColor(0,0,0);
+ g.setColor(1,1,1);
+ g.clear();
+}
+
+function digit(num){
+ return String.fromCharCode(num + 48);
+}
+function timeString(h, m){
+ return digit(h/10) + digit(h%10) + ":" + digit(m/10) + digit(m%10);
+}
+function dayString(w){
+ return digit(w/10) + digit(w%10);
+}
+
+function getSteps() {
+ if (WIDGETS.wpedom !== undefined) {
+ return WIDGETS.wpedom.getSteps();
+ }
+ return '????';
+}
+
+
+
+function draw(){
+ drawBackground();
+ var date = new Date();
+ var h = date.getHours(), m = date.getMinutes();
+ var d = date.getDate(), w = date.getDay();
+
+ g.setBgColor(0,0,0);
+ g.setColor(1,1,1);
+
+
+ // g.setFont('Vector', 30);
+ // g.setFont("7x11Numeric7Seg", 5);
+ g.setFontLECO1976Regular42();
+ g.setFontAlign(0, -1);
+ g.drawString(timeString(h, m), g.getWidth()/2,28);
+ g.drawString(dayString(w), g.getWidth()*3/4,88);
+ g.setColor(0,1,0);
+ g.fillRect(0,76,g.getWidth(), 80);
+ g.reset();
+
+ // Steps
+ g.setFontLECO1976Regular22();
+ g.setFontAlign(-1, -1);
+ g.drawString(getSteps(), 8, 88);
+
+ // g.drawString(temperature, 4, 108);
+
+
+ // widget redraw
+ Bangle.drawWidgets();
+ queueDraw();
+}
+
+
+////////////////////////////////////////////////////
+// Bangle.setBarometerPower(true);
+
+Bangle.loadWidgets();
+draw();
+
+
+// Bangle.on('pressure', function(e){
+// temperature = e.temperature;
+// draw();
+// });
+
+//the following section is also from waveclk
+Bangle.on('lcdPower',on=>{
+ if (on) {
+ draw(); // draw immediately, queue redraw
+ } else { // stop draw timer
+ if (drawTimeout) clearTimeout(drawTimeout);
+ drawTimeout = undefined;
+ }
+});
+
+Bangle.setUI("clock");
+
+Bangle.drawWidgets();
\ No newline at end of file
diff --git a/apps/glbasic/glbasic.app.js b/apps/glbasic/glbasic.app.js
new file mode 100644
index 000000000..ff7837ada
--- /dev/null
+++ b/apps/glbasic/glbasic.app.js
@@ -0,0 +1,202 @@
+Graphics.prototype.setFontLECO1976Regular42 = function (scale) {
+ // Actual height 42 (41 - 0)
+ g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAA/AAAAAAAAH/AAAAAAAA//AAAAAAAP//AAAAAAB///AAAAAAP///AAAAAB////AAAAAf////AAAAD////4AAAAf////AAAAH////4AAAA////+AAAAA////wAAAAA///+AAAAAA///gAAAAAA//8AAAAAAA//gAAAAAAA/4AAAAAAAA/AAAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////gD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4B/gH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAH+AAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), 46, atob("ERkmHyYmJiYmJCYmEQ=="), 60 + (scale << 8) + (1 << 16));
+};
+
+Graphics.prototype.setFontLECO1976Regular22 = function (scale) {
+ // Actual height 22 (21 - 0)
+ g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/nA/+cD/5wP/nAAAAAAAAPwAA/gAD+AAPwAAAAAD+AAP4AA/gAAAAAAAAAAAAAcOAP//A//8D//wP//AHDgAcOAP//A//8D//wP//AHDgAAAAAAAAH/jgf+OB/44H/jj8OP/w4//Dj/8OPxw/4HD/gcP+Bw/4AAAAAAAP+AA/8AD/wQOHHA4c8D//wP/8A//gAD4AAfAAH/8A//wP//A84cDjhwIP/AA/8AB/wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8ABwAAAAAAAAD8AAP4AA/gAD8AAAAAAAAAAAEAAD+AB//A///v/D//gB/wABwAAAAAADgAA/wAf/4P8///wf/4AP8AAOAAAAAAAAAyAAHcAAPwAD/gAP/AA/8AA/AAH8AAMwAAAAAAAAAAAAADgAAOAAA4AAf8AD/wAP/AA/8AAOAAA4AADgAAAAAAAAAAD8AAfwAB/AAD8AAAAAAAADgAAOAAA4AADgAAOAAA4AADgAAAAAAAAAADgAAOAAA4AADgAAAAAAAAABwAB/AA/8A//gP/gA/wADwAAIAAAAAAD//wP//A//8D//wOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA4AcDgBwOAHA//8D//wP//A//8AABwAAHAAAcAAAAAAAA+f8D5/wPn/A+f8DhxwOHHA4ccDhxwP/HA/8cD/xwP/HAAAAAAAAOAHA4AcDhxwOHHA4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/wAP/AA/8AD/wAAHAAAcAABwAAHAA//8D//wP//A//8AAAAAAAA/98D/3wP/fA/98DhxwOHHA4ccDhxwOH/A4f8Dh/wOH/AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccDh/wOH/A4f8Dh/wAAAAAAAD4AAPgAA+AADgAAOAAA4AADgAAP//A//8D//wP//AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA//8D//wP//A//8AAAAAAAAOA4A4DgDgOAOA4AAAAAAAAOA/A4H8DgfwOA/AAAAAAAAB4AAPwAA/AAD8AAf4ABzgAPPAA8cAHh4AAAAAAAAAAAAHHAAccABxwAHHAAccABxwAHHAAccABxwAHHAAAAAAAAAOHAA4cADzwAPPAAf4AB/gAD8AAPwAAeAAB4AAAAAAAAA+AAD4AAPgAA+ecDh9wOH3A4fcDhwAP/AA/8AD/wAP/AAAAAAAAAP//4///j//+P//44ADjn/OOf845/zjnHOP8c4//zj//OP/84AAAAAAAP//A//8D//wP//A4cADhwAOHAA4cAD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA//8D//wP9/A/j8AAAAAAAA//8D//wP//A//8DgBwOAHA4AcDgBwOAHA4AcDgBwOAHAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA8A8D//wH/+AP/wAf+AAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4ccDhxwOAHA4AcAAAAAAAA//8D//wP//A//8DhwAOHAA4cADhwAOHAA4cADgAAOAAAAAAD//wP//A//8D//wOAHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA//8D//wP//A//8ABwAAHAAAcAABwAP//A//8D//wP//AAAAAAAAP//A//8D//wP//AAAAAAAAOAHA4AcDgBwOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA//8D//wP//A//8AHwAA/AAP8AB/wAPn/A8f8DB/wIH/AAAAAAAAP//A//8D//wP//AAAcAABwAAHAAAcAABwAAHAAAAAAAAP//A//8D//wP//Af8AAP+AAH/AAD8AAHwAD/AB/wAf8AP+AA//8D//wP//AAAAAAAAP//A//8D//wP//AfwAAfwAAfwAAfwAAfwP//A//8D//wAAAAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHAA4cADhwAOHAA/8AD/wAP/AA/8AAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//+P//4///j//+AAA4AADgAAAP//A//8D//wP//A4eADh+AOH8A4f4D/3wP/HA/8MD/wQAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA4AADgAAOAAA//8D//wP//A//8DgAAOAAA4AADgAAAAAA//8D//wP//A//8AABwAAHAAAcAABwP//A//8D//wP//AAAADAAAPgAA/wAD/4AB/8AA/8AAfwAB/AA/8Af+AP/AA/wAD4AAMAAA4AAD+AAP/gA//8AH/wAB/AAf8Af/wP/4A/4AD/gAP/4AH/8AB/wAB/AB/8D//wP/gA/gADgAAIABA4AcDwDwPw/Afn4Af+AA/wAD/AA//AH5+A/D8DwDwOAHAgAEAAAAP/AA/8AD/wAP/AAAf8AB/wAH/AAf8D/wAP/AA/8AD/wAAAAAAAADh/wOH/A4f8Dh/wOHHA4ccDhxwOHHA/8cD/xwP/HA/8cAAAAAAAAf//9///3///f//9wAA3AADcAAMAAAOAAA/gAD/wAH/8AB/8AA/wAAPAAAEAAAAHAADcAANwAB3///f//9///wAA"), 32, atob("BwYLDg4UDwYJCQwMBgkGCQ4MDg4ODg4NDg4GBgwMDA4PDg4ODg4NDg4GDQ4MEg8ODQ8ODgwODhQODg4ICQg="), 22 + (scale << 8) + (1 << 16));
+};
+
+
+require("Font7x11Numeric7Seg").add(Graphics);
+
+
+// the following 2 sections are used from waveclk to schedule minutely updates
+// timeout used to update every minute
+var drawTimeout;
+
+// schedule a draw for the next minute
+function queueDraw() {
+ if (drawTimeout) clearTimeout(drawTimeout);
+ drawTimeout = setTimeout(function () {
+ drawTimeout = undefined;
+ draw();
+ }, 60000 - (Date.now() % 60000));
+}
+
+function drawBackground() {
+ g.setBgColor(0, 0, 0);
+ g.setColor(1, 1, 1);
+ g.clear();
+}
+
+function digit(num) {
+ return String.fromCharCode(num + 48);
+}
+
+function timeString(h, m) {
+ return digit(h / 10) + digit(h % 10) + ":" + digit(m / 10) + digit(m % 10);
+}
+
+function dayString(w) {
+ return digit(w / 10) + digit(w % 10);
+}
+
+function getSteps() {
+ if (WIDGETS.wpedom !== undefined) {
+ return WIDGETS.wpedom.getSteps();
+ }
+ return '????';
+}
+
+/**
+ * draws calender week view (-1,0,1) for given date
+ */
+function drawCal() {
+ d = /*this.date ? this.date : */ new Date();
+
+ const DAY_NAME_FONT_SIZE = 10;
+ const CAL_Y = g.getHeight() - 44; // Bangle.appRect.y+this.DATE_FONT_SIZE()+10+this.TIME_FONT_SIZE()+3;
+ const CAL_AREA_H = 44; // g.getHeight()-CAL_Y+24; //+24: top widgtes only
+ const CELL_W = g.getWidth() / 7; //cell width
+ const CELL_H = (CAL_AREA_H - DAY_NAME_FONT_SIZE) / 3; //cell heigth
+ const DAY_NUM_FONT_SIZE = Math.min(CELL_H + 3, 15); //size down, max 15
+
+ const wdStrt = 1;
+
+ const ABR_DAY = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
+ const IS_SUNDAY = [1, 0, 0, 0, 0, 1, 1]; // what days are sunday?
+ const nrgb = ["#000", "#FFF", "#F00", "#0F0", "#00F", "#FF0"]; //fg, r ,g , b
+ const suClr = 5; // sunday color fg
+ const tdyMrkClr = 3; // today bk
+ const tdyNumClr = 0; // today fg
+
+ g.setFont("Vector", DAY_NAME_FONT_SIZE + 3);
+ g.setColor(nrgb[1]);
+ g.setFontAlign(-1, -1);
+ // g.clearRect(Bangle.appRect.x, CAL_Y, Bangle.appRect.x2, CAL_Y+CAL_AREA_H);
+
+ //draw grid & Headline
+ const dNames = ABR_DAY.map((a) => a.length <= 2 ? a : a.substr(0, 2)); //force shrt 2
+ for (var dNo = 0; dNo < dNames.length; dNo++) {
+ const dIdx = wdStrt >= 0 ? ((wdStrt + dNo) % 7) : ((dNo + d.getDay() + 4) % 7);
+ const dName = dNames[dIdx];
+ // if(dNo>0) { g.drawLine(dNo*CELL_W, CAL_Y, dNo*CELL_W, CAL_Y+CAL_AREA_H-1);}
+
+
+ var colTx = 0;
+ var colBk = 1;
+ if (IS_SUNDAY[dIdx]) {
+ colBk = suClr;
+ }
+
+ g.setColor(nrgb[colBk]);
+ g.fillRect(dNo * CELL_W, CAL_Y, dNo * CELL_W + CELL_W, CAL_Y + DAY_NAME_FONT_SIZE - 1);
+ g.setColor(nrgb[colTx]);
+ g.drawString(dName, dNo * CELL_W + (CELL_W - g.stringWidth(dName)) / 2 + 2, CAL_Y - 1);
+ // g.setColor(nrgb[clTxt]);
+ }
+ g.setColor(nrgb[1]);
+ var nextY = CAL_Y + DAY_NAME_FONT_SIZE;
+
+ // horizontal lines
+ // for(i=0; i<3; i++){ const y=nextY+i*CELL_H; g.drawLine(Bangle.appRect.x, y, Bangle.appRect.x2, y); }
+
+ g.setFont("Vector", DAY_NUM_FONT_SIZE);
+
+ g.setFont("7x11Numeric7Seg", 1);
+
+ //write days
+ const tdyDate = d.getDate();
+ const days = wdStrt >= 0 ? 7 + ((7 + d.getDay() - wdStrt) % 7) : 10; //start day (week before=7 days + days in this week realtive to week start) or fixed 7+3 days
+ var rD = new Date(d.getTime());
+ rD.setDate(rD.getDate() - days);
+ var rDate = rD.getDate();
+ for (var y = 0; y < 3; y++) {
+ for (var x = 0; x < dNames.length; x++) {
+ if (rDate === tdyDate) { //today
+ g.setColor(nrgb[tdyMrkClr]); //today marker color or fg color
+
+ // rectangle
+ g.fillRect(x * CELL_W, nextY + CELL_H - 1, x * CELL_W + CELL_W, nextY + CELL_H + CELL_H - 1);
+ g.setColor(nrgb[tdyNumClr]); //today color or fg color
+
+ // simulate "bold"
+ g.drawString(rDate, 1 + x * CELL_W + ((CELL_W - g.stringWidth(rDate)) / 2) + 2, nextY + ((CELL_H - DAY_NUM_FONT_SIZE + 2) / 2) + (CELL_H * y));
+
+ } else if (IS_SUNDAY[rD.getDay()]) { //sundays
+ g.setColor(nrgb[suClr]);
+ } else { //default
+ g.setColor(nrgb[1]);
+ }
+ g.drawString(rDate, x * CELL_W + ((CELL_W - g.stringWidth(rDate)) / 2) + 2, nextY + ((CELL_H - DAY_NUM_FONT_SIZE + 2) / 2) + (CELL_H * y));
+ rD.setDate(rDate + 1);
+ rDate = rD.getDate();
+ }
+ }
+}
+
+
+function draw() {
+ g.reset();
+ drawBackground();
+ var date = new Date();
+ var h = date.getHours(),
+ m = date.getMinutes();
+ var d = date.getDate(),
+ w = date.getDay(); // d=1..31; w=0..6
+
+ g.setBgColor(0, 0, 0);
+ g.setColor(1, 1, 1);
+
+
+ // g.setFont('Vector', 30);
+ // g.setFont("7x11Numeric7Seg", 5);
+ g.setFontLECO1976Regular42();
+ g.setFontAlign(0, -1);
+ g.drawString(timeString(h, m), g.getWidth() / 2, 28);
+ g.drawString(dayString(d), g.getWidth() * 3 / 4, 88);
+ g.setColor(0, 1, 0);
+ g.fillRect(0, 76, g.getWidth(), 80);
+ g.reset();
+
+ // Steps
+ g.setFontLECO1976Regular22();
+ g.setFontAlign(-1, -1);
+ g.drawString(getSteps(), 8, 88);
+
+ drawCal();
+
+
+ // widget redraw
+ Bangle.drawWidgets();
+ queueDraw();
+}
+
+
+////////////////////////////////////////////////////
+// Bangle.setBarometerPower(true);
+
+Bangle.loadWidgets();
+draw();
+
+
+// Bangle.on('pressure', function(e){
+// temperature = e.temperature;
+// draw();
+// });
+
+//the following section is also from waveclk
+Bangle.on('lcdPower', on => {
+ if (on) {
+ draw(); // draw immediately, queue redraw
+ } else { // stop draw timer
+ if (drawTimeout) clearTimeout(drawTimeout);
+ drawTimeout = undefined;
+ }
+});
+
+Bangle.setUI("clock");
+
+Bangle.drawWidgets();
diff --git a/apps/glbasic/glbasic.icon.js b/apps/glbasic/glbasic.icon.js
new file mode 100644
index 000000000..0c3280635
--- /dev/null
+++ b/apps/glbasic/glbasic.icon.js
@@ -0,0 +1 @@
+require("heatshrink").decompress(atob("mEw4UA///ssp4XthFCBwUBqoABqAaGBZcFBZdX1W1qgLHrwLKqv/6oLJAAILHioLJn5qBAAYLEBQoLeHQQABv4LjGAgLYq2qAAOlBbBHFBdPAKcQLdWcb7jAAoLcn4LKgEVHQVUBQsAgoLLq//6oLIr2q2oXJBZQvCqALGgILTA="))
diff --git a/apps/glbasic/glbasic_screenshot.png b/apps/glbasic/glbasic_screenshot.png
new file mode 100644
index 000000000..151ee38b9
Binary files /dev/null and b/apps/glbasic/glbasic_screenshot.png differ
diff --git a/apps/glbasic/icon48.png b/apps/glbasic/icon48.png
new file mode 100644
index 000000000..368e08750
Binary files /dev/null and b/apps/glbasic/icon48.png differ
diff --git a/apps/glbasic/metadata.json b/apps/glbasic/metadata.json
new file mode 100644
index 000000000..7c79097da
--- /dev/null
+++ b/apps/glbasic/metadata.json
@@ -0,0 +1,17 @@
+{
+ "id": "glbasic",
+ "name": "GLBasic Clock",
+ "shortName": "GLBasic",
+ "version": "0.20",
+ "description": "A clock with large numbers",
+ "dependencies": {"widpedom":"app"},
+ "icon": "icon48.png",
+ "screenshots": [{"url":"glbasic_screenshot.png"}],
+ "type": "clock",
+ "tags": "clock",
+ "supports": ["BANGLEJS", "BANGLEJS2"],
+ "storage": [
+ {"name":"glbasic.app.js","url":"glbasic.app.js"},
+ {"name":"glbasic.img","url":"glbasic.icon.js","evaluate":true}
+ ]
+}
diff --git a/apps/gpsrec/ChangeLog b/apps/gpsrec/ChangeLog
index 365405846..f923739f0 100644
--- a/apps/gpsrec/ChangeLog
+++ b/apps/gpsrec/ChangeLog
@@ -28,4 +28,5 @@
0.24: Better support for Bangle.js 2, avoid widget area for Graphs, smooth graphs more
0.25: Fix issue where if Bangle.js 2 got a GPS fix but no reported time, errors could be caused by the widget (fix #935)
0.26: Multiple bugfixes
-0.27: Map drawing with light theme (fix #1023)
+0.27: Map drawing with light theme (fix #1023)
+0.28: Show distance more accurately in conjunction with new locale app (fix #1523)
diff --git a/apps/gpsrec/app.js b/apps/gpsrec/app.js
index 833a816ea..527eb780d 100644
--- a/apps/gpsrec/app.js
+++ b/apps/gpsrec/app.js
@@ -248,7 +248,7 @@ function plotTrack(info) {
g.fillCircle(ox,oy,5);
if (info.qOSTM) g.setColor(0, 0, 0);
else g.setColor(g.theme.fg);
- g.drawString(require("locale").distance(dist),g.getWidth() / 2, g.getHeight() - 20);
+ g.drawString(require("locale").distance(dist,2),g.getWidth() / 2, g.getHeight() - 20);
g.setFont("6x8",2);
g.setFontAlign(0,0,3);
g.drawString("Back",g.getWidth() - 10, g.getHeight()/2);
diff --git a/apps/gpsrec/metadata.json b/apps/gpsrec/metadata.json
index 088b8c741..8f4736066 100644
--- a/apps/gpsrec/metadata.json
+++ b/apps/gpsrec/metadata.json
@@ -1,7 +1,7 @@
{
"id": "gpsrec",
"name": "GPS Recorder",
- "version": "0.27",
+ "version": "0.28",
"description": "Application that allows you to record a GPS track. Can run in background",
"icon": "app.png",
"tags": "tool,outdoors,gps,widget",
diff --git a/apps/gsat/ChangeLog b/apps/gsat/ChangeLog
new file mode 100644
index 000000000..48156d0d4
--- /dev/null
+++ b/apps/gsat/ChangeLog
@@ -0,0 +1 @@
+0.01: Added Source Code
diff --git a/apps/gsat/README.md b/apps/gsat/README.md
new file mode 100644
index 000000000..faf986947
--- /dev/null
+++ b/apps/gsat/README.md
@@ -0,0 +1,3 @@
+# Geek Squad Appointment Timer
+
+An app dedicated to setting a 20 minute timer for Geek Squad Appointments.
diff --git a/apps/gsat/app-icon.js b/apps/gsat/app-icon.js
new file mode 100644
index 000000000..06f93e2ef
--- /dev/null
+++ b/apps/gsat/app-icon.js
@@ -0,0 +1 @@
+require("heatshrink").decompress(atob("mEwwIdah/wAof//4ECgYFB4AFBg4FB8AFBj/wh/4AoM/wEB/gFBvwCEBAU/AQP4gfAj8AgPwAoMPwED8AFBg/AAYIBDA4ngg4TB4EBApkPKgJSBJQIFTMgIFCJIIFDKoIFEvgFBGoMAnw7DP4IFEh+BAoItBg+DNIQwBMIaeCKoKxCPoIzCEgKVHUIqtFXIrFFaIrdFdIwAV"))
diff --git a/apps/gsat/app.js b/apps/gsat/app.js
new file mode 100644
index 000000000..3a7d443fe
--- /dev/null
+++ b/apps/gsat/app.js
@@ -0,0 +1,38 @@
+// Clear screen
+g.clear();
+
+const secsinmin = 60;
+const quickfixperiod = 900;
+var seconds = 1200;
+
+function countSecs() {
+ if (seconds != 0) {seconds -=1;}
+ console.log(seconds);
+}
+function drawTime() {
+ g.clear();
+ g.setFontAlign(0,0);
+ g.setFont('Vector', 12);
+ g.drawString('Geek Squad Appointment Timer', 125, 20);
+ if (seconds == 0) {
+ g.setFont('Vector', 35);
+ g.drawString('Appointment', 125, 100);
+ g.drawString('finished!', 125, 150);
+ Bangle.buzz();
+ return;
+ }
+ min = seconds / secsinmin;
+ if (seconds < quickfixperiod) {
+ g.setFont('Vector', 20);
+ g.drawString('Quick Fix', 125, 50);
+ g.drawString('Period Passed!', 125, 75);
+ }
+ g.setFont('Vector', 50);
+ g.drawString(Math.ceil(min), 125, 125);
+ g.setFont('Vector', 25);
+ g.drawString('minutes', 125, 165);
+ g.drawString('remaining', 125, 195);
+}
+drawTime();
+setInterval(countSecs, 1000);
+setInterval(drawTime, 60000);
diff --git a/apps/gsat/app.png b/apps/gsat/app.png
new file mode 100644
index 000000000..cf057046b
Binary files /dev/null and b/apps/gsat/app.png differ
diff --git a/apps/gsat/metadata.json b/apps/gsat/metadata.json
new file mode 100644
index 000000000..878d213e4
--- /dev/null
+++ b/apps/gsat/metadata.json
@@ -0,0 +1,16 @@
+{
+ "id": "gsat",
+ "name": "Geek Squad Appointment Timer",
+ "shortName": "gsat",
+ "version": "0.01",
+ "description": "Starts a 20 minute timer for appointments at Geek Squad.",
+ "icon": "app.png",
+ "tags": "tool",
+ "readme": "README.md",
+ "supports": ["BANGLEJS"],
+ "screenshots": [{"url":"screenshot.png"}],
+ "storage": [
+ {"name":"gsat.app.js","url":"app.js"},
+ {"name":"gsat.img","url":"app-icon.js","evaluate":true}
+ ]
+}
diff --git a/apps/gsat/screenshot.png b/apps/gsat/screenshot.png
new file mode 100644
index 000000000..032319bf6
Binary files /dev/null and b/apps/gsat/screenshot.png differ
diff --git a/apps/locale/ChangeLog b/apps/locale/ChangeLog
index 2dbb4febb..d21cb1a56 100644
--- a/apps/locale/ChangeLog
+++ b/apps/locale/ChangeLog
@@ -15,4 +15,7 @@
0.13: Now use shorter de_DE date format to more closely match other languages for size
0.14: Added some first translations for Messages in nl_NL
0.15: Fixed sv_SE formatting, long date does not work well for Bangle.js2
- Added Swedish localisation with English text
\ No newline at end of file
+ Added Swedish localisation with English text
+0.16: Remove global variables that used RAM
+ Add second 'dp' argument for decimal places in distance/speed/temp (fix #1523)
+0.17: Fix regression where long month names were 'undefined' (fix #1641)
diff --git a/apps/locale/locale.html b/apps/locale/locale.html
index b23225d5f..bac938ffa 100644
--- a/apps/locale/locale.html
+++ b/apps/locale/locale.html
@@ -158,10 +158,10 @@ exports = { name : "en_GB", currencySym:"£",
"%HH": "('0'+getHours(d)).slice(-2)",
"%MM": "('0'+d.getMinutes()).slice(-2)",
"%SS": "('0'+d.getSeconds()).slice(-2)",
- "%A": "day.split(',')[d.getDay()]",
- "%a": "day.split(',')[d.getDay() + 7]",
- "%B": "month.split(',')[d.getMonth()]",
- "%b": "month.split(',')[d.getMonth() + 12]",
+ "%A": `${js(locale.day)}.split(',')[d.getDay()]`,
+ "%a": `${js(locale.abday)}.split(',')[d.getDay()]`,
+ "%B": `${js(locale.month)}.split(',')[d.getMonth()]`,
+ "%b": `${js(locale.abmonth)}.split(',')[d.getMonth()]`,
"%p": `d.getHours()<12?${js(locale.ampm[0].toUpperCase())}:${js(locale.ampm[1].toUpperCase())}`,
"%P": `d.getHours()<12?${js(locale.ampm[0].toLowerCase())}:${js(locale.ampm[1].toLowerCase())}`
};
@@ -182,10 +182,10 @@ exports = { name : "en_GB", currencySym:"£",
var temperature = locale.temperature=='°F' ? '(t*9/5)+32' : 't';
var localeModule = `
-var day = ${js(locale.day + ',' + locale.abday)};
-var month = ${js(locale.month + ',' + locale.abmonth)};
-function round(n) {
- return n < 10 ? Math.round(n * 10) / 10 : Math.round(n);
+function round(n, dp) {
+ if (dp===undefined) dp=0;
+ var p = Math.min(dp,dp - Math.floor(Math.log(n)/Math.log(10)));
+ return n.toFixed(p);
}
var is12;
function getHours(d) {
@@ -197,8 +197,8 @@ function getHours(d) {
exports = {
name: ${js(locale.lang)},
currencySym: ${js(locale.currency_symbol)},
- dow: (d,short) => day.split(',')[d.getDay() + (short ? 7 : 0)],
- month: (d,short) => month.split(',')[d.getMonth() + (short ? 12 : 0)],
+ dow: (d,short) => ${js(locale.day + ',' + locale.abday)}.split(',')[d.getDay() + (short ? 7 : 0)],
+ month: (d,short) => ${js(locale.month + ',' + locale.abmonth)}.split(',')[d.getMonth() + (short ? 12 : 0)],
number: (n, dec) => {
if (dec == null) dec = 2;
var w = n.toFixed(dec),
@@ -215,9 +215,9 @@ exports = {
return s.substr(0, i + 3) + r + (d ? '${locale.decimal_point}' + d: '');
},
currency: n => ${currency},
- distance: n => n < ${distanceUnits[locale.distance[1]]} ? round(${unitConv(distanceUnits[locale.distance[0]])}) + ${js(locale.distance[0])} : round(${unitConv(distanceUnits[locale.distance[1]])}) + ${js(locale.distance[1])},
- speed: n => round(${unitConv(speedUnits[locale.speed])}) + ${js(locale.speed)},
- temp: t => Math.round(${temperature}) + ${js(locale.temperature)},
+ distance: (n,dp) => n < ${distanceUnits[locale.distance[1]]} ? round(${unitConv(distanceUnits[locale.distance[0]])},dp) + ${js(locale.distance[0])} : round(${unitConv(distanceUnits[locale.distance[1]])},dp) + ${js(locale.distance[1])},
+ speed: (n,dp) => round(${unitConv(speedUnits[locale.speed])},dp) + ${js(locale.speed)},
+ temp: (t,dp) => round(${temperature},dp) + ${js(locale.temperature)},
translate: s => ${locale.trans?`{var t=${js(locale.trans)};s=''+s;return t[s]||t[s.toLowerCase()]||s;}`:`s`},
date: (d,short) => short ? \`${dateS}\` : \`${dateN}\`,
time: (d,short) => short ? \`${timeS}\` : \`${timeN}\`,
diff --git a/apps/locale/metadata.json b/apps/locale/metadata.json
index c8908c7a7..54ad64e80 100644
--- a/apps/locale/metadata.json
+++ b/apps/locale/metadata.json
@@ -1,7 +1,7 @@
{
"id": "locale",
"name": "Languages",
- "version": "0.15",
+ "version": "0.17",
"description": "Translations for different countries",
"icon": "locale.png",
"type": "locale",
diff --git a/apps/messages/ChangeLog b/apps/messages/ChangeLog
index c39e7d6fe..416cb1694 100644
--- a/apps/messages/ChangeLog
+++ b/apps/messages/ChangeLog
@@ -39,3 +39,5 @@
0.24: Remove left-over debug statement
0.25: Fix widget memory usage issues if message received and watch repeatedly calls Bangle.drawWidgets (fix #1550)
0.26: Setting to auto-open music
+0.27: Add 'mark all read' option to popup menu (fix #1624)
+0.28: Option to auto-unlock the watch when a new message arrives
diff --git a/apps/messages/README.md b/apps/messages/README.md
index 655c549b9..780d8e50b 100644
--- a/apps/messages/README.md
+++ b/apps/messages/README.md
@@ -20,6 +20,7 @@ to the clock where a ringing bell will be shown in the Widget bar.
is chosen if there isn't much message text, but this specifies the smallest the font should get before
it starts getting clipped.
* `Auto-Open Music` - Should the app automatically open when the phone starts playing music?
+* `Unlock Watch` - Should the app unlock the watch when a new message arrives, so you can touch the buttons at the bottom of the app?
## New Messages
diff --git a/apps/messages/app.js b/apps/messages/app.js
index 403f9b5d8..655fc7122 100644
--- a/apps/messages/app.js
+++ b/apps/messages/app.js
@@ -302,6 +302,11 @@ function showMessageSettings(msg) {
saveMessages();
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0});
},
+ /*LANG*/"Mark all read" : () => {
+ MESSAGES.forEach(msg => msg.new = false);
+ saveMessages();
+ checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0});
+ },
/*LANG*/"Delete all messages" : () => {
E.showPrompt(/*LANG*/"Are you sure?", {title:/*LANG*/"Delete All Messages"}).then(isYes => {
if (isYes) {
diff --git a/apps/messages/lib.js b/apps/messages/lib.js
index 0f514a73d..7d49d4c64 100644
--- a/apps/messages/lib.js
+++ b/apps/messages/lib.js
@@ -57,15 +57,23 @@ exports.pushMessage = function(event) {
// otherwise load messages/show widget
var loadMessages = Bangle.CLOCK || event.important;
// first, buzz
- var quiet = (require('Storage').readJSON('setting.json',1)||{}).quiet;
- if (!quiet && loadMessages && global.WIDGETS && WIDGETS.messages)
+ var quiet = (require('Storage').readJSON('setting.json',1)||{}).quiet;
+ var unlockWatch = (require('Storage').readJSON('messages.settings.json',1)||{}).unlockWatch;
+ if (!quiet && loadMessages && global.WIDGETS && WIDGETS.messages){
WIDGETS.messages.buzz();
+ if(unlockWatch != false){
+ Bangle.setLocked(false);
+ Bangle.setLCDPower(1); // turn screen on
+ }
+ }
// after a delay load the app, to ensure we have all the messages
if (exports.messageTimeout) clearTimeout(exports.messageTimeout);
exports.messageTimeout = setTimeout(function() {
exports.messageTimeout = undefined;
// if we're in a clock or it's important, go straight to messages app
- if (loadMessages) return load("messages.app.js");
+ if (loadMessages){
+ return load("messages.app.js");
+ }
if (!quiet && (!global.WIDGETS || !WIDGETS.messages)) return Bangle.buzz(); // no widgets - just buzz to let someone know
WIDGETS.messages.show();
}, 500);
diff --git a/apps/messages/metadata.json b/apps/messages/metadata.json
index a3d3f4179..6f35f39b0 100644
--- a/apps/messages/metadata.json
+++ b/apps/messages/metadata.json
@@ -1,7 +1,7 @@
{
"id": "messages",
"name": "Messages",
- "version": "0.26",
+ "version": "0.28",
"description": "App to display notifications from iOS and Gadgetbridge/Android",
"icon": "app.png",
"type": "app",
diff --git a/apps/messages/settings.js b/apps/messages/settings.js
index 99843602b..589d603da 100644
--- a/apps/messages/settings.js
+++ b/apps/messages/settings.js
@@ -4,6 +4,7 @@
if (settings.vibrate===undefined) settings.vibrate=".";
if (settings.repeat===undefined) settings.repeat=4;
if (settings.unreadTimeout===undefined) settings.unreadTimeout=60;
+ settings.unlockWatch=!!settings.unlockWatch;
settings.openMusic=!!settings.openMusic;
settings.maxUnreadTimeout=240;
return settings;
@@ -49,6 +50,11 @@
format: v => v?/*LANG*/'Yes':/*LANG*/'No',
onchange: v => updateSetting("openMusic", v)
},
+ /*LANG*/'Unlock Watch': {
+ value: !!settings().unlockWatch,
+ format: v => v?/*LANG*/'Yes':/*LANG*/'No',
+ onchange: v => updateSetting("unlockWatch", v)
+ },
};
E.showMenu(mainmenu);
})
diff --git a/apps/mtgwatchface/README.md b/apps/mtgwatchface/README.md
new file mode 100644
index 000000000..36c0c9efb
--- /dev/null
+++ b/apps/mtgwatchface/README.md
@@ -0,0 +1,5 @@
+## Magic the Gathering Watch Face
+Magic the Gathering themed watch face. Embrace the inner wizzard. Dispay any of the different types of mana on your watch. Which color are you devoted to today?
+
+### Touch Enabled
+Simply touch the screen on the sides to switch the mana colors
diff --git a/apps/mtgwatchface/app-icon.js b/apps/mtgwatchface/app-icon.js
new file mode 100644
index 000000000..61ba9e656
--- /dev/null
+++ b/apps/mtgwatchface/app-icon.js
@@ -0,0 +1 @@
+E.toArrayBuffer(atob("MDCDAf/////////////////////////////////+AAAP/////////////////wAAAAAAAB+v////x//////+AAAAAAAAAAAv///////////wAAAAAAAAAAAhxxxxx/////+OAAAAAAAAAAAP//////////wAAAAAAAAAABxx/////////wAOAAAAAAAAAAAAB////////wABwAAAAAAAAAAAAB//////wAAAOAAAAAAAAAAAAB//////wAAAAAAAAAAAAAAAAB/////+AAAAAAAAAAAAAAAAAAP////wAAAAAAAAAAAAAAAAAAB////+AAAAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAP///wAAAOOAAAAAAAAAP+AAAP///wABx///wAAAAAB////wAB//+AAB////+AAAAAP////wAP///wAB/////wAAAB/////wAB//+AAP/////wAAAB/////+AB///wAP/////wAAAB/////wAB//+AAP/////wAAAB/////+AB///wABxx//wAAAABx///xwAB///wAAAAAAAAAAAAAAAAAAAP////wAAAAAAAABwAAAAAAAB/////+AAAAAAAOBwAAAAAAAP///////wAAAABwBwAAAAABx////wAAAAAAAAAOB+AAAAAAAAAP/x///////wBwBwAB///////x/////////wB+P+AP///////x/x/x//x//wBxxwAB/x/xx//x///+Px+P/wAAAAAP/wOPwP/x/x/wPx/x/wAAAAAB/x//x//x///+OB+P/wAAAAAP/+P+B//x/x/wPwAB/wAAAAAB/x/+B//x///+P/wP/wAAOAAP/+P+P//x/x/wP/x//xwAAAAB/x/xxx/x///+P+P//xwAOAOP/+OOAP/x/x///x///xwAP/+B///////x////+P///x+B//+P///////x/xxxxxxxxx/////xxxxxxxxx//////////////////////////xx///xx/x/x///////x/////wP+P+B+P/wPx+P+OP+OP////xwAPxwB//x/xxx+B//xx////+OOPwP///+OBwAOOP+Px/////x///////x////x///x///////////////////////////w=="))
diff --git a/apps/mtgwatchface/app.js b/apps/mtgwatchface/app.js
new file mode 100644
index 000000000..d54247033
--- /dev/null
+++ b/apps/mtgwatchface/app.js
@@ -0,0 +1,182 @@
+var blueImg = {
+ width : 93, height : 145, bpp : 2,
+ transparent : 1,
+ palette : new Uint16Array([31,65535,65535,59167]),
+ buffer : require("heatshrink").decompress(atob("qoAlgoWVgIWVKqwWXioWVOasAOarJWgEMLikAhZcUgEK4IWToEK2AWV1hcToEO1ZcTCwOqLiYWCLiYWCLiZbCLiYWClRcSCwS5TCwUCLiQWCgBcSCwZcSqECCwJcSCwZcSCwZcSCwOqCwJcRqEDCwRcRCwhcRCwXALiVUCwhcQCwpcQqkACwZcQCwpcQCwPqwAWCLh4WGLiAWFLiAWB2AWCLiBWBCwhcPCwxcPgE60AWDLh9ACwpcBgIWO1gWElWACxsKCwsO0BFMoEOCwsD1CiMqEO1AWEX4JcMCxE6LhlQgWrCwpcNqkDCwxcNCwOqCwpcNCxM6f5dUgGq4AWFf5o8BCwz/NCwOACwz/MBoOwCwyiMoBqBCwyiBIpQWB0AWGG4PACxUKCxAJBaJNQh2sCw7wBIpIWB1AWHRYR0IqkC1YWIHAIuIfoIWJeIMAFw79CCxKVBFxAWBfo50DFxD9BCxLyCUY9AP4IWJP4T9QAAfqFw9QcxKjDFw7mKFxbPBcxIuKZ5guKCx0OHgK4GCxgOBwDSEoDPLAAUK1kBXArPKAAeq4AuDXALPLAAU6Fwi4OAAMD1R0DXBwuDUYa4PAAMCaQhjBCx0AlQuDUJ7SDOgShQaQSjCUKDSDIoNUUJ8CUYiKBUJ+gUYRFBgChPlYwBOgSKQFYJ0COYKKPUAJ0B0EFRQJMBABhxB4D6BgNUgSKOIQInBlWAipzQnQQBhxFBOYLOP1RtBIoJzQgWqIocQOZ8A1SLBIoMEOaL9BIoRzQlWqaIMq4FAAQJzPH4JZChQcBc5pcCgYCBLh4WBLgS2CLhyhBXIS2CLh86OYSiBLiEDaARcSgEPCARcCMQQAQLgQCCACD/CL4QAQLgMBL4QARLgL/QLgsQUR5cGgRFSCYMBUoYAQ1cAqE6IqRwBqjoQOYewipFTOYMFIqZBBgJFBRaMDOYNVIoLRR1QWBqDRSFIMVqkDIqPqwEVIqcq2EFIoMOXSCdBCwNUMIQWP0EBqtVoC6QnS4BCwNQgSnBCx6hBAAJ5CCyZ0BUZwWFOgIuOlQWEOgMKFxvqCwqjCFxnq0AWEOgMKRhcD1SxBCwlQFwK7KhwWHFwIKB4CIJBYQWFFwPqIxPqCxAuDPwJDHEIL8CFww6B3hwFFgIgBCw67BHYWvCof61RxCgoWHXYKXBAAO//4ECMoUVCw4uCh4SCAAarCCxJ1BC4xKCLRBGEAAJDC/4GCLRCNFAA4VLC5IsMC5BZLO5AVLoAA=="))
+}
+var redImg = {
+ width : 130, height : 144, bpp : 1,
+ transparent : 1,
+ palette : new Uint16Array([63488,65535]),
+ buffer : require("heatshrink").decompress(atob("/4AD8EAh4HEAA38C4UHB5QeBAAQhKwA5Ev4PI/APEgYxK+AQDj5SOgJjLEIYgH/hbDKgQgHJ4JbCKgYgGBQKNDIJIaCII0/Lg4pC/y1IBISMD4CkIc4owDYoprCHIQPDKIopCDAS0EKIjwCIAT2EKIhwCFAQlCCQRwGB4JlCAgJ5C/ZwEBQIkCGgIgBgf+OYRgBF4QEBB4IZCv/PHQZPCYwgJBh/zLQZ4DwAPCCgMBGATRF/ydDGAWPMAbqFGAnhCoXAZIowE/gLBwHweITsHv43B8F8bIowF4LLBv+HaAQcBOwTWBgP4v/8n/jFQUHFgLfBLYV+FgM//AqDK4KCCfgWDDgRgC/EPBYP+VgY+BBgPfRgYLBn+fIAMALo4OBFYPjIAUPK4SMFFYJcBAgXgTwwKBIAMPEoV4TwzoBIAODIAUeGBMPVYIEBg+AVowKC/EOGoTPHGAMB/8BwAPBniYCAAZsCOoY1BCwIAFDYLRCAAIjBZIJRGv7VBAAVgUQxRBUwkAjBxGAAYwDgwnBB5AwEwBxGKgoACOIwwHRAQAIQoQACOIwwIOI7XFAAJxKa4QACB5RREIBQgEOJTUFB5RiEWRJiFWRP9cohAJ/gqBUgRAJ/0f+LlDIBH+g/4v5RCIBH+gP4n/gIBcAv0PMIRAJFQMDeoZAIFQPAWYbCIFQPwv6BCh57DAgYKBvEfKAUDBQQWBMwQKBjxyBGATJFv6MCg+BSIZACbIYaCgfgv7SFB4YAD/EfaQoPHn0H+CRECoQAEj+BKAoPHSIN+KAh2CAAkD+EfKAg1CAAv8g5KCKAI1DAAl/gKhEa4ZQF4EgKAaWDAAkPHIjyBB48HFIhQBSA6LBEoiAIHQIJDMAJwIv4JDEoJwIn5AEMBJKBMBpKFYIJgHB4JADAoLRHLQJADYJIJBHQbBJBII6DOAJQHB4Q6DAoKhGDIQ6DOAJQHB4I6DOAPIWA46ESwPGUBBQDQAPhSIxwCJQQFB8FAMBChCAoPwmBQIMARmB+EMIBBgDCgMDYJl//EBSJH+EoY0BuAgGn4PDh4PBnjCHB4cHB4MPB40AgQUDB4MHIAwAFQgMBQ4QAMM4YALn4POj/gB5pzCABh4EABSpBwAQN/5AOagJwO/4PPWRiAB/6yNB4JxNB4JxNv4PBOJaPBAALjLh4PCOJQOEOJQ9CAASiIHoYwLgYPGMI4PHGA4PHGA4/HGBBfFSRJ/FAATDHB470HII70Kn6zOB4qzIgEfWZoPHQQ4vGKJKTHEA6CHEAzTIEAxOGMQ7iIaosHBhA"))
+}
+var greenImg = {
+ width : 142, height : 145, bpp : 1,
+ transparent : 1,
+ palette : new Uint16Array([2016,65535]),
+ buffer : require("heatshrink").decompress(atob("/4AF/kAv4JGAA4aBn4QN+ARQHwQRNEQRHOwARPNAJ0JAAvgCAUBK58AgYQL/ARDg4RL4ARDgkAh40NEpg0EAAQjJGggRMCA0Aj6/Gg40HbA3+Q4KeDAAjGFBwMDaYYAEIZrGIIZCOIPA58JCBRqFGhZqFGhZqFCBcDCAf8CJZYEVpDVIVpBYHaYJYP+ARMEYZ7MEYgQMCIZ7MfIhGNfIZGNUAaNMfIaNNLAf4LCDUMLAhGQRppGDGhsDGiCNCPZ0fPZ4RDYRpqDCBsAv40PPgQQOYYIiGh6OJRgyCC+DVFYI0DFQMDCIsfAwoZBZIMPBQs/Gg0PSgM/bwrBHnx1CDgkBGg0AvBGBDgsHYI0BIwSGFj7OKIwosBAAccXghjFC4kHF4cDTAsDC4iTERo0OfwpeDTA04dgoMDZoxGERAkDNIpGFgJYDh7MGRpMwAwt8CJJXFahAAIg5NFABUfFQoAK/4QPQ4gAMZIwAFXok/ZwcAhAvFDol+BYl4MgodECwl/AokDdgkBCwYKBDgl/MgkGUgn/UocB/4pEKQgRBfAYpBZBQjEg46ECI7JPh63RIwbJZVZQRMLBMQAws+Aws/DAVgBQrnGSoUDFo1ASgrdChxMLCAKnCvhdLCIQnBQwcPCAr4BAAK5Bv7fDv6LFh4RC/4nBBgQbBCIoQDG4YbDCIhoCAAbxDv4RFK4YAC/gJBgZyDGhJqCDYQQDDAQRHHQppFAAR8BBIX4cYgRG4CWDUoZGHEYJoBFAZ7IAAxqDPYylJSgQAKSYTlEABJYDIxpYDPY5YJCBixDYQ4AFWIbCHI35G/I0oQCgF/Gh8BERnALBxoDAAMHEJxqMIgY1NVwYRMEQw2KEQyQJ/gQICI6/DAAyLLAAi0GK5CiI+ARJfY3we4oACj7VOERB+KYhPwgcAhDWN4EcgF4fRuA/EDOAP8RgxYEg/gj/gGRRYCj/Av4TBCJaQBEAITBCJfn/xTB4IQLGwQgMAAf5DRAA="))
+}
+var whiteImg = {
+ width : 153, height : 145, bpp : 2,
+ transparent : 0,
+ palette : new Uint16Array([65535,65535,65504,65505]),
+ buffer : require("heatshrink").decompress(atob("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"))
+}
+var blackImg = {
+ width : 152, height : 144, bpp : 1,
+ transparent : 1,
+ palette : new Uint16Array([0,65535]),
+ buffer : require("heatshrink").decompress(atob("/4AGwAKBg4LHAAoeEh4RL8AyNEhAABj4RJ/ASGgYSJ4BsPG5AABn4RH/gSIJhHwCRCaIQQxfLXIQAGgJwQORISvJY/4OJISH8CERwASJVQ3+gC9JjwmF/EAuASImByFEYN4JRB7Bv7hFCREHAQM/cIs8VRMAj4SDAwISLh5wEM4ISKOQYSCj4SKX4f8FgROBfBiWBNAL3JAAKFCXQUDXxISEcAZPCABCYCCQZyLTARHLTAwSPg7PDCSAROXwYSPXwJ/LCS7RBCSN/eoQShn/4CUcfCSXwCUcPCSXgCSEHCWZHDgfAC4bfMCQn//wRGgf//4SFg4IBDAY3DBIYSDn4IBTowJCJgMBwA3CAAJCCAAYJCDgISCgIICIQQACBIf4CQZTCAAIsCKggvCCQQIDL4pdCCQoIDKoQACj4ICR4ISCBAYSFOASiFCQiFEv5oHDYYSFBAYSJPYISUUAiOECSRUECQazECRLkDCVbaDCRDsECSo6BCRwQBCQyEDCRKqICQoHBHQMPew4SE/zEBCQLHEM4YSEFwISCEwigDCQngCQgTCJQarEPIUBCoIAPgYSSNQYADgUAsASHIooABgwSIg7JDCRyaBCR5/BCRFwBI0fW4gSMeYJyGhwSKL4wSJaoQSHvAIFgLoECQqhGfgY5FCRBwBAARzECRF/CQYmEjwSGVAIAEJoYSGGwgADwASHNwYSOGwwSGgISDXIISQCJASDnwSDJRISEgYSCgYSMn4SDZgYSKw4SR/4SQbQISVLxt4OKIABnASRAAQS/CRb2NCX4S/CTkACRsMCR9ACQUCCRtgCQUGCX4SGuASChwSN+ASVjwSPEYM+CRv4CQV+CSP+CSEHCR38gEP/4SggIOBCSUfCRvAgP/gE/CRvgCQOAAwUAAwIAI/EDAwgSL/4SFwAGFAAvnKQsHAwgA=="))
+}
+var backgrounds = [blackImg, blueImg, redImg, greenImg, whiteImg]
+var manaColors = ["#000000","#0000ff","#ff0000","#00ff00","#ffff00"]
+var bgIndex = 0;
+g.width = g.getWidth();
+g.height = g.getHeight();
+
+//console.log(backgrounds.length);
+
+//var Layout = require("Layout");
+//var layout = new Layout();
+
+Graphics.prototype.setFontTreasurehuntDOYwE_20 = function(scale) {
+ // Actual height 20 (19 - 0)
+ this.setFontCustom(atob("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C////4AP////gA/7/q+ADwB8A8APQLwDwAvS/APAC//8A4AP//0DAAv/f0sAAvg//gAAED/8AAAAL/gAAAAL4AAAAAEAAAAAAAAAAC//8AAAv//8AAL///8AA//7/wAH4AF/AAtAAB9ADwAAD4APAAALgA8AAAtADwAAC0APQAAPAAvAAA0AA8AALAADgAA0AAEAAAAAAAAAAAAHgAFbwAv///+AD////4AP////wA/v+q/AD8EAH8APwAAfgA+AAB+AD8AAD4APwAAPgA/gAA8AC/QAHgAP/kB9AAv/+/wAA///9AAB///QAAAf/0AAAAEQAAAAAAAAAAAH/4AAAD//8AAAv//8AAH///4AA/r//wAH0fB/AAsB4B+ADwHQD8APAcALwA8BwAeADwDACwAPAcAPAA+BwA8AD8LQHQALwtA8AAOBgCQAAQAAAAAHVURWQAv///+AC////4AP////gA//+q+AD4BwC8APQDADgA8AMANADwAwAAAPQHQAAA/AcAAAD8AQAAAPwAAAAA8AAAAABgAAAAAAAAAAAAAf//QAAD///QAAv///AAH/qv+AAvAAL8ADwAAPwANAAAvAB0BQA8ALQPADwAtA8APAC0DwA4ALwOAHgAPw9A8AA/D//gAC0P/8AABAr5AAAAAAAAAH/r5rgAf///9AB////0AL+///gAYAOQeAAAA0AUAAADQAAAAANAAAAAA4AAAKADwAQA+7/rvAD////4AL////gAv+v6+ACgAAAsAAAAAAQAAAAAAAB1BUAoAH//+/gAf///+AC////4APqv+vgAQAAAKAAAAAAEAAAAAAAKQAAAAP/AAAAb3//v+v+P/////w/////9D/+v/5AFAAEAAAAAAAAAAPVaVSgA////+AC////4AP////QA////+ACUf9V4AAD/4DgAAv/0EAAH9/wAAB/i/0AAP4D/wAD+AL/gAPgAL/AA8AAL+ACAAAH8AAAAAL4AAAAAPwAAAAAfAAAAAAsAAAAAAwAAAAADAAAAAAIAAAAAAADgFBQYAP7///wA////+AD////4APuvv/wAUAQAfAAAAAA4AAAAADgAAAAAOAAAAAA9AAAAAD8AAAAAeQAAAACwAAAAAFAAL///0AA/+lbwAP/AALQA/wAAIAD9AAAAAPwAAAAA/AAAAAD9AAAAAH9AAAAAD+AAAAAH/UBQAAH///gAB////AAf///4AD//6vQAv+oAEAD/QAAAAP8AAAAAvQAAAAC0AAAAAPgAABAAfgAAsAA/QALwAB/Qb/AAB///8AAB///gAAB//8AAABqUAAAAAAAAD+pZuq/v/////k/////kD/+AAAAO/8AAAAS/8AAAAD/wAAAAH/wAAAAP/AAAAAv9AAAABv4AAAQA/0AADAD/wAANAH/AAA0AL9AADkVf9AAP///9AA////8AD5ABWQAEAAAAAAAAAAAAAAH//8AAA///4AAP+r/wAB9AH/AALgAH8AB4AAH0ALAAALgA8AAAfADwAAA8APQAADgA/QAAMAC/QAGwAL/lC9AAf//vwAA///9AAB//+QAAB//wAAAAWQAAAAAAAAAIAAAAAH1WpVVB7//////P/////4/+///+DVRfVUAMAA8AAAwADwAADQAPAAAPAA4AAA/gHAAAD/74AAAP//AAAAf/8AAAAb+AAAAAFQAAAAAAAAAAAAD//8AAA///4AAL///wAA9QL/AALgAH9AB8AAH0ALQAALgAsAAAfADwAEA8APAA8DgA/AD4NAC/AL2wAH/UP9AAf//v0AA////wAB////AAB//x8AAAagA0AAAAADQAAAAAJAAAAAAACwAAAPAPVWqr4Av////AC////0AH///8AAf/7/AAB9QAQAAHAAAAAAcABgAAB8APAAAH4B/QAAf5v/QAA////AAD//f+gAD/0v/wAHpAv/wAAAAL/0AAAAL/gAAAAH/AAAAAH8AAAAALwAAAAAPAAAAAAcAAAAAA0AAAAABQAAAAAAAP/AHwAC/+AvQAP/8C9AC2/0D0APA/gPgA8C/AvADwL8B8APA/wHgA8D/QtADwP/rwAPgv//AAvA//wAB0D/+AAAAH/QAAAABUAAAAAAAAALgAAAAA8AAAAADwAAAAAPAAAAAA8AAAAADwAAAAAPQAARQA//7//AD////8AP////gA////+AD1WQD8APAAAHwA8AAAOADwAAAQAPAAAAAA+AAAAAD8AAAAAPwAAAAA9AAAAADgAAAAAEAAAAAA4L/5AAD2//8AAP///+AA////8AD/mv/wAPkAH/AAsAAH8AAAAAL4AAAAAPwAAAAA/AAAAAD8AAAAALQAAAAAtADAAADwAOQAJPAA////8AD////4AP////0A//v//wBkAAL/AAAAAC8AAAAABgAAAAAAAPgAAAAA/0AAAAD/4AAAAL/+AAAAL//QAAAG//0AAAB//1AAAAv/9AAAAf/4AAAAG/gAAAAv+AAAAL/0AAAf/5AAAv/4AAAf/5AAAC/9AAAAP+AAAAC+QAAAAPgAAAAA8AAAAAAAAAAAAAAAAAAAf+lf0AB/QAfwAHwAAvgAIAAAfAAAAAA8AAAAALwAAAAB/AAAAAP8AAAAB/wAYAL//AD+///0AP///9AA///+QAC+//8AABQBv4AAAAAv0AAAAAP4AAAAAbwAAAAA/QAAAAD9AAAAAP0AAAAB/QCwAAf9AH0Ab/wAP+//8AAf///wAAv//4AAAb/5AAAAAAAAAFAAACwA4AAAPADgAAH8AP0AB/QA/5Av0AC/+H+AAD///gAAG//8AAAC//0AAAC//0AAAP//5AAH/v/4AB/0b/gAP4AH+AC+AAH8APQAAHgA4AAALACAAAAUAAAAAAAA8AAAAAD9AAAAAP8AAAAA/8AAAABv8AAoAAf9QLwAA////AAAf//4AAA///gAkf/+/AD/+AAoAP/QAAQA/gAAAAD4AAAAAPAAAAAA0AAAAAAAAAAAAP8AAfgA/wAC/AD9AAv4APQAb/QA8AL/8ADwH//wAPG//vAA+//4+AC//9D4AP/+QPQA/9AA9AD+AAD0APQAALAA8AAAtACgAAD9AAAAAP0AAAAApAAAAAAAAAAAAAAAAAAAAAZVUAAAR/////vH7////8cABVVXxwAAAAHCAAAAAYAAAAAAQAAAAAAC4AAAAAP9AAAAAf/QAAAAC/0AAAAAv+QAAAAb/1AAAAB/+QAAAAf/AAAAAG8AAAAABAAAAAAAAAAAAAAGAAAAAkcAAAADh1AUAAOL/////8v/////hQAAFVVAAAAAAAAAAAAAAAGAAAAAB0AAAAAfAAAAAHwAAAAAvQAAAAAfAAAAAAcAAAAAAQAAAAAAAAAAEAAAAABwAAAAAPAAAAAA8AAAAADwAAAAAPAAAAAA8AAAAADwAAAAAHAAAAAAIAAAAAAANAAAAAA8AAAAAB4AAAAAAQAAAAAAAAAAAAAAABwAAAAAdAAAAAHwAABAH9AAAMA/wAABwH+AAAH4/0AAAP/7QAAAv/dAAAAv/0AAAAv/gAAAAL/0AAAAH/QAAAAC9AAAAAC4AAAAACgAAAAAFAAAAAAAAACAAAAAAsEAIAAD/6/0AAL///AAA///8AAD1fB0AALQ8DgAAvvwNAAD//RwAAH+/uAAAGS/wAAAAH/AAAAALgAAAAAAAAAAD/+AAAAv/+AAAH//8AAAtAH0AADQALgAAMAAPAAAwAA8AAHQADQAAPAAcAAA8ADwAADwANAAAFAAQAAAAAAAAALQRXgAA///9AAD///4AAP///gAA8VA9AADwAD0AAPAAPQAA9AA9AAD8ADwAAP5AOAAA//rwAAB//+AAAB//gAAAAqoAAAAAAAAAAAP/4AAAC//4AAAf//wAAC09vQAANDQuAAAwMA8AADAwDwAAdDANAAA8MBwAADw0PAAAPDQ0AAAUABAAAAAAAAAAlUBVAAD///4AAP///gAA///+AAD1tS8AAPA0DgAA4DQJAADgOAAAAPA0AAAA8CAAAADwAAAAANAAAAAAAAAAAAAD/+AAAAv/+AAAH/v8AAAtAH0AADQALgAAMCAPAAAwMA8AAHQwDQAAPDAcAAA8PnwAADw/9AAAFCqQAAAAAAAAACAABQAAP76tAAA///0AAH///QAAZG1dAAAADAUAAAAMAAAAEAwAAAC0XVUAAL///gAAf//9AAB6r64AAFAABQAAAAAAAAA1FAkAAD///QAAP//9AAB+//0AABAACQAAAAAAAAAAAAAAC9QABbgL////8Av////QC////0AHVVlUAAAAAAAAAHq6qwAAf///AAB///8AAL///wAAUf8LAAAD/0EAAAfv0AAAH4v0AAA+A/wAAHwB/QAAcAC/AAAQAB+AAAAAB8AAAAADwAAAAAHAAAAAAMAAAAAAgAAAAAAAAFAAAAAA4VVDAAD///8AAP///wAA////AABQEB8AAAAABwAAAAAHAAAAAAeAAAAAB8AAAAALQAAAAA0AAAAAAAAAC//wAAAf//gAAD+UfAAAfwAIAAB8AAAAADwAAAAAPQAAAAAvgAAAAAvkFAAAA//8AAAP//wAAC///AAAP6AEAAA/AAAAADwAAAAAOAAAAAA9ABwAAC8AvAAAD+v8AAAC//gAAAC/4AAAABQAAAAAAAAAC4ABAbgL/7//8Av///9AD/+RUAALv8AAAAAv4AAAAB/wAAAAC/QAAAAH+AAAAQH8AAAHAP4AAAtAfwAAD5V/gAAL///gAAf6q+AABkAAAAAAAAAAAAABv/QAAAP//AAAD//9AAAfQv4AADwAfgAAdAAPAABwAA8AALAACwAA9AAPAAB9AA0AAH+UfAAAP//0AAA//+AAAA//QAAAAaUAAAAAAAAAAHAAAcAAsAAHgAC7r/8AAH///gAAf//4AAB/u+AAAHUDgAAAcANAAAB4C0AAAH4fAAAAP/4AAAA//AAAAB/gAAAABUAAAAAAAAAAAAH/9AAAB//8AAAP//0AAB5B/gAAPAAvAAB0AA8AAHAADwAAsAULAAD4C48AAH0H3QAAf+X8AAA///wAAC///gAAC/9eAAAAZAsAAAAAAwAAAAABAAAAAAAAAHAAAcAAsAAHgAC7r/8AAH///gAAf//4AAB/u+AAAHUAAAAAcAIAAAB4C0AAAH4f0AAAP//0AAA/+/wAAB/h/QAABUB9AAAAAD0AAAAAHQAAAAAMAAAAAAAAAAPgBAAAD/Q8AAAf+DwAAD78LQAANHweAAA0PA4AADQ+DQAAOD+8AAA8P/wAADw/9AAAFB/QAAAAAUAAAAAAAAAAPAAAAAA4AAAAAHQAAAAAdAAAAAAwAAAAAHAAAAAAelVmAAB///8AAH///wAAf///AAC1VA8AALAABwAAcAACAABwAAAAAHwAAAAAvAAAAAC4AAAAAGAAAAAAAAAAAAAQAAAAADCvQAAAO//gAAA///gAAH+v/AAAOQH8AAAgAHwAAAAAPQAAAAAuAAAAACwAABAAHAAANAF8AAA///wAAD///AAAP//8AAAkAGgAAAAAAAAAfQAAAAB/gAAAAD/0AAAAL/5AAAAG/9AAAAB//QAAAAv+AAAAAL4AAAAH/gAAAb/4AAAL/0AAAB/4AAAAP5AAAAA9AAAAAHQAAAAAEAAAAAAAAAAAAB//QAAAP//QAAA/lvAAADwAuAAAIAA8AAAAACwAAAAAvAAAAAD8AAAlb/wAAH//9AAAf//QAAB+v9AAAAAH8AAAAAG8AAAAADwAAAAAPQAAAAB9AAA0AP0AAD6r+AAAH//0AAAL/9AAAABVAAAAAAAAAABwAA8AALQAfQAAf0H4AAA/5/AAAC//wAAAB//AAAAB//QAAAf//QAAH+L8AAA/AL0AAHgAHQAAcAAKAAAQAAAAAAAAAAAAMAAAAAB8AAAAAH8AAAAAP8ABAAAb5Q4AAAP//wAAAL//AAAi//8AAH/lBwAAf0ABAAB8AAAAADAAAAAAIAAAAAAAAAAAAH8ALgAAfgA/AAB4Af4AALAL/gAA8L/9AAC7/94AAD//bwAAP/guAAA/QB0AAHkADQAAcAANAABgAB8AAAAAHgAAAAAAAAAAAAAAAAXQAAAH//+QAA/+L/gALUABbAAkAAAIAAAAAAAAqpVUABX/////+fq////0AAABFVAAAAAAABAAAAEAHQAAHQAP/hv8AAb///AAAALkAAAAAEAAAAAAAAAAAAAAAAAAAOAAAAAC0AAAAAPAAAAAA9AAAAAA4AAAAAHgAAAAAtAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="), 32, atob("BgYHDQsQEwQKCAgLBgkGDA4GCwoPCwwMCwwGBgwJCwoNGREQExEQEREICBcOHRUTERUaEBYXFh0TERIKCwcKCgYSDg0PDQ0NDgcGEg0XERAPEhINExERFw4ODwYFBgoK"), 20+(scale<<8)+(2<<16));
+ return this;
+}
+
+Graphics.prototype.setFontTreasurehuntDOYwE_40_N = function(scale) {
+ // Actual height 40 (39 - 0)
+ this.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAA8AAAAABwAAAAAHAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAB4AAAAAfwAAAAB/AAAAA/4AAAAH+AAAAB/gAAAAf4AAAAH+AAAAB/gAAAA/4AAAAP+AAAAB/wAAAA/8AAAAf/AAAAD/gAAAAf4AAAAA+AAAAADwAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAB/AAAAAP/wAAAB//wAAAf//wAAD///AAAf8H+AAB+AH8AAPgAHwAB4AAPgAHAAAeAA4AAB4ADgAAHgAOAAAOAA4AAB4ADgAAHgAPAAAeAA+AADwAD8AAeAAH4AD4AAf4APgAA/8H8AAD///gAAD//8AAAH//gAAAH/4AAAAAYAAAAAAAAAAAAAAAAAAAAAAAAYAABAADgAAeAAf///4AD////gAP///+AA////4ABwAADgAAAAAGAAAAAAAAAAAAAAABwAAGAAPgAA4AB8AAHgAHwAA8AA8AAHwADgAA/AAMAAP8AB4AB/4AHgAf3gAPAH8eAA8B/B4AD4P4HgAP/+AeAA//wB4AB/8AHgAD/AAPAAPgAB8AAAAAPwAAAAB+AAAAAHgAAAAAAAAAAAAAAAAAAAAABgAAPwAOAMA/AB4DwAcAHAeAA4AOD4ADgA4/AAMADn8AAwAP/wADAA/3AAcAD8cADwAPh4AeAB4HgH4AHAfB/gAAA//8AAAB//gAAAD/8AAAAH/AAAAAEAAAAAAAAAAAAAAAAAAA4AAAAADgAAAAAeAAAAAD4AAAAAPgAAAAB+AAAAAP4AAAAB/gAAAAHOAAAAA44AAAAHjgAAAA8OAGAAPg4AYAD8Dh/gAP///+AB////wAH////gAP///+AA4BwA4AAAHAAgAAAMAAAAAB4AAAAAHgAAAAAeAAAAAB4AAAAAPAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAcAPAAADwAMAAA+AAwAAP4ADAAD/AAOAA/8AA4AD/wADAAP3AAMAA8cABwADhwAfAAOHgD4AA4fAfgADh//8AAfD//gAB8P/8AAHwf/gAAeB/4AABwD/AAAAAAAAAAAAAAAAAAAf/AAAAH//AAAA//+AAAP//4AAB/5/gAAP8A/AAB/wB8AAf3ADwAD8cAPAAfDwA4AB4PADgAPA8AeAB4DwDwAPAHgPAB4Afj4APAB//AB4AD/8APgAH/AA8AAP4ADgAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAPAAAAAB4AADgAPAAA+AB8AAP4AHwAD/gAfAAf4AB8AH+AADwA/gAAPAP4AAA8D+AAADw/wAAAPP8AAAA9/AAAAD/4AAAAP+AAAAA/gAAAAD4AAAAAfAAAAAAwAAAAACAAAAAAAAAAAAAAAAAAAAAAH8AAAAA/4AAAAP/wAADh//AAA/Pw+AAH+8B4AA//AHgAH/8AeAAcHgA4ADgeADgAOB4AOAB4HwB4AHg/gPgAfn/x8AB/9//gAD/3/8AAP+P/gAAPwf4AAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAACAAfgAAcAH/gADwA//AAeAD/8ADwAf/4AeADwHgDwAeAeAfABwA4D4APADgfAA4AOD4ADgA4/AAPADn4AA+AN/AAD4A/4AAP8f+AAA///wAAB//8AAAH//gAAAH/4AAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAHAeAAAA+D4AAAB4PwAAAHg/AAAAcB4AAAAAAAAAAAAAA"), 46, atob("CxgcCxcUHhUXFxYXCw=="), 40+(scale<<8)+(1<<16));
+ return this;
+}
+
+//Bluetooth on
+var btOnImg = {
+ width : 15, height : 22, bpp : 3,
+ transparent : 2,
+ buffer : E.toArrayBuffer(atob("/////H//////A/////4A/////4BH////4JI////4JJH///4J5I///4BP5I///BJ/5A//BJPP5A/4JJ5P5H4J/PL/JHBJf/5JI4JJ/7JJIBP75/ZJAJZPJ/JIBJJ5/JJHBJP/JJA4JJ/JJI/4JPJJI//4BJJA////AAH//A=="))
+}
+var btOffImg = {
+ width : 15, height : 22, bpp : 1,
+ buffer : E.toArrayBuffer(atob("AAAAwAcAGgBEAQgEEBgQIDCAIQAiACgAUABgAMABgAKACQARAEGDAPgA"))
+}
+
+//Charging symbol
+var chrgOn = {
+ width : 8, height : 15, bpp : 2,
+ transparent : 0,
+ palette : new Uint16Array([65535,65504,63488,64928]),
+ buffer : E.toArrayBuffer(atob("qterVKlcrVClcLVAlVfVVA1cDVANQA3ADQAPAAwA"))
+}
+
+function draw() {
+ g.clear();
+ drawBackground();
+ drawDate();
+ drawTime();
+ drawBattery();
+ drawBluetooth();
+ drawBatteryStatus();
+}
+function drawDate() {
+ days = ["Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat"]
+ months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+ var d = new Date();
+ var dateString = `${days[d.getDay()]} ${months[d.getMonth()]} ${d.getDate()}`;
+ g.setFontTreasurehuntDOYwE_20(1);
+ var sm = g.stringMetrics(dateString);
+ g.setColor(0,0,0).drawString(dateString, (g.width - sm.width) / 2, g.height - sm.height - 3);
+}
+
+function drawTime(){
+ var top = 100; pad = 6; bh=48; bw=62; linew=3
+ var boxH = {x:pad,y:top,x2:pad+bw,y2:top+bh};
+ var boxM = {x:g.width-pad-bw,y:top,x2:g.width-pad,y2:top+bh};
+ var innerH = {x:boxH.x+linew, y:boxH.y+linew, x2:boxH.x2-linew, y2:boxH.y2-linew}
+ var innerM = {x:boxM.x+linew, y:boxM.y+linew, x2:boxM.x2-linew, y2:boxM.y2-linew}
+ g.setColor(manaColors[bgIndex]).fillRect(boxH).fillRect(boxM).clearRect(innerH).clearRect(innerM);
+
+ //Draw the hour and minute
+ g.setFontTreasurehuntDOYwE_40_N(1);
+ var d = new Date();
+ var h = `00${d.getHours()}`.slice(-2);
+ var m = `00${d.getMinutes()}`.slice(-2);
+
+ var yOffset = 1; xOffset=2;
+ var mH = g.stringMetrics(h);
+ var mM = g.stringMetrics(m);
+ var xH = (bw - mH.width)/2 + boxH.x+xOffset;
+ var yH = (bh - mH.height)/2 +boxH.y+yOffset;
+ console.log `Hours: ${h}, x: ${xH}, y:${yH}`;
+ var xM = (bw - mM.width)/2 + boxM.x+xOffset;
+ var yM = (bh - mM.height)/2 +boxM.y+yOffset;
+ g.setColor(0,0,0).drawString (h, xH, yH).drawString(m, xM, yM);
+}
+
+function drawBattery(){
+ var pad = 6; top=pad; bh=10; bw=40; linew=1;
+ var box = {x:g.width-pad-bw,y:top,x2:g.width-pad,y2:top+bh};
+ var innerB = {x:box.x+linew, y:box.y+linew, x2:box.x2-linew, y2:box.y2-linew}
+ var batteryFill={x:box.x+linew, y:box.y+linew, x2:(box.x-linew)+bw*E.getBattery()/100, y2:box.y2-linew}
+ g.setColor(manaColors[bgIndex]).fillRect(box).clearRect(innerB).setColor(0,1,0).fillRect(batteryFill);
+}
+
+function drawBluetooth(){
+ var img;
+ if (NRF.getSecurityStatus().connected) {
+ img=btOnImg;
+ } else {
+ img=btOffImg;
+ }
+ g.reset().drawImage(img,3,3);
+}
+function drawBatteryStatus(){
+ var left = g.width - (20+6+4);
+ var img;
+ if (Bangle.isCharging()) {
+ console.log("Charger connected");
+ img=chrgOn;
+ g.reset().drawImage(img,left, 4);
+ } else {
+ console.log("Charger not connected");
+ }
+}
+
+function drawBackground() {
+ var metrics = g.imageMetrics(backgrounds[bgIndex]);
+ g.drawImage(backgrounds[bgIndex], (g.width-metrics.width)/2, 5);
+}
+
+function nextBackground() {
+ bgIndex++;
+ if (bgIndex > backgrounds.length - 1) bgIndex = 0;
+ if (bgIndex < 0) bgIndex = backgrounds.length - 1;
+}
+function prevBackground() {
+ bgIndex--;
+ if (bgIndex > backgrounds.length - 1) bgIndex = 0;
+ if (bgIndex < 0) bgIndex = backgrounds.length - 1;
+}
+
+Bangle.on("touch", function (button, xy) {
+ if (button == 1) {
+ prevBackground();
+ } else {
+ nextBackground();
+ }
+ draw();
+});
+
+draw();
+Bangle.setLocked(false);
+Bangle.setLCDPower(true);
+Bangle.setUI("clock");
+
+NRF.on('connect', draw);
+NRF.on('disconnect', draw);
+Bangle.on("charging", draw);
+
+var redrawTimerId = setInterval(function(){
+ draw();
+}, 60000);
diff --git a/apps/mtgwatchface/black.png b/apps/mtgwatchface/black.png
new file mode 100644
index 000000000..a348053a5
Binary files /dev/null and b/apps/mtgwatchface/black.png differ
diff --git a/apps/mtgwatchface/icon.png b/apps/mtgwatchface/icon.png
new file mode 100644
index 000000000..5d9fb2cd3
Binary files /dev/null and b/apps/mtgwatchface/icon.png differ
diff --git a/apps/mtgwatchface/metadata.json b/apps/mtgwatchface/metadata.json
new file mode 100644
index 000000000..fd81ce10f
--- /dev/null
+++ b/apps/mtgwatchface/metadata.json
@@ -0,0 +1,29 @@
+{
+ "id": "mtgwatchface",
+ "name": "MTG Watchface",
+ "shortName": "Magic the Gathering Watch Face",
+ "version": "1v03",
+ "description": "Magic the Gathering themed watch face. Embrace the inner wizzard. Dispay any of the different types of mana on your watch. Which color are you devoted to today? ",
+ "icon": "icon.png",
+ "screenshots": [
+ {"url": "black.png"}
+ ],
+ "tags": "clock",
+ "type": "clock",
+ "supports": [
+ "BANGLEJS2"
+ ],
+ "readme": "README.md",
+ "allow_emulator": true,
+ "storage": [
+ {
+ "name": "mtgwatchface.app.js",
+ "url": "app.js"
+ },
+ {
+ "name": "mtgwatchface.img",
+ "url": "app-icon.js",
+ "evaluate":true
+ }
+ ]
+}
diff --git a/apps/pebble/ChangeLog b/apps/pebble/ChangeLog
index 0cba5a2b2..01f653f48 100644
--- a/apps/pebble/ChangeLog
+++ b/apps/pebble/ChangeLog
@@ -5,3 +5,4 @@
0.05: Fix typo in settings - Purple
0.06: Added dependancy on Pedometer Widget
0.07: Fixed icon and ong file to 48x48
+0.08: Added theme options and optional lock symbol
diff --git a/apps/pebble/README.md b/apps/pebble/README.md
index 4b0233781..953db0ba7 100644
--- a/apps/pebble/README.md
+++ b/apps/pebble/README.md
@@ -4,11 +4,12 @@
* Designed specifically for Bangle 2
* A choice of 6 different background colous through its setting menu. Goto Settings, App/Widget settings, Pebble.
-* Supports the Light and Dark themes
+* Supports the Light and Dark themes (or set theme independently)
* Uses pedometer widget to get latest step count
* Dependant apps are installed when Pebble installs
* Uses the whole screen, widgets are made invisible but still run in the background
* When battery is less than 30% main screen goes Red
+* Optionally show a lock symbol when screen is locked (default off, enable in Settings)


diff --git a/apps/pebble/metadata.json b/apps/pebble/metadata.json
index 4295d7507..eb049e78e 100644
--- a/apps/pebble/metadata.json
+++ b/apps/pebble/metadata.json
@@ -2,7 +2,7 @@
"id": "pebble",
"name": "Pebble Clock",
"shortName": "Pebble",
- "version": "0.07",
+ "version": "0.08",
"description": "A pebble style clock to keep the rebellion going",
"dependencies": {"widpedom":"app"},
"readme": "README.md",
@@ -11,6 +11,7 @@
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS", "BANGLEJS2"],
+ "allow_emulator": true,
"storage": [
{"name":"pebble.app.js","url":"pebble.app.js"},
{"name":"pebble.settings.js","url":"pebble.settings.js"},
diff --git a/apps/pebble/pebble.app.js b/apps/pebble/pebble.app.js
index 106e09b82..062592e47 100644
--- a/apps/pebble/pebble.app.js
+++ b/apps/pebble/pebble.app.js
@@ -10,9 +10,10 @@ Graphics.prototype.setFontLECO1976Regular22 = function(scale) {
const SETTINGS_FILE = "pebble.json";
let settings;
+let theme;
function loadSettings() {
- settings = require("Storage").readJSON(SETTINGS_FILE,1)|| {'bg': '#0f0', 'color': 'Green'};
+ settings = require("Storage").readJSON(SETTINGS_FILE,1)|| {'bg': '#0f0', 'color': 'Green', 'theme':'System', 'showlock':false};
}
var img = require("heatshrink").decompress(atob("oFAwkEogA/AH4A/AH4A/AH4A/AE8AAAoeXoAfeDQUBmcyD7A+Dh///8QD649CiAfaHwUvD4sEHy0DDYIfEICg+Cn4fHICY+DD4nxcgojOHwgfEIAYfRCIQaDD4ZAFD5r7DH4//kAfRCIZ/GAAnwD5p9DX44fTHgYSBf4ofVDAQEBl4fFUAgfOXoQzBgIfFBAIfPP4RAEAoYAB+cRiK/SG4h/WIBAfXIA7CBAAswD55AHn6fUIBMCD65AHl4gCmcziAfQQJqfQQJpiDgk0IDXxQLRAEECaBM+QgRYRYgUIA0CD4ggSQJiDCiAKBICszAAswD55AHABKBVD7BAFABIqBD5pAFABPxD55AOD6BADiIAJQAyxLABwf/gaAPAH4A/AH4ARA=="));
@@ -44,15 +45,15 @@ function draw() {
g.fillRect(0, 0, w, h2 - t);
// contrast bar
- g.setColor(g.theme.fg);
+ g.setColor(theme.fg);
g.fillRect(0, h2 - t, w, h2);
// day and steps
- if (settings.color == 'Blue' || settings.color == 'Red')
- g.setColor('#fff'); // white on blue or red best contrast
- else
- g.setColor('#000'); // otherwise black regardless of theme
-
+ //if (settings.color == 'Blue' || settings.color == 'Red')
+ // g.setColor('#fff'); // white on blue or red best contrast
+ //else
+ // g.setColor('#000'); // otherwise black regardless of theme
+ g.setColor(theme.day);
g.setFontLECO1976Regular22();
g.setFontAlign(0, -1);
g.drawString(da[0].toUpperCase(), w/4, ha); // day of week
@@ -60,16 +61,16 @@ function draw() {
// time
// white on red for battery warning
- g.setColor(!batteryWarning ? g.theme.bg : '#f00');
+ g.setColor(!batteryWarning ? theme.bg : '#f00');
g.fillRect(0, h2, w, h3);
g.setFontLECO1976Regular42();
g.setFontAlign(0, -1);
- g.setColor(!batteryWarning ? g.theme.fg : '#fff');
+ g.setColor(!batteryWarning ? theme.fg : '#fff');
g.drawString(timeStr, w/2, h2 + 8);
// contrast bar
- g.setColor(g.theme.fg);
+ g.setColor(theme.fg);
g.fillRect(0, h3, w, h3 + t);
// the bottom
@@ -79,15 +80,17 @@ function draw() {
g.setColor(settings.bg);
g.drawImage(img, w/2 + ((w/2) - 64)/2, 1, { scale: 1 });
drawCalendar(((w/2) - 42)/2, 14, 42, 4, da[2]);
+
+ drawLock();
}
// at x,y width:wi thicknes:th
function drawCalendar(x,y,wi,th,str) {
- g.setColor(g.theme.fg);
+ g.setColor(theme.fg);
g.fillRect(x, y, x + wi, y + wi);
- g.setColor(g.theme.bg);
+ g.setColor(theme.bg);
g.fillRect(x + th, y + th, x + wi - th, y + wi - th);
- g.setColor(g.theme.fg);
+ g.setColor(theme.fg);
let hook_t = 6;
// first calendar hook, one third in
@@ -107,6 +110,38 @@ function getSteps() {
return '????';
}
+function loadThemeColors() {
+ theme = {fg: g.theme.fg, bg: g.theme.bg, day: g.toColor(0,0,0)};
+ if (settings.theme === "Dark") {
+ theme.fg = g.toColor(1,1,1);
+ theme.bg = g.toColor(0,0,0);
+ }
+ else if (settings.theme === "Light") {
+ theme.fg = g.toColor(0,0,0);
+ theme.bg = g.toColor(1,1,1);
+ }
+ // day and steps
+ if (settings.color == 'Blue' || settings.color == 'Red')
+ theme.day = g.toColor(1,1,1); // white on blue or red best contrast
+}
+
+function drawLock(){
+ if (settings.showlock) {
+ if (Bangle.isLocked()){
+ g.setColor(theme.day);
+ g.setBgColor(settings.bg);
+ g.drawImage(atob("DhABH+D/wwMMDDAwwMf/v//4f+H/h/8//P/z///f/g=="), 1, 4);
+ } else {
+ g.setColor(settings.bg);
+ g.fillRect(0, 0, 20, 20);
+ }
+ }
+}
+
+Bangle.on('lock', function(on) {
+ drawLock();
+});
+
g.clear();
Bangle.loadWidgets();
/*
@@ -116,6 +151,7 @@ Bangle.loadWidgets();
*/
for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
loadSettings();
+loadThemeColors();
setInterval(draw, 15000); // refresh every 15s
draw();
Bangle.setUI("clock");
diff --git a/apps/pebble/pebble.settings.js b/apps/pebble/pebble.settings.js
index ff408907d..8a5fba63b 100644
--- a/apps/pebble/pebble.settings.js
+++ b/apps/pebble/pebble.settings.js
@@ -2,7 +2,7 @@
const SETTINGS_FILE = "pebble.json";
// initialize with default settings...
- let s = {'bg': '#0f0', 'color': 'Green'}
+ let s = {'bg': '#0f0', 'color': 'Green', 'theme':'System', 'showlock':false}
// ...and overwrite them with any saved values
// This way saved values are preserved if a new version adds more settings
@@ -20,6 +20,7 @@
var color_options = ['Green','Orange','Cyan','Purple','Red','Blue'];
var bg_code = ['#0f0','#ff0','#0ff','#f0f','#f00','#00f'];
+ var theme_options = ['System', 'Light', 'Dark'];
E.showMenu({
'': { 'title': 'Pebble Clock' },
@@ -32,7 +33,24 @@
s.color = color_options[v];
s.bg = bg_code[v];
save();
- },
- }
+ }
+ },
+ 'Theme': {
+ value: 0 | theme_options.indexOf(s.theme),
+ min: 0, max: theme_options.length - 1,
+ format: v => theme_options[v],
+ onchange: v => {
+ s.theme = theme_options[v];
+ save();
+ }
+ },
+ 'Show Lock': {
+ value: settings.showlock,
+ format: () => (settings.showlock ? 'Yes' : 'No'),
+ onchange: () => {
+ settings.showlock = !settings.showlock;
+ save();
+ }
+ },
});
})
diff --git a/apps/quicklaunch/ChangeLog b/apps/quicklaunch/ChangeLog
new file mode 100644
index 000000000..ec66c5568
--- /dev/null
+++ b/apps/quicklaunch/ChangeLog
@@ -0,0 +1 @@
+0.01: Initial version
diff --git a/apps/quicklaunch/app-icon.js b/apps/quicklaunch/app-icon.js
new file mode 100644
index 000000000..14ae94823
--- /dev/null
+++ b/apps/quicklaunch/app-icon.js
@@ -0,0 +1 @@
+require("heatshrink").decompress(atob("kMigILIgPAAYMD/ADBwcGhkAwM5wcA/+2//Av/Rn/giFoyFggkUrFggEKlAkCiApCx+AAYNGoADBkU4AYMQj4DBvEICANkAoIPBgE2B4MAiMAH4MAwECAYNALYUgBIISCHYMYAoQWBAIMEgAYBAIMBwEDDQNgDwUf/4eBg4DCAA4"))
diff --git a/apps/quicklaunch/app.js b/apps/quicklaunch/app.js
new file mode 100644
index 000000000..f2b749e3e
--- /dev/null
+++ b/apps/quicklaunch/app.js
@@ -0,0 +1,120 @@
+var settings = Object.assign(require("Storage").readJSON("quicklaunch.json", true) || {});
+
+var apps = require("Storage").list(/\.info$/).map(app=>{var a=require("Storage").readJSON(app,1);return a&&{name:a.name,type:a.type,sortorder:a.sortorder,src:a.src};}).filter(app=>app && (app.type=="app" || app.type=="launch" || app.type=="clock" || !app.type));
+
+apps.sort((a,b)=>{
+ var n=(0|a.sortorder)-(0|b.sortorder);
+ if (n) return n; // do sortorder first
+ if (a.nameb.name) return 1;
+ return 0;
+});
+
+function save(key, value) {
+ settings[key] = value;
+ require("Storage").write("quicklaunch.json",settings);
+}
+
+// Quick Launch menu
+function showMainMenu() {
+ var mainmenu = {
+ "" : { "title" : "Quick Launch" },
+ "< Back" : ()=>{load();}
+ };
+
+ //List all selected apps
+ mainmenu["Left: "+settings.leftapp.name] = function() { E.showMenu(leftmenu); };
+ mainmenu["Right: "+settings.rightapp.name] = function() { E.showMenu(rightmenu); };
+ mainmenu["Up: "+settings.upapp.name] = function() { E.showMenu(upmenu); };
+ mainmenu["Down: "+settings.downapp.name] = function() { E.showMenu(downmenu); };
+ mainmenu["Tap: "+settings.tapapp.name] = function() { E.showMenu(tapmenu); };
+
+ return E.showMenu(mainmenu);
+}
+
+//Left swipe menu
+var leftmenu = {
+ "" : { "title" : "Left Swipe" },
+ "< Back" : showMainMenu
+};
+
+leftmenu["(none)"] = function() {
+ save("leftapp", {"name":"(none)"});
+ showMainMenu();
+};
+apps.forEach((a)=>{
+ leftmenu[a.name] = function() {
+ save("leftapp", a);
+ showMainMenu();
+ };
+});
+
+//Right swipe menu
+var rightmenu = {
+ "" : { "title" : "Right Swipe" },
+ "< Back" : showMainMenu
+};
+
+rightmenu["(none)"] = function() {
+ save("rightapp", {"name":"(none)"});
+ showMainMenu();
+};
+apps.forEach((a)=>{
+ rightmenu[a.name] = function() {
+ save("rightapp", a);
+ showMainMenu();
+ };
+});
+
+//Up swipe menu
+var upmenu = {
+ "" : { "title" : "Up Swipe" },
+ "< Back" : showMainMenu
+};
+
+upmenu["(none)"] = function() {
+ save("upapp", {"name":"(none)"});
+ showMainMenu();
+};
+apps.forEach((a)=>{
+ upmenu[a.name] = function() {
+ save("upapp", a);
+ showMainMenu();
+ };
+});
+
+//Down swipe menu
+var downmenu = {
+ "" : { "title" : "Down Swipe" },
+ "< Back" : showMainMenu
+};
+
+downmenu["(none)"] = function() {
+ save("downapp", {"name":"(none)"});
+ showMainMenu();
+};
+apps.forEach((a)=>{
+ downmenu[a.name] = function() {
+ save("downapp", a);
+ showMainMenu();
+ };
+});
+
+//Tap menu
+var tapmenu = {
+ "" : { "title" : "Tap" },
+ "< Back" : showMainMenu
+};
+
+tapmenu["(none)"] = function() {
+ save("tapapp", {"name":"(none)"});
+ showMainMenu();
+};
+apps.forEach((a)=>{
+ tapmenu[a.name] = function() {
+ save("tapapp", a);
+ showMainMenu();
+ };
+});
+
+showMainMenu();
diff --git a/apps/quicklaunch/app.png b/apps/quicklaunch/app.png
new file mode 100644
index 000000000..3d1d0fdd2
Binary files /dev/null and b/apps/quicklaunch/app.png differ
diff --git a/apps/quicklaunch/boot.js b/apps/quicklaunch/boot.js
new file mode 100644
index 000000000..3670c4776
--- /dev/null
+++ b/apps/quicklaunch/boot.js
@@ -0,0 +1,67 @@
+(function() {
+ var settings = Object.assign(require("Storage").readJSON("quicklaunch.json", true) || {});
+
+ //list all sources
+ var apps = require("Storage").list(/\.info$/).map(app=>{var a=require("Storage").readJSON(app,1);return a&&{src:a.src};});
+
+ //populate empty app list
+
+ if (!settings.leftapp) {
+ settings["leftapp"] = {"name":"(none)"};
+ require("Storage").write("quicklaunch.json",settings);
+ }
+ if (!settings.rightapp) {
+ settings["rightapp"] = {"name":"(none)"};
+ require("Storage").write("quicklaunch.json",settings);
+ }
+ if (!settings.upapp) {
+ settings["upapp"] = {"name":"(none)"};
+ require("Storage").write("quicklaunch.json",settings);
+ }
+ if (!settings.downapp) {
+ settings["downapp"] = {"name":"(none)"};
+ require("Storage").write("quicklaunch.json",settings);
+ }
+ if (!settings.tapapp) {
+ settings["tapapp"] = {"name":"(none)"};
+ require("Storage").write("quicklaunch.json",settings);
+ }
+
+ //activate on clock faces
+ var sui = Bangle.setUI;
+ Bangle.setUI = function(mode, cb) {
+ sui(mode,cb);
+ if(!mode) return;
+ if ("object"==typeof mode) mode = mode.mode;
+ if (!mode.startsWith("clock")) return;
+
+ function tap() {
+ //tap, check if source exists, launch
+ if ((settings.tapapp.src) && apps.some(e => e.src === settings.tapapp.src)) load (settings.tapapp.src);
+ }
+
+ let drag;
+ let e;
+
+ Bangle.on("touch",tap);
+ Bangle.on("drag", e => {
+ if (!drag) { // start dragging
+ drag = {x: e.x, y: e.y};
+ } else if (!e.b) { // released
+ const dx = e.x-drag.x, dy = e.y-drag.y;
+ drag = null;
+ //horizontal swipes, check if source exists, launch
+ if (Math.abs(dx)>Math.abs(dy)+10) {
+ if ((settings.leftapp.src) && apps.some(e => e.src === settings.leftapp.src) && dx<0) load(settings.leftapp.src);
+ if ((settings.rightapp.src) && apps.some(e => e.src === settings.rightapp.src) && dx>0) load(settings.rightapp.src);
+ }
+ //vertical swipes, check if source exists, launch
+ else if (Math.abs(dy)>Math.abs(dx)+10) {
+ if ((settings.upapp.src) && apps.some(e => e.src === settings.upapp.src) && dy<0) load(settings.upapp.src);
+ if ((settings.downapp.src) && apps.some(e => e.src === settings.downapp.src) && dy>0) load(settings.downapp.src);
+ }
+ }
+ });
+
+ };
+})();
diff --git a/apps/quicklaunch/metadata.json b/apps/quicklaunch/metadata.json
new file mode 100644
index 000000000..6411d1a5f
--- /dev/null
+++ b/apps/quicklaunch/metadata.json
@@ -0,0 +1,14 @@
+{ "id": "quicklaunch",
+ "name": "Quick Launch",
+ "icon": "app.png",
+ "version":"0.01",
+ "description": "Tap or swipe left/right/up/down on your clock face to launch up to five apps of your choice.",
+ "tags": "tools, system",
+ "supports": ["BANGLEJS2"],
+ "storage": [
+ {"name":"quicklaunch.app.js","url":"app.js"},
+ {"name":"quicklaunch.boot.js","url":"boot.js"},
+ {"name":"quicklaunch.img","url":"app-icon.js","evaluate":true}
+ ],
+ "data": [{"name":"quicklaunch.json"}]
+}
diff --git a/apps/recorder/ChangeLog b/apps/recorder/ChangeLog
index 90937e160..877b1354a 100644
--- a/apps/recorder/ChangeLog
+++ b/apps/recorder/ChangeLog
@@ -17,3 +17,5 @@
0.11: Fix KML and GPX export when there is no GPS data
0.12: Fix 'Back' label positioning on track/graph display, make translateable
0.13: Fix for when widget is used before app
+0.14: Remove unneeded variable assignment
+0.15: Show distance more accurately in conjunction with new locale app (fix #1523)
diff --git a/apps/recorder/app.js b/apps/recorder/app.js
index 99252e0e2..fb3dfab4f 100644
--- a/apps/recorder/app.js
+++ b/apps/recorder/app.js
@@ -307,7 +307,7 @@ function viewTrack(filename, info) {
g.fillCircle(ox,oy,5);
if (info.qOSTM) g.setColor("#000");
else g.setColor(g.theme.fg);
- g.drawString(require("locale").distance(dist),g.getWidth() / 2, g.getHeight() - 20);
+ g.drawString(require("locale").distance(dist,2),g.getWidth() / 2, g.getHeight() - 20);
g.setFont("6x8",2);
g.setFontAlign(0,0,3);
var isBTN3 = "BTN3" in global;
diff --git a/apps/recorder/metadata.json b/apps/recorder/metadata.json
index e2400603d..4146e92be 100644
--- a/apps/recorder/metadata.json
+++ b/apps/recorder/metadata.json
@@ -2,7 +2,7 @@
"id": "recorder",
"name": "Recorder",
"shortName": "Recorder",
- "version": "0.13",
+ "version": "0.15",
"description": "Record GPS position, heart rate and more in the background, then download to your PC.",
"icon": "app.png",
"tags": "tool,outdoors,gps,widget",
diff --git a/apps/recorder/widget.js b/apps/recorder/widget.js
index 221bc6c1a..4a105754b 100644
--- a/apps/recorder/widget.js
+++ b/apps/recorder/widget.js
@@ -248,7 +248,7 @@
}
var buttons={Yes:"yes",No:"no"};
if (newFileName) buttons["New"] = "new";
- var prompt = E.showPrompt("Overwrite\nLog " + settings.file.match(/\d+/)[0] + "?",{title:"Recorder",buttons:buttons}).then(selection=>{
+ return E.showPrompt("Overwrite\nLog " + settings.file.match(/\d+/)[0] + "?",{title:"Recorder",buttons:buttons}).then(selection=>{
if (selection==="no") return false; // just cancel
if (selection==="yes") {
require("Storage").open(settings.file,"r").erase();
@@ -259,7 +259,6 @@
}
return WIDGETS["recorder"].setRecording(1);
});
- return prompt;
}
settings.recording = isOn;
updateSettings(settings);
diff --git a/apps/run/ChangeLog b/apps/run/ChangeLog
index 401a68de9..de070dbd8 100644
--- a/apps/run/ChangeLog
+++ b/apps/run/ChangeLog
@@ -9,3 +9,6 @@
0.08: Added support for notifications from exstats. Support all stats from exstats
0.09: Fix broken start/stop if recording not enabled (fix #1561)
0.10: Don't allow the same setting to be chosen for 2 boxes (fix #1578)
+0.11: Notifications fixes
+0.12: Fix for recorder not stopping at end of run. Bug introduced in 0.11
+0.13: Revert #1578 (stop duplicate entries) as with 2v12 menus it causes other boxes to be wiped (fix #1643)
diff --git a/apps/run/app.js b/apps/run/app.js
index d066c8b1f..19dcd7e88 100644
--- a/apps/run/app.js
+++ b/apps/run/app.js
@@ -68,7 +68,7 @@ function onStartStop() {
if (!prepPromises.length) // fix for Promise.all bug in 2v12
prepPromises.push(Promise.resolve());
-
+
Promise.all(prepPromises)
.then(() => {
if (running) {
@@ -124,7 +124,7 @@ function configureNotification(stat) {
}
Object.keys(settings.notify).forEach((statType) => {
- if (settings.notify[statType].increment > 0) {
+ if (settings.notify[statType].increment > 0 && exs.stats[statType]) {
configureNotification(exs.stats[statType]);
}
});
diff --git a/apps/run/metadata.json b/apps/run/metadata.json
index 51239d297..afa52b2f7 100644
--- a/apps/run/metadata.json
+++ b/apps/run/metadata.json
@@ -1,6 +1,6 @@
{ "id": "run",
"name": "Run",
- "version":"0.10",
+ "version":"0.13",
"description": "Displays distance, time, steps, cadence, pace and more for runners.",
"icon": "app.png",
"tags": "run,running,fitness,outdoors,gps",
diff --git a/apps/run/settings.js b/apps/run/settings.js
index 949f7a235..c3bb31a0d 100644
--- a/apps/run/settings.js
+++ b/apps/run/settings.js
@@ -42,11 +42,6 @@
value: Math.max(statsIDs.indexOf(settings[boxID]),0),
format: v => statsList[v].name,
onchange: v => {
- for (var i=1;i<=6;i++)
- if (settings["B"+i]==statsIDs[v]) {
- settings["B"+i]="";
- boxMenu["Box "+i].value=0;
- }
settings[boxID] = statsIDs[v];
saveSettings();
},
@@ -90,8 +85,8 @@
[[300, 1],[300, 0],[300, 1],[300, 0],[300, 1]],
];
notificationsMenu[/*LANG*/"Dist Pattern"] = {
- value: Math.max(0,vibPatterns.findIndex((p) => JSON.stringify(p) === JSON.stringify(settings.notify.dist.notifications))),
- min: 0, max: vibPatterns.length,
+ value: Math.max(0,vibTimes.findIndex((p) => JSON.stringify(p) === JSON.stringify(settings.notify.dist.notifications))),
+ min: 0, max: vibTimes.length - 1,
format: v => vibPatterns[v]||/*LANG*/"Off",
onchange: v => {
settings.notify.dist.notifications = vibTimes[v];
@@ -100,8 +95,8 @@
}
}
notificationsMenu[/*LANG*/"Step Pattern"] = {
- value: Math.max(0,vibPatterns.findIndex((p) => JSON.stringify(p) === JSON.stringify(settings.notify.step.notifications))),
- min: 0, max: vibPatterns.length,
+ value: Math.max(0,vibTimes.findIndex((p) => JSON.stringify(p) === JSON.stringify(settings.notify.step.notifications))),
+ min: 0, max: vibTimes.length - 1,
format: v => vibPatterns[v]||/*LANG*/"Off",
onchange: v => {
settings.notify.step.notifications = vibTimes[v];
@@ -110,8 +105,8 @@
}
}
notificationsMenu[/*LANG*/"Time Pattern"] = {
- value: Math.max(0,vibPatterns.findIndex((p) => JSON.stringify(p) === JSON.stringify(settings.notify.time.notifications))),
- min: 0, max: vibPatterns.length,
+ value: Math.max(0,vibTimes.findIndex((p) => JSON.stringify(p) === JSON.stringify(settings.notify.time.notifications))),
+ min: 0, max: vibTimes.length - 1,
format: v => vibPatterns[v]||/*LANG*/"Off",
onchange: v => {
settings.notify.time.notifications = vibTimes[v];
diff --git a/apps/smclock/metadata.json b/apps/smclock/metadata.json
index 55668adcc..ca40193a2 100644
--- a/apps/smclock/metadata.json
+++ b/apps/smclock/metadata.json
@@ -4,7 +4,7 @@
"shortName": "MonoClock",
"icon": "app.png",
"screenshots": [{ "url": "screenshot0.png" }, {"url": "screenshot1.png" }],
- "version": "0.04",
+ "version": "0.06",
"description": "A simple watchface based on my stylised monogram.",
"type": "clock",
"tags": "clock",
diff --git a/apps/timerclk/ChangeLog b/apps/timerclk/ChangeLog
index 5560f00bc..e17baa27c 100644
--- a/apps/timerclk/ChangeLog
+++ b/apps/timerclk/ChangeLog
@@ -1 +1,2 @@
0.01: New App!
+0.02: Add sunrise/sunset. Fix timer bugs.
diff --git a/apps/timerclk/README.md b/apps/timerclk/README.md
index fd6d2b16b..c27a8f6f8 100644
--- a/apps/timerclk/README.md
+++ b/apps/timerclk/README.md
@@ -11,6 +11,7 @@ A clock based on the Anton Clock with stopwatches, timers and alarms based on th
* alarms
* multiple stopwatches, timers and alarms
* stopwatches and timers keep running in the background
+* optional time of sunrise/sunset using the My Location app - hidden by default
## Images
diff --git a/apps/timerclk/alarm.info b/apps/timerclk/alarm.info
deleted file mode 100644
index 1289f8cef..000000000
--- a/apps/timerclk/alarm.info
+++ /dev/null
@@ -1 +0,0 @@
-{"id":"timerclk","name":"tclk Alarm","src":"timerclk.alarm.js","icon":"timerclk.img","version":"0.01","tags":"","files":"","sortorder":10}
diff --git a/apps/timerclk/app.js b/apps/timerclk/app.js
index eeb3ac4cd..c750fcfde 100644
--- a/apps/timerclk/app.js
+++ b/apps/timerclk/app.js
@@ -3,6 +3,23 @@ Graphics.prototype.setFontAnton = function(scale) {
g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAAAAAAAA/gAAAAAAAAAAP/gAAAAAAAAAH//gAAAAAAAAB///gAAAAAAAAf///gAAAAAAAP////gAAAAAAD/////gAAAAAA//////gAAAAAP//////gAAAAH///////gAAAB////////gAAAf////////gAAP/////////gAD//////////AA//////////gAA/////////4AAA////////+AAAA////////gAAAA///////wAAAAA//////8AAAAAA//////AAAAAAA/////gAAAAAAA////4AAAAAAAA///+AAAAAAAAA///gAAAAAAAAA//wAAAAAAAAAA/8AAAAAAAAAAA/AAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////AAAAAB///////8AAAAH////////AAAAf////////wAAA/////////4AAB/////////8AAD/////////+AAH//////////AAP//////////gAP//////////gAP//////////gAf//////////wAf//////////wAf//////////wAf//////////wA//8AAAAAB//4A//wAAAAAAf/4A//gAAAAAAP/4A//gAAAAAAP/4A//gAAAAAAP/4A//wAAAAAAf/4A///////////4Af//////////wAf//////////wAf//////////wAf//////////wAP//////////gAP//////////gAH//////////AAH//////////AAD/////////+AAB/////////8AAA/////////4AAAP////////gAAAD///////+AAAAAf//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/gAAAAAAAAAAP/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/AAAAAAAAAAA//AAAAAAAAAAA/+AAAAAAAAAAB/8AAAAAAAAAAD//////////gAH//////////gAP//////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/4AAAAB/gAAD//4AAAAf/gAAP//4AAAB//gAA///4AAAH//gAB///4AAAf//gAD///4AAA///gAH///4AAD///gAP///4AAH///gAP///4AAP///gAf///4AAf///gAf///4AB////gAf///4AD////gA////4AH////gA////4Af////gA////4A/////gA//wAAB/////gA//gAAH/////gA//gAAP/////gA//gAA///8//gA//gAD///w//gA//wA////g//gA////////A//gA///////8A//gA///////4A//gAf//////wA//gAf//////gA//gAf/////+AA//gAP/////8AA//gAP/////4AA//gAH/////gAA//gAD/////AAA//gAB////8AAA//gAA////wAAA//gAAP///AAAA//gAAD//8AAAA//gAAAP+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/+AAAAAD/wAAB//8AAAAP/wAAB///AAAA//wAAB///wAAB//wAAB///4AAD//wAAB///8AAH//wAAB///+AAP//wAAB///+AAP//wAAB////AAf//wAAB////AAf//wAAB////gAf//wAAB////gA///wAAB////gA///wAAB////gA///w//AAf//wA//4A//AAA//wA//gA//AAAf/wA//gB//gAAf/wA//gB//gAAf/wA//gD//wAA//wA//wH//8AB//wA///////////gA///////////gA///////////gA///////////gAf//////////AAf//////////AAP//////////AAP/////////+AAH/////////8AAH///+/////4AAD///+f////wAAA///8P////gAAAf//4H///+AAAAH//gB///wAAAAAP4AAH/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wAAAAAAAAAA//wAAAAAAAAAP//wAAAAAAAAB///wAAAAAAAAf///wAAAAAAAH////wAAAAAAA/////wAAAAAAP/////wAAAAAB//////wAAAAAf//////wAAAAH///////wAAAA////////wAAAP////////wAAA///////H/wAAA//////wH/wAAA/////8AH/wAAA/////AAH/wAAA////gAAH/wAAA///4AAAH/wAAA//+AAAAH/wAAA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gAAAAAAAAH/4AAAAAAAAAAH/wAAAAAAAAAAH/wAAAAAAAAAAH/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//8AAA/////+B///AAA/////+B///wAA/////+B///4AA/////+B///8AA/////+B///8AA/////+B///+AA/////+B////AA/////+B////AA/////+B////AA/////+B////gA/////+B////gA/////+B////gA/////+A////gA//gP/gAAB//wA//gf/AAAA//wA//gf/AAAAf/wA//g//AAAAf/wA//g//AAAA//wA//g//gAAA//wA//g//+AAP//wA//g////////gA//g////////gA//g////////gA//g////////gA//g////////AA//gf///////AA//gf//////+AA//gP//////+AA//gH//////8AA//gD//////4AA//gB//////wAA//gA//////AAAAAAAH////8AAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////gAAAAB///////+AAAAH////////gAAAf////////4AAB/////////8AAD/////////+AAH//////////AAH//////////gAP//////////gAP//////////gAf//////////wAf//////////wAf//////////wAf//////////wAf//////////4A//wAD/4AAf/4A//gAH/wAAP/4A//gAH/wAAP/4A//gAP/wAAP/4A//gAP/4AAf/4A//wAP/+AD//4A///wP//////4Af//4P//////wAf//4P//////wAf//4P//////wAf//4P//////wAP//4P//////gAP//4H//////gAH//4H//////AAH//4D/////+AAD//4D/////8AAB//4B/////4AAA//4A/////wAAAP/4AP////AAAAB/4AD///4AAAAAAAAAH/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//AAAAAAAAAAA//gAAAAAAAAAA//gAAAAAAAAAA//gAAAAAAADgA//gAAAAAAP/gA//gAAAAAH//gA//gAAAAB///gA//gAAAAP///gA//gAAAD////gA//gAAAf////gA//gAAB/////gA//gAAP/////gA//gAB//////gA//gAH//////gA//gA///////gA//gD///////gA//gf///////gA//h////////gA//n////////gA//////////gAA/////////AAAA////////wAAAA///////4AAAAA///////AAAAAA//////4AAAAAA//////AAAAAAA/////4AAAAAAA/////AAAAAAAA////8AAAAAAAA////gAAAAAAAA///+AAAAAAAAA///4AAAAAAAAA///AAAAAAAAAA//4AAAAAAAAAA/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//gB///wAAAAP//4H///+AAAA///8P////gAAB///+f////4AAD///+/////8AAH/////////+AAH//////////AAP//////////gAP//////////gAf//////////gAf//////////wAf//////////wAf//////////wA///////////wA//4D//wAB//4A//wB//gAA//4A//gA//gAAf/4A//gA//AAAf/4A//gA//gAAf/4A//wB//gAA//4A///P//8AH//4Af//////////wAf//////////wAf//////////wAf//////////wAf//////////gAP//////////gAP//////////AAH//////////AAD/////////+AAD///+/////8AAB///8f////wAAAf//4P////AAAAH//wD///8AAAAA/+AAf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//gAAAAAAAAB///+AA/+AAAAP////gA//wAAAf////wA//4AAB/////4A//8AAD/////8A//+AAD/////+A///AAH/////+A///AAP//////A///gAP//////A///gAf//////A///wAf//////A///wAf//////A///wAf//////A///wA///////AB//4A//4AD//AAP/4A//gAB//AAP/4A//gAA//AAP/4A//gAA/+AAP/4A//gAB/8AAP/4A//wAB/8AAf/4Af//////////wAf//////////wAf//////////wAf//////////wAf//////////wAP//////////gAP//////////gAH//////////AAH/////////+AAD/////////8AAB/////////4AAAf////////wAAAP////////AAAAB///////4AAAAAD/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/AAB/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="), 46, atob("EiAnGicnJycnJycnEw=="), 78+(scale<<8)+(1<<16));
};
+var SunCalc = require("https://raw.githubusercontent.com/mourner/suncalc/master/suncalc.js");
+const LOCATION_FILE = "mylocation.json";
+let location;
+var sunRise = "--:--";
+var sunSet = "--:--";
+var sunIcons = "\0" + atob("DwyBAAAAAAAAAAgAOAD4A/gP+D/4//gAAAAA") + "\0" + atob("FQyDAAAAAAAAAAAAAAAABAAAAAAABAAIABAAAAABAAABAAAAAAABJAAAAABAAJJJIABAABAJJJJIBAAAAJJJJJIAAAAJJJJJJIAAJBJJJJJJBIAAJJJJJJIAAAAAAAAAAAAA") + "\0" + atob("DwyBAAAAAAAAA//j/4P+A/gD4AOAAgAAAAAA");
+
+function loadLocation() {
+ location = require('Storage').readJSON(LOCATION_FILE, true) || {lat:51.5072,lon:0.1276,location:"London"};
+}
+
+function updateSunRiseSunSet(location) {
+ var times = SunCalc.getTimes(new Date(), location.lat, location.lon);
+ sunRise = require("locale").time(times.sunrise, 1);
+ sunSet = require("locale").time(times.sunset, 1);
+}
+
var timerclk = require("timerclk.lib.js");
var settings = require('Storage').readJSON("timerclk.json", true) || {};
settings = Object.assign({
@@ -12,11 +29,14 @@ settings = Object.assign({
"dateFontSize":2,
"dowFont":"6x8",
"dowFontSize":2,
+ "srssFont":"6x8",
+ "srssFontSize":2,
"specialFont":"6x8",
"specialFontSize":2,
"shortDate":true,
"showStopwatches":true,
"showTimers":true,
+ "showSrss":false,
}, settings.clock||{});
var stopwatches = [], timers = [];
@@ -77,7 +97,12 @@ function drawSpecial() {
queueDraw(drawSpecialTimeout, interval, drawSpecial);
}
+var drawCount=0;
+
function draw() {
+ if (drawCount++ % 60 == 0) {
+ updateSunRiseSunSet(location);
+ }
var x = g.getWidth()/2;
var y = g.getHeight()/2;
g.reset();
@@ -85,6 +110,7 @@ function draw() {
var timeStr = require("locale").time(date,1);
var dateStr = require("locale").date(date,settings.shortDate).toUpperCase();
var dowStr = require("locale").dow(date).toUpperCase();
+ var srssStr = sunRise + sunIcons + sunSet;
// draw time
if (settings.timeFont == "Anton") {
@@ -105,6 +131,13 @@ function draw() {
g.setFontAlign(0,0).setFont(settings.dowFont, settings.dowFontSize);
y += g.stringMetrics(dowStr).height/2;
g.drawString(dowStr,x,y);
+ if (settings.showSrss) {
+ // draw sun rise sun set
+ y += g.stringMetrics(dowStr).height/2;
+ g.setFontAlign(0,0).setFont(settings.srssFont, settings.srssFontSize);
+ y += g.stringMetrics(srssStr).height/2;
+ g.drawString(srssStr,x,y);
+ }
// queue draw in one minute
queueDraw(drawTimeout, 60000, draw);
}
@@ -147,5 +180,6 @@ Bangle.setUI("clock"); // Show launcher when middle button pressed
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
+loadLocation();
draw();
if (stopwatches || timers) drawSpecial();
diff --git a/apps/timerclk/boot.js b/apps/timerclk/boot.js
index 9a09f68f3..b6eb05c14 100644
--- a/apps/timerclk/boot.js
+++ b/apps/timerclk/boot.js
@@ -1,26 +1,17 @@
var timerclkTimerTimeout;
var timerclkAlarmTimeout;
function timerclkCheckTimers() {
+ var expiresIn=require("timerclk.lib.js").timerExpiresIn;
if (timerclkTimerTimeout) clearTimeout(timerclkTimerTimeout);
var timers = require('Storage').readJSON('timerclk.timer.json',1)||[];
timers = timers.filter(e=>e.start);
if (timers.length) {
- timers = timers.sort((a,b)=>{
- var at = a.timeAdd;
- if (a.start) at += Date.now()-a.start;
- at = a.period-at;
- var bt = b.timeAdd;
- if (b.start) bt += Date.now()-b.start;
- bt = b.period-bt;
- return at-bt;
- });
+ timers = timers.sort((a,b)=>expiresIn(a)-expiresIn(b));
if (!require('Storage').read("timerclk.timer.alert.js")) {
console.log("No timer app!");
} else {
- var time = timers[0].timeAdd;
- if (timers[0].start) time += Date.now()-timers[0].start;
- time = timers[0].time - time;
- if (time<1000) t=1000;
+ var time = expiresIn(timers[0]);
+ if (time<1000) time=1000;
if (timerclkTimerTimeout) clearTimeout(timerclkTimerTimeout);
timerclkTimerTimeout = setTimeout(() => load("timerclk.timer.alert.js"),time);
}
@@ -38,7 +29,7 @@ function timerclkCheckAlarms() {
} else {
var time = alarms[0].time-currentTime;
if (alarms[0].last == new Date().getDate() || time < 0) time += 86400000;
- if (time<1000) t=1000;
+ if (time<1000) time=1000;
if (timerclkAlarmTimeout) clearTimeout(timerclkAlarmTimeout);
timerclkAlarmTimeout = setTimeout(() => load("timerclk.alarm.alert.js"),time);
}
diff --git a/apps/timerclk/lib.js b/apps/timerclk/lib.js
index 718962fe0..dd3893fa1 100644
--- a/apps/timerclk/lib.js
+++ b/apps/timerclk/lib.js
@@ -125,3 +125,5 @@ exports.registerControls = function(o) {
});
}
};
+
+exports.timerExpiresIn=t=>t.time-(Date.now()-t.start);
diff --git a/apps/timerclk/metadata.json b/apps/timerclk/metadata.json
index 6b415c0fc..7c6c7c9b3 100644
--- a/apps/timerclk/metadata.json
+++ b/apps/timerclk/metadata.json
@@ -2,7 +2,7 @@
"id": "timerclk",
"name": "Timer Clock",
"shortName":"Timer Clock",
- "version":"0.01",
+ "version":"0.02",
"description": "A clock with stopwatches, timers and alarms build in.",
"icon": "app-icon.png",
"type": "clock",
@@ -28,10 +28,7 @@
{"name":"timerclk.timer.js","url":"timer.js"},
{"name":"timerclk.timer.alert.js","url":"timer.alert.js"},
{"name":"timerclk.alarm.js","url":"alarm.js"},
- {"name":"timerclk.alarm.alert.js","url":"alarm.alert.js"},
- {"name":"timerclk.stopwatch.info","url":"stopwatch.info"},
- {"name":"timerclk.timer.info","url":"timer.info"},
- {"name":"timerclk.alarm.info","url":"alarm.info"}
+ {"name":"timerclk.alarm.alert.js","url":"alarm.alert.js"}
],
"data": [{"name":"timerclk.json"},{"name":"timerclk.stopwatch.json"},{"name":"timerclk.timer.json"},{"name":"timerclk.alarm.json"}],
"sortorder": 0
diff --git a/apps/timerclk/settings.js b/apps/timerclk/settings.js
index 556dded98..992985f52 100644
--- a/apps/timerclk/settings.js
+++ b/apps/timerclk/settings.js
@@ -12,9 +12,12 @@
"dowFontSize":2,
"specialFont":"6x8",
"specialFontSize":2,
+ "srssFont":"6x8",
+ "srssFontSize":2,
"shortDate":true,
"showStopwatches":true,
"showTimers":true,
+ "showSrss":false,
}, settings.clock||{});
settings.stopwatch = Object.assign({
"font":"Vector",
@@ -108,6 +111,23 @@
writeSettings();
}
},
+ "sun font":{
+ value: 0|g.getFonts().indexOf(settings.clock.srssFont),
+ format: v => g.getFonts()[v],
+ min: 0, max: g.getFonts().length-1,
+ onchange: v => {
+ settings.clock.srssFont = g.getFonts()[v];
+ writeSettings();
+ }
+ },
+ "sun size":{
+ value: 0|settings.clock.srssFontSize,
+ min: 0,
+ onchange: v => {
+ settings.clock.srssFontSize = v;
+ writeSettings();
+ }
+ },
"short date": {
value: !!settings.clock.shortDate,
format: BOOL_FORMAT,
@@ -132,6 +152,14 @@
writeSettings();
}
},
+ "sun times": {
+ value: !!settings.clock.showSrss,
+ format: v=>v?/*LANG*/"Show":/*LANG*/"Hide",
+ onchange: v => {
+ settings.clock.showSrss = v;
+ writeSettings();
+ }
+ },
};
var stopwatchMenu = {
diff --git a/apps/timerclk/stopwatch.info b/apps/timerclk/stopwatch.info
deleted file mode 100644
index 72ad418b1..000000000
--- a/apps/timerclk/stopwatch.info
+++ /dev/null
@@ -1 +0,0 @@
-{"id":"timerclk","name":"tclk Stopwatch","src":"timerclk.stopwatch.js","icon":"timerclk.img","version":"0.01","tags":"","files":"","sortorder":10}
diff --git a/apps/timerclk/timer.alert.js b/apps/timerclk/timer.alert.js
index f51ea6767..96352097d 100644
--- a/apps/timerclk/timer.alert.js
+++ b/apps/timerclk/timer.alert.js
@@ -14,10 +14,7 @@ function showTimer(timer) {
buttons : {/*LANG*/"Ok":true}
}).then(function(ok) {
buzzCount = 0;
- if (ok) {
- timer.time += Date.now() - timer.start;
- timer.start = null;
- }
+ timer.start = null;
require("Storage").write("timerclk.timer.json",JSON.stringify(timers));
load();
});
@@ -45,16 +42,8 @@ console.log("checking for timers...");
var timers = require("Storage").readJSON("timerclk.timer.json",1)||[];
var active = timers.filter(e=>e.start);
if (active.length) {
- // if there's an timer, show it
- active = active.sort((a,b)=>{
- var at = a.time;
- if (a.start) at += Date.now()-a.start;
- at = a.period-at;
- var bt = b.time;
- if (b.start) bt += Date.now()-b.start;
- bt = b.period-bt;
- return at-bt;
- });
+ // if there's an active timer, show it
+ active = active.sort((a,b)=>timerclk.timerExpiresIn(a)-timerclk.timerExpiresIn(b));
showTimer(active[0]);
} else {
// otherwise just go back to default app
diff --git a/apps/timerclk/timer.info b/apps/timerclk/timer.info
deleted file mode 100644
index 39a338693..000000000
--- a/apps/timerclk/timer.info
+++ /dev/null
@@ -1 +0,0 @@
-{"id":"timerclk","name":"tclk Timer","src":"timerclk.timer.js","icon":"timerclk.img","version":"0.01","tags":"","files":"","sortorder":10}
diff --git a/apps/timerclk/timer.js b/apps/timerclk/timer.js
index 060c07813..25052e6ae 100644
--- a/apps/timerclk/timer.js
+++ b/apps/timerclk/timer.js
@@ -34,18 +34,20 @@ function update() {
}
function play() {
if (all[current].start) { // running
- all[current].timeAdd += Date.now() - all[current].start;
+ all[current].timeAdd = Date.now() - all[current].start;
all[current].start = null;
update();
} else { // paused
- all[current].start = Date.now();
+ all[current].start = Date.now() - all[current].timeAdd;
+ all[current].timeAdd = 0;
update();
}
require("Storage").write("timerclk.timer.json",JSON.stringify(all));
timerclkCheckTimers();
}
function reset() {
- all[current] = defaultElement.clone();
+ all[current].start = null;
+ all[current].timeAdd = 0;
update();
require("Storage").write("timerclk.timer.json",JSON.stringify(all));
timerclkCheckTimers();
diff --git a/apps/todolist/README.md b/apps/todolist/README.md
index 27c7cfb63..0e1beb74a 100644
--- a/apps/todolist/README.md
+++ b/apps/todolist/README.md
@@ -2,39 +2,63 @@ Todo List
========
This is a simple Todo List application.
+The content is loaded from a JSON file.
+A task can be marked as completed or uncompleted.

-The content is loaded from a JSON file.
-You can mark a task as completed.
+Once installed, the list can be modified via the `Download data from app` icon in the [Bangle.js App Store](https://banglejs.com/apps/) (TodoList app).
+
+
+
JSON file content example:
```javascript
[
{
- name: "Pro",
- children: [
+ "name": "Pro",
+ "children": [
{
- name: "Read doc",
- done: true,
- children: [],
+ "name": "Read doc",
+ "done": true,
+ "children": []
}
- ],
+ ]
},
{
- name: "Pers",
- children: [
+ "name": "Pers",
+ "children": [
{
- name: "Grocery",
- children: [
- { name: "Milk", done: false, children: [] },
- { name: "Eggs", done: false, children: [] },
- { name: "Cheese", done: false, children: [] },
- ],
+ "name": "Grocery",
+ "children": [
+ {
+ "name": "Milk",
+ "done": false,
+ "children": []
+ },
+ {
+ "name": "Eggs",
+ "done": false,
+ "children": []
+ },
+ {
+ "name": "Cheese",
+ "done": false,
+ "children": []
+ }
+ ]
},
- { name: "Workout", done: false, children: [] },
- { name: "Learn Rust", done: false, children: [] },
- ],
- },
+ {
+ "name": "Workout",
+ "done": false,
+ "children": []
+ },
+ {
+ "name": "Learn Rust",
+ "done": false,
+ "children": []
+ }
+ ]
+ }
]
```
\ No newline at end of file
diff --git a/apps/todolist/interface.html b/apps/todolist/interface.html
new file mode 100644
index 000000000..5b9cb038e
--- /dev/null
+++ b/apps/todolist/interface.html
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+ Reload from watch
+ Upload to watch
+ Download
+
+
+
+
+
+
+
diff --git a/apps/todolist/metadata.json b/apps/todolist/metadata.json
index 0833a86bd..a8eb6118b 100644
--- a/apps/todolist/metadata.json
+++ b/apps/todolist/metadata.json
@@ -10,6 +10,7 @@
"tags": "tool,todo",
"supports": ["BANGLEJS", "BANGLEJS2"],
"readme": "README.md",
+ "interface": "interface.html",
"storage": [
{ "name": "todolist.app.js", "url": "app.js" },
{ "name": "todolist.img", "url": "app-icon.js", "evaluate": true }
diff --git a/apps/todolist/screenshot4.png b/apps/todolist/screenshot4.png
new file mode 100644
index 000000000..43db1b0e6
Binary files /dev/null and b/apps/todolist/screenshot4.png differ
diff --git a/apps/touchtimer/ChangeLog b/apps/touchtimer/ChangeLog
index 0969a3da4..f81907152 100644
--- a/apps/touchtimer/ChangeLog
+++ b/apps/touchtimer/ChangeLog
@@ -2,3 +2,4 @@
0.02: Add settings menu
0.03: Add ability to repeat last timer
0.04: Add 5 second count down buzzer
+0.05: Fix 5 second count down buzzer to be only in the final 5 seconds
diff --git a/apps/touchtimer/app.js b/apps/touchtimer/app.js
index c2f2fb5e9..18c07feef 100644
--- a/apps/touchtimer/app.js
+++ b/apps/touchtimer/app.js
@@ -129,7 +129,12 @@ var main = () => {
// Buzz lightly when there are less then 5 seconds left
if (settings.countDownBuzz) {
var remainingSeconds = timerCountDown.getAdjustedTime().seconds;
- if (remainingSeconds <= 5 && remainingSeconds > 0) {
+ var remainingMinutes = timerCountDown.getAdjustedTime().minutes;
+ var remainingHours = timerCountDown.getAdjustedTime().hours;
+ if ( remainingSeconds <= 5
+ && remainingSeconds > 0
+ && remainingMinutes <= 0
+ && remainingHours <= 0) {
Bangle.buzz();
}
}
diff --git a/apps/touchtimer/metadata.json b/apps/touchtimer/metadata.json
index 0f2b9f491..9261f3619 100644
--- a/apps/touchtimer/metadata.json
+++ b/apps/touchtimer/metadata.json
@@ -2,7 +2,7 @@
"id": "touchtimer",
"name": "Touch Timer",
"shortName": "Touch Timer",
- "version": "0.04",
+ "version": "0.05",
"description": "Quickly and easily create a timer with touch-only input. The time can be easily set with a number pad.",
"icon": "app.png",
"tags": "tools",
diff --git a/apps/waypointer/ChangeLog b/apps/waypointer/ChangeLog
new file mode 100644
index 000000000..1b584f7dd
--- /dev/null
+++ b/apps/waypointer/ChangeLog
@@ -0,0 +1,2 @@
+0.01: New app!
+0.02: Make Bangle.js 2 compatible
diff --git a/apps/waypointer/README.md b/apps/waypointer/README.md
index e98fdbb7e..c0b4c5125 100644
--- a/apps/waypointer/README.md
+++ b/apps/waypointer/README.md
@@ -24,7 +24,7 @@ need to travel in to reach the selected waypoint. The blue text is
the name of the current waypoint. NONE means that there is no
waypoint set and so bearing and distance will remain at 0. To select
a waypoint, press BTN2 (middle) and wait for the blue text to turn
-white. Then use BTN1 and BTN3 to select a waypoint. The waypoint
+white. Then use BTN1 and BTN3 (swipe up/down on Bangle.js 2) to select a waypoint. The waypoint
choice is fixed by pressing BTN2 again. In the screen shot below a
waypoint giving the location of Stone Henge has been selected.
diff --git a/apps/waypointer/app.js b/apps/waypointer/app.js
index d3aab7c50..615fbbc36 100644
--- a/apps/waypointer/app.js
+++ b/apps/waypointer/app.js
@@ -1,24 +1,25 @@
-var pal_by = new Uint16Array([0x0000,0xFFC0],0,1); // black, yellow
-var pal_bw = new Uint16Array([0x0000,0xffff],0,1); // black, white
-var pal_bb = new Uint16Array([0x0000,0x07ff],0,1); // black, blue
+const scale = g.getWidth()/240;
+var pal_by = new Uint16Array([g.getBgColor(),0xFFC0],0,1); // black, yellow
+var pal_bw = new Uint16Array([g.getBgColor(),g.getColor()],0,1); // black, white
+var pal_bb = new Uint16Array([g.getBgColor(),0x07ff],0,1); // black, blue
// having 3 2 color pallette keeps the memory requirement lower
-var buf1 = Graphics.createArrayBuffer(160,160,1, {msb:true});
-var buf2 = Graphics.createArrayBuffer(80,40,1, {msb:true});
+var buf1 = Graphics.createArrayBuffer(160*scale,160*scale,1, {msb:true});
+var buf2 = Graphics.createArrayBuffer(g.getWidth()/3,40*scale,1, {msb:true});
var arrow_img = require("heatshrink").decompress(atob("lEowIPMjAEDngEDvwED/4DCgP/wAEBgf/4AEBg//8AEBh//+AEBj///AEBn///gEBv///wmCAAImCAAIoBFggE/AkaaEABo="));
function flip1(x,y) {
- g.drawImage({width:160,height:160,bpp:1,buffer:buf1.buffer, palette:pal_by},x,y);
+ g.drawImage({width:160*scale,height:160*scale,bpp:1,buffer:buf1.buffer, palette:pal_by},x,y);
buf1.clear();
}
function flip2_bw(x,y) {
- g.drawImage({width:80,height:40,bpp:1,buffer:buf2.buffer, palette:pal_bw},x,y);
+ g.drawImage({width:g.getWidth()/3,height:40*scale,bpp:1,buffer:buf2.buffer, palette:pal_bw},x,y);
buf2.clear();
}
function flip2_bb(x,y) {
- g.drawImage({width:80,height:40,bpp:1,buffer:buf2.buffer, palette:pal_bb},x,y);
+ g.drawImage({width:g.getWidth()/3,height:40*scale,bpp:1,buffer:buf2.buffer, palette:pal_bb},x,y);
buf2.clear();
}
@@ -51,12 +52,12 @@ function drawCompass(course) {
previous.course = course;
buf1.setColor(1);
- buf1.fillCircle(80,80,79,79);
+ buf1.fillCircle(buf1.getWidth()/2,buf1.getHeight()/2,79*scale);
buf1.setColor(0);
- buf1.fillCircle(80,80,69,69);
+ buf1.fillCircle(buf1.getWidth()/2,buf1.getHeight()/2,69*scale);
buf1.setColor(1);
- buf1.drawImage(arrow_img, 80, 80, {scale:3, rotate:radians(course)} );
- flip1(40, 30);
+ buf1.drawImage(arrow_img, buf1.getWidth()/2, buf1.getHeight()/2, {scale:3*scale, rotate:radians(course)} );
+ flip1(40*scale, Bangle.appRect.y+6*scale);
}
/***** COMPASS CODE ***********/
@@ -138,7 +139,7 @@ function distance(a,b){
function drawN(){
- buf2.setFont("Vector",24);
+ buf2.setFont("Vector",24*scale);
var bs = wp_bearing.toString();
bs = wp_bearing<10?"00"+bs : wp_bearing<100 ?"0"+bs : bs;
var dst = loc.distance(dist);
@@ -147,12 +148,12 @@ function drawN(){
// show distance on the left
if (previous.dst !== dst) {
- previous.dst = dst
+ previous.dst = dst;
buf2.setColor(1);
buf2.setFontAlign(-1,-1);
- buf2.setFont("Vector", 20);
+ buf2.setFont("Vector", 20*scale);
buf2.drawString(dst,0,0);
- flip2_bw(0, 200);
+ flip2_bw(0, g.getHeight()-40*scale);
}
// bearing, place in middle at bottom of compass
@@ -160,9 +161,9 @@ function drawN(){
previous.bs = bs;
buf2.setColor(1);
buf2.setFontAlign(0, -1);
- buf2.setFont("Vector",38);
- buf2.drawString(bs,40,0);
- flip2_bw(80, 200);
+ buf2.setFont("Vector",38*scale);
+ buf2.drawString(bs,40*scale,0);
+ flip2_bw(g.getWidth()/3, g.getHeight()-40*scale);
}
// waypoint name on right
@@ -170,13 +171,13 @@ function drawN(){
previous.selected = selected;
buf2.setColor(1);
buf2.setFontAlign(1,-1); // right, bottom
- buf2.setFont("Vector", 20);
- buf2.drawString(wp.name, 80, 0);
+ buf2.setFont("Vector", 20*scale);
+ buf2.drawString(wp.name, 80*scale, 0);
if (selected)
- flip2_bw(160, 200);
+ flip2_bw(g.getWidth()/3*2, g.getHeight()-40*scale);
else
- flip2_bb(160, 200);
+ flip2_bb(g.getWidth()/3*2, g.getHeight()-40*scale);
}
}
@@ -229,9 +230,11 @@ function startdraw(){
}
function setButtons(){
- setWatch(nextwp.bind(null,-1), BTN1, {repeat:true,edge:"falling"});
- setWatch(doselect, BTN2, {repeat:true,edge:"falling"});
- setWatch(nextwp.bind(null,1), BTN3, {repeat:true,edge:"falling"});
+ Bangle.setUI("updown", d=>{
+ if (d<0) { nextwp(-1); }
+ else if (d>0) { nextwp(1); }
+ else { doselect(); }
+ });
}
Bangle.on('lcdPower',function(on) {
diff --git a/apps/waypointer/metadata.json b/apps/waypointer/metadata.json
index cb477107b..111259bbc 100644
--- a/apps/waypointer/metadata.json
+++ b/apps/waypointer/metadata.json
@@ -1,11 +1,11 @@
{
"id": "waypointer",
"name": "Way Pointer",
- "version": "0.01",
+ "version": "0.02",
"description": "Navigate to a waypoint using the GPS for bearing and compass to point way, uses the same waypoint interface as GPS Navigation",
"icon": "waypointer.png",
"tags": "tool,outdoors,gps",
- "supports": ["BANGLEJS"],
+ "supports": ["BANGLEJS", "BANGLEJS2"],
"readme": "README.md",
"interface": "waypoints.html",
"storage": [
diff --git a/apps/widbt_notify/ChangeLog b/apps/widbt_notify/ChangeLog
new file mode 100644
index 000000000..b5a50210e
--- /dev/null
+++ b/apps/widbt_notify/ChangeLog
@@ -0,0 +1,10 @@
+0.02: Tweaks for variable size widget system
+0.03: Ensure redrawing works with variable size widget system
+0.04: Fix automatic update of Bluetooth connection status
+0.05: Make Bluetooth widget thinner, and when on a bright theme use light grey for disabled color
+0.06: Tweaking colors for dark/light themes and low bpp screens
+0.07: Memory usage improvements
+0.08: Disable LCD on, on bluetooth status change
+0.09: Vibrate on connection loss
+0.10: Bug fix
+0.11: Avoid too many notifications. Change disconnected colour to red.
diff --git a/apps/widbt_notify/metadata.json b/apps/widbt_notify/metadata.json
new file mode 100644
index 000000000..0b795c2c8
--- /dev/null
+++ b/apps/widbt_notify/metadata.json
@@ -0,0 +1,13 @@
+{
+ "id": "widbt_notify",
+ "name": "Bluetooth Widget with Notification",
+ "version": "0.11",
+ "description": "Show the current Bluetooth connection status in the top right of the clock and vibrate when disconnected.",
+ "icon": "widget.png",
+ "type": "widget",
+ "tags": "widget,bluetooth",
+ "supports": ["BANGLEJS","BANGLEJS2"],
+ "storage": [
+ {"name":"widbt_notify.wid.js","url":"widget.js"}
+ ]
+}
diff --git a/apps/widbt_notify/widget.js b/apps/widbt_notify/widget.js
new file mode 100644
index 000000000..47765f3d0
--- /dev/null
+++ b/apps/widbt_notify/widget.js
@@ -0,0 +1,46 @@
+WIDGETS.bluetooth_notify = {
+ area: "tr",
+ width: 15,
+ warningEnabled: 1,
+ draw: function() {
+ g.reset();
+ if (NRF.getSecurityStatus().connected) {
+ g.setColor((g.getBPP() > 8) ? "#07f" : (g.theme.dark ? "#0ff" : "#00f"));
+ } else {
+ // 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'){
+ draw();
+ }else{
+ load(); // fallback. This might reset some variables
+ }
+ },
+
+ connect: function() {
+ WIDGETS.bluetooth_notify.draw();
+ },
+
+ disconnect: function() {
+ if(WIDGETS.bluetooth_notify.warningEnabled == 1){
+ E.showMessage(/*LANG*/'Connection\nlost.', 'Bluetooth');
+ setInterval(()=>{WIDGETS.bluetooth_notify.redrawCurrentApp();}, 3000); // clear message - this will reload the widget, resetting 'warningEnabled'.
+
+ WIDGETS.bluetooth_notify.warningEnabled = 0;
+ setTimeout('WIDGETS.bluetooth_notify.warningEnabled = 1;', 30000); // don't buzz for the next 30 seconds.
+
+ var quiet = (require('Storage').readJSON('setting.json',1)||{}).quiet;
+ if(!quiet){
+ Bangle.buzz(700, 1); // buzz on connection loss
+ }
+ }
+ WIDGETS.bluetooth_notify.draw();
+ }
+};
+
+NRF.on('connect', WIDGETS.bluetooth_notify.connect);
+NRF.on('disconnect', WIDGETS.bluetooth_notify.disconnect);
diff --git a/apps/widbt_notify/widget.png b/apps/widbt_notify/widget.png
new file mode 100644
index 000000000..1a884a62c
Binary files /dev/null and b/apps/widbt_notify/widget.png differ
diff --git a/backup.js b/backup.js
index 75e236049..8a894666e 100644
--- a/backup.js
+++ b/backup.js
@@ -79,7 +79,9 @@ function bangleUpload() {
.then(() => file.async("string"))
.then(data => {
console.log("decoded", path);
- if (path.startsWith(BACKUP_STORAGEFILE_DIR)) {
+ if (data.length==0) { // https://github.com/espruino/BangleApps/issues/1593
+ console.log("Can't restore files of length 0, ignoring "+path);
+ } else if (path.startsWith(BACKUP_STORAGEFILE_DIR)) {
path = path.substr(BACKUP_STORAGEFILE_DIR.length+1);
cmds += AppInfo.getStorageFileUploadCommands(path, data)+"\n";
} else if (!path.includes("/")) {
diff --git a/core b/core
index 27c7db603..e9097fa68 160000
--- a/core
+++ b/core
@@ -1 +1 @@
-Subproject commit 27c7db6035832837ca3909ea52939f60803df72f
+Subproject commit e9097fa680182069a5814c3e566a0bcbcb5e72a1
diff --git a/index.html b/index.html
index bd8ddea5a..7a94f684a 100644
--- a/index.html
+++ b/index.html
@@ -128,9 +128,10 @@
Utilities
Set Bangle.js Time
- Remove all Apps
+ Remove all Apps
+ Reinstall apps
Install default apps
- Install favourite apps
+ Install favourite apps
Backup
Restore
Settings
@@ -179,6 +180,6 @@
-
+