Merge branch 'espruino:master' into master
|
@ -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 += '</td></tr>';
|
||||
markup += '</tbody><tr><td id="advbtn" colspan="2">';
|
||||
markup += '<button type="button" onclick="document.getElementById(\'edittoken\').classList.toggle(\'showadv\')">Advanced</button>';
|
||||
markup += '<button class="btn" type="button" onclick="document.getElementById(\'edittoken\').classList.toggle(\'showadv\')">Advanced</button>';
|
||||
markup += '</td></tr></table></form>';
|
||||
markup += '<button type="button" onclick="updateTokens()">Cancel Edit</button>';
|
||||
markup += '<button type="button" onclick="saveEdit(' + id + ', false)">Save Changes</button>';
|
||||
markup += '<button class="btn" type="button" onclick="updateTokens()">Cancel Edit</button>';
|
||||
markup += ' ';
|
||||
markup += '<button class="btn" type="button" onclick="saveEdit(' + id + ', false)">Save Changes</button>';
|
||||
markup += ' ';
|
||||
if (tokens[id].isnew) {
|
||||
markup += '<button type="button" onclick="startScan(handleTokenQr,cancelTokenQr)">Scan QR</button>';
|
||||
markup += '<button class="btn" type="button" onclick="startScan(handleTokenQr,cancelTokenQr)">Scan QR</button>';
|
||||
} else {
|
||||
markup += '<button type="button" onclick="showTokenQr()">Show QR</button>';
|
||||
markup += '<button type="button" onclick="saveEdit(' + id + ', true)">Forget Token</button>';
|
||||
markup += '<button class="btn" type="button" onclick="showTokenQr()">Show QR</button>';
|
||||
markup += ' ';
|
||||
markup += '<button class="btn" type="button" onclick="saveEdit(' + id + ', true)">Forget Token</button>';
|
||||
}
|
||||
document.getElementById('edit').innerHTML = markup;
|
||||
document.body.className = 'editing';
|
||||
|
@ -304,9 +307,23 @@ function updateTokens() {
|
|||
return '<input name="exp_' + id + '" type="checkbox" onclick="exportTokens(false, \'' + id + '\')">';
|
||||
};
|
||||
const tokenButton = function(fn, id, label, dir) {
|
||||
return '<button type="button" onclick="' + fn + '(' + id + (dir ? ',' + dir : '') + ')">' + label + '</button>';
|
||||
return '<button class="btn" type="button" onclick="' + fn + '(' + id + (dir ? ',' + dir : '') + ')">' + label + '</button>';
|
||||
};
|
||||
var markup = '<table><tr><th>';
|
||||
var markup = '';
|
||||
markup += '<div class="select">';
|
||||
markup += '<button class="btn" type="button" onclick="addToken()">Add Token</button>';
|
||||
markup += ' ';
|
||||
markup += '<button class="btn" type="button" onclick="saveTokens()">Save to watch</button>';
|
||||
markup += ' ';
|
||||
markup += '<button class="btn" type="button" onclick="startScan(handleImportQr,cancelImportQr)">Import</button>';
|
||||
markup += ' ';
|
||||
markup += '<button class="btn" type="button" onclick="document.body.className=\'export\'">Export</button>';
|
||||
markup += '</div><div class="export">';
|
||||
markup += '<button class="btn" type="button" onclick="document.body.className=\'select\'">Cancel</button>';
|
||||
markup += ' ';
|
||||
markup += '<button class="btn" type="button" onclick="exportTokens(true, null)">Show QR</button>';
|
||||
markup += '</div>';
|
||||
markup += '<table><tr><th>';
|
||||
markup += tokenSelect('all');
|
||||
markup += '</th><th>Token</th><th colspan="2">Order</th></tr>';
|
||||
/* any tokens marked new are cancelled new additions and must be removed */
|
||||
|
@ -331,15 +348,6 @@ function updateTokens() {
|
|||
markup += '</td></tr>';
|
||||
}
|
||||
markup += '</table>';
|
||||
markup += '<div class="select">';
|
||||
markup += '<button type="button" onclick="addToken()">Add Token</button>';
|
||||
markup += '<button type="button" onclick="saveTokens()">Save to watch</button>';
|
||||
markup += '<button type="button" onclick="startScan(handleImportQr,cancelImportQr)">Import</button>';
|
||||
markup += '<button type="button" onclick="document.body.className=\'export\'">Export</button>';
|
||||
markup += '</div><div class="export">';
|
||||
markup += '<button type="button" onclick="document.body.className=\'select\'">Cancel</button>';
|
||||
markup += '<button type="button" onclick="exportTokens(true, null)">Show QR</button>';
|
||||
markup += '</div>';
|
||||
document.getElementById('tokens').innerHTML = markup;
|
||||
document.body.className = 'select';
|
||||
}
|
||||
|
@ -604,7 +612,7 @@ function qrBack() {
|
|||
<div id="scan">
|
||||
<table>
|
||||
<tr><td><canvas id="qr-canvas"></canvas></td></tr>
|
||||
<tr><td><button type="button" onclick="scanBack()">Cancel</button></td></tr>
|
||||
<tr><td><button class="btn" type="button" onclick="scanBack()">Cancel</button></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
@ -613,7 +621,7 @@ function qrBack() {
|
|||
|
||||
<div id="showqr">
|
||||
<table><tr><td id="qrcode"></td></tr><tr><td>
|
||||
<button type="button" onclick="qrBack()">Back</button>
|
||||
<button class="btn" type="button" onclick="qrBack()">Back</button>
|
||||
</td></tr></table>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: Barometer altitude adjustment setting
|
||||
|
|
|
@ -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:**<br>
|
||||
Bike Speedometer App by <i>github.com/HilmarSt</i><br>
|
||||
Big parts of the software are based on <i>github.com/espruino/BangleApps/tree/master/apps/speedalt</i><br>
|
||||
|
|
|
@ -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="))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"}]
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
});
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"}],
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
timezonedb.csv.zip
|
||||
TimeZoneDB.csv.zip
|
||||
country.csv
|
||||
zone.csv
|
||||
timezone.csv
|
||||
time_zone.csv
|
||||
database.sql
|
||||
readme.txt
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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."];
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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"}],
|
||||
|
|
|
@ -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<now && (!("starttime" in zone) || zone.starttime<starttime)) {
|
||||
zone.starttime = starttime;
|
||||
zone.offs = Math.floor(offs/60);
|
||||
}
|
||||
zones[r[0]] = zone;
|
||||
})
|
||||
.on('end', () => {
|
||||
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<now && (!("starttime" in zone) || zone.starttime<starttime)) {
|
||||
zone.starttime = starttime;
|
||||
zone.offs = Math.floor(offs/60);
|
||||
}
|
||||
})
|
||||
.on('end', () => {
|
||||
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<offsets.length; i++) {
|
||||
let o = offsets[i].toString();
|
||||
fs.write(fd, ` case ${o}: return ${JSON.stringify(offsdict[o])};\n`, handleWrite);
|
||||
}
|
||||
fs.write(fd, " default: return [\"Houston, we have a bug.\"];\n", handleWrite);
|
||||
fs.write(fd, " }\n", handleWrite);
|
||||
fs.write(fd, "};\n", handleWrite);
|
||||
console.log('Done.');
|
||||
});
|
||||
})
|
||||
})
|
||||
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<offsets.length; i++) {
|
||||
let o = offsets[i].toString();
|
||||
fs.write(fd, ` case ${o}: return ${JSON.stringify(offsdict[o])};\n`, handleWrite);
|
||||
}
|
||||
fs.write(fd, " default: return [\"Houston, we have a bug.\"];\n", handleWrite);
|
||||
fs.write(fd, " }\n", handleWrite);
|
||||
fs.write(fd, "};\n", handleWrite);
|
||||
console.log('Done.');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,4 +2,6 @@
|
|||
0.02: Temporary intermediate version
|
||||
0.03: Basic colors
|
||||
0.04: Bug fix score reset after Game Over, new icon
|
||||
0.05: Chevron marker on the randomly added square
|
||||
0.05: Chevron marker on the randomly added square
|
||||
0.06: Fixed issue 1609 added a message popup state handler to control unwanted screen redraw
|
||||
0.07: Optimized the mover algorithm for efficiency (work in progress)
|
|
@ -144,6 +144,13 @@ const buttons = {
|
|||
},
|
||||
add: function(btn) {
|
||||
this.all.push(btn);
|
||||
},
|
||||
isPopUpActive: false,
|
||||
activatePopUp: function() {
|
||||
this.isPopUpActive = true;
|
||||
},
|
||||
deActivatePopUp: function() {
|
||||
this.isPopUpActive = false;
|
||||
}
|
||||
};
|
||||
/**
|
||||
|
@ -157,16 +164,16 @@ const buttons = {
|
|||
const mover = {
|
||||
direction: {
|
||||
up: {name: 'up', step: 1, innerBegin: 0, innerEnd: rows-1, outerBegin: 0, outerEnd: cols-1, iter: rows -1,
|
||||
sqIndex: function (m,n) {return m*(cols) + n;}, sqNextIndex: function (m,n) {return m < rows -1 ? (m+1)*(cols) + n : -1;}
|
||||
sqIndex: function (i,o) {return i*(cols) + o;}, sqNextIndex: function (i,o) {return i < rows -1 ? (i+1)*(cols) + o : -1;}
|
||||
},
|
||||
down: {name: 'down', step:-1, innerBegin: rows-1, innerEnd: 0, outerBegin: cols-1, outerEnd: 0, iter: rows -1,
|
||||
sqIndex: function (m,n) {return m*(cols) + n;}, sqNextIndex: function (m,n) {return m > 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<sortedLineVals.length-1) {
|
||||
if (sortedLineVals[r] == sortedLineVals[r+1]) {
|
||||
++sortedLineVals[r];
|
||||
addToScore(sortedLineVals[r]);
|
||||
sortedLineVals[++r] = 0;
|
||||
}
|
||||
r++;
|
||||
}
|
||||
let mergedLineVals = sortedLineVals.filter((val)=>{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();
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.20: New App!
|
||||
|
|
@ -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();
|
|
@ -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();
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEw4UA///ssp4XthFCBwUBqoABqAaGBZcFBZdX1W1qgLHrwLKqv/6oLJAAILHioLJn5qBAAYLEBQoLeHQQABv4LjGAgLYq2qAAOlBbBHFBdPAKcQLdWcb7jAAoLcn4LKgEVHQVUBQsAgoLLq//6oLIr2q2oXJBZQvCqALGgILTA="))
|
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 1.3 KiB |
|
@ -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}
|
||||
]
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: Added Source Code
|
|
@ -0,0 +1,3 @@
|
|||
# Geek Squad Appointment Timer
|
||||
|
||||
An app dedicated to setting a 20 minute timer for Geek Squad Appointments.
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwIdah/wAof//4ECgYFB4AFBg4FB8AFBj/wh/4AoM/wEB/gFBvwCEBAU/AQP4gfAj8AgPwAoMPwED8AFBg/AAYIBDA4ngg4TB4EBApkPKgJSBJQIFTMgIFCJIIFDKoIFEvgFBGoMAnw7DP4IFEh+BAoItBg+DNIQwBMIaeCKoKxCPoIzCEgKVHUIqtFXIrFFaIrdFdIwAV"))
|
|
@ -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);
|
After Width: | Height: | Size: 929 B |
|
@ -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}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 4.2 KiB |
|
@ -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
|
||||
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)
|
||||
|
|
|
@ -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}\`,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "locale",
|
||||
"name": "Languages",
|
||||
"version": "0.15",
|
||||
"version": "0.17",
|
||||
"description": "Translations for different countries",
|
||||
"icon": "locale.png",
|
||||
"type": "locale",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
})
|
||||
|
|
|
@ -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
|
|
@ -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=="))
|
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 2.2 KiB |
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||

|
||||

|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
},
|
||||
});
|
||||
})
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: Initial version
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("kMigILIgPAAYMD/ADBwcGhkAwM5wcA/+2//Av/Rn/giFoyFggkUrFggEKlAkCiApCx+AAYNGoADBkU4AYMQj4DBvEICANkAoIPBgE2B4MAiMAH4MAwECAYNALYUgBIISCHYMYAoQWBAIMEgAYBAIMBwEDDQNgDwUf/4eBg4DCAA4"))
|
|
@ -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.name<b.name) return -1;
|
||||
if (a.name>b.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();
|
After Width: | Height: | Size: 321 B |
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
})();
|
|
@ -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"}]
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: Add sunrise/sunset. Fix timer bugs.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"id":"timerclk","name":"tclk Alarm","src":"timerclk.alarm.js","icon":"timerclk.img","version":"0.01","tags":"","files":"","sortorder":10}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -125,3 +125,5 @@ exports.registerControls = function(o) {
|
|||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.timerExpiresIn=t=>t.time-(Date.now()-t.start);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"id":"timerclk","name":"tclk Stopwatch","src":"timerclk.stopwatch.js","icon":"timerclk.img","version":"0.01","tags":"","files":"","sortorder":10}
|
|
@ -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
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"id":"timerclk","name":"tclk Timer","src":"timerclk.timer.js","icon":"timerclk.img","version":"0.01","tags":"","files":"","sortorder":10}
|
|
@ -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();
|
||||
|
|
|
@ -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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
|
@ -0,0 +1,135 @@
|
|||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../../css/spectre.min.css" />
|
||||
|
||||
<style type="text/css">
|
||||
.alert {
|
||||
padding: 20px;
|
||||
background-color: #f44336; /* Red */
|
||||
color: white;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="info"></div>
|
||||
|
||||
<button id="btnReload" class="btn btn-primary">Reload from watch</button>
|
||||
<button id="btnUpload" class="btn btn-primary">Upload to watch</button>
|
||||
<button id="btnDownload" class="btn btn-primary">Download</button>
|
||||
|
||||
<pre id="todos" contenteditable></pre>
|
||||
|
||||
<script src="../../core/lib/interface.js"></script>
|
||||
<script>
|
||||
const fileTodoList = "todolist.json";
|
||||
|
||||
function errorFormat() {
|
||||
var date = new Date();
|
||||
var error =
|
||||
'<p class="alert">' +
|
||||
date.toUTCString() +
|
||||
" : Wrong format, it should be JSON" +
|
||||
"</p>";
|
||||
return error;
|
||||
}
|
||||
|
||||
function getEditableContent() {
|
||||
return document.getElementById("todos").innerHTML.replace(/<[^>]*>/g, '');;
|
||||
}
|
||||
|
||||
function isJsonString(str) {
|
||||
try {
|
||||
JSON.parse(str);
|
||||
} catch (e) {
|
||||
console.log(str)
|
||||
console.log(e)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function uploadFile(fileid, contents) {
|
||||
Puck.write(
|
||||
`\x10(function() {
|
||||
require("Storage").write("${fileid}",'${contents}');
|
||||
Bluetooth.print("OK");
|
||||
})()\n`,
|
||||
(ret) => {
|
||||
console.log("uploadFile", ret);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/* Load settings JSON file from the watch.
|
||||
*/
|
||||
function loadTodos() {
|
||||
document.getElementById("info").innerHTML = "";
|
||||
Util.showModal("Loading...");
|
||||
Puck.eval(`require('Storage').readJSON("${fileTodoList}")`, (data) => {
|
||||
document.getElementById("todos").innerHTML = JSON.stringify(
|
||||
data,
|
||||
null,
|
||||
2
|
||||
);
|
||||
Util.hideModal();
|
||||
});
|
||||
}
|
||||
/* Save settings as a JSON file on the watch.
|
||||
*/
|
||||
function uploadTodos() {
|
||||
document.getElementById("info").innerHTML = "";
|
||||
Util.showModal("Uploading...");
|
||||
let jsonTodos = getEditableContent();
|
||||
if (isJsonString(jsonTodos)) {
|
||||
let shortJsonTodos = JSON.stringify(JSON.parse(jsonTodos));
|
||||
uploadFile(fileTodoList, shortJsonTodos);
|
||||
} else {
|
||||
document.getElementById("info").innerHTML = errorFormat();
|
||||
}
|
||||
Util.hideModal();
|
||||
}
|
||||
|
||||
function downloadTodos() {
|
||||
document.getElementById("info").innerHTML = "";
|
||||
Util.showModal("Downloading...");
|
||||
let jsonTodos = getEditableContent();
|
||||
if (isJsonString(jsonTodos)) {
|
||||
var a = document.createElement("a"),
|
||||
file = new Blob([jsonTodos], { type: "application/json" });
|
||||
var url = URL.createObjectURL(file);
|
||||
a.href = url;
|
||||
a.download = fileTodoList;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
setTimeout(function () {
|
||||
document.body.removeChild(a);
|
||||
window.URL.revokeObjectURL(url);
|
||||
}, 0);
|
||||
} else {
|
||||
document.getElementById("info").innerHTML = errorFormat();
|
||||
}
|
||||
Util.hideModal();
|
||||
}
|
||||
|
||||
document
|
||||
.getElementById("btnUpload")
|
||||
.addEventListener("click", function () {
|
||||
uploadTodos();
|
||||
});
|
||||
document
|
||||
.getElementById("btnDownload")
|
||||
.addEventListener("click", function () {
|
||||
downloadTodos();
|
||||
});
|
||||
document
|
||||
.getElementById("btnReload")
|
||||
.addEventListener("click", function () {
|
||||
loadTodos();
|
||||
});
|
||||
function onInit() {
|
||||
loadTodos();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -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 }
|
||||
|
|
After Width: | Height: | Size: 72 KiB |
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.01: New app!
|
||||
0.02: Make Bangle.js 2 compatible
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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": [
|
||||
|
|
|
@ -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.
|
|
@ -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"}
|
||||
]
|
||||
}
|
|
@ -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);
|