1
0
Fork 0

Merge branch 'espruino:master' into master

master
Peer David 2022-04-01 08:26:12 +02:00 committed by GitHub
commit 816d3f2997
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
106 changed files with 1630 additions and 393 deletions

View File

@ -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 += '&nbsp;';
markup += '<button class="btn" type="button" onclick="saveEdit(' + id + ', false)">Save Changes</button>';
markup += '&nbsp;';
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 += '&nbsp;';
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 += '&nbsp;';
markup += '<button class="btn" type="button" onclick="saveTokens()">Save to watch</button>';
markup += '&nbsp;';
markup += '<button class="btn" type="button" onclick="startScan(handleImportQr,cancelImportQr)">Import</button>';
markup += '&nbsp;';
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 += '&nbsp;';
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>

View File

@ -1 +1,2 @@
0.01: New App!
0.02: Barometer altitude adjustment setting

View File

@ -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>

View File

@ -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="))

View File

@ -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);

View File

@ -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"}]
}

View File

@ -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);
});

View File

@ -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)

View File

@ -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

View File

@ -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",

View File

@ -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

View File

@ -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);

View File

@ -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"}],

View File

@ -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",

View File

@ -1,4 +1,5 @@
timezonedb.csv.zip
TimeZoneDB.csv.zip
country.csv
zone.csv
timezone.csv
time_zone.csv
database.sql
readme.txt

View File

@ -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

View File

@ -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."];
}
};

View File

@ -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"}],

View File

@ -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,24 +30,17 @@ fs.createReadStream(__dirname+'/country.csv')
} else {
country = countries[r[1]]; // e.g. United States
}
zones[parseInt(r[0])] = {"name": `${city}, ${country}`};
})
.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]);
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;
}
zone = zones[code];
if (starttime<now && (!("starttime" in zone) || zone.starttime<starttime)) {
zone.starttime = starttime;
zone.offs = Math.floor(offs/60);
}
zones[r[0]] = zone;
})
.on('end', () => {
for (z in zones) {
@ -68,7 +61,7 @@ fs.createReadStream(__dirname+'/country.csv')
}
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, "// 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);
@ -83,6 +76,5 @@ fs.createReadStream(__dirname+'/country.csv')
fs.write(fd, "};\n", handleWrite);
console.log('Done.');
});
})
})
});
});

View File

@ -3,3 +3,5 @@
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.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)

View File

@ -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 = '| ';
for (let o = dir.outerBegin; step*o <= step*dir.outerEnd; o=o+step) {
// 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
let allVals = allSquares.map(sq=>{return sq.getExpVal()});
let allLineVals = [];
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);
allLineVals.push(allVals[dir.sqIndex(m,o)]);
}
if (allSquares[idx].expVal == 0 && nextIdx >= 0) {
allSquares[idx].setExpVal(allSquares[nextIdx].expVal);
allSquares[nextIdx].setExpVal(0);
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++;
}
}
}
},
// 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
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) {
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);
}
}
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);
}
}
@ -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]
// 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();

View File

@ -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",

2
apps/glbasic/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.20: New App!

View File

@ -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();

202
apps/glbasic/glbasic.app.js Normal file
View File

@ -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();

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEw4UA///ssp4XthFCBwUBqoABqAaGBZcFBZdX1W1qgLHrwLKqv/6oLJAAILHioLJn5qBAAYLEBQoLeHQQABv4LjGAgLYq2qAAOlBbBHFBdPAKcQLdWcb7jAAoLcn4LKgEVHQVUBQsAgoLLq//6oLIr2q2oXJBZQvCqALGgILTA="))

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
apps/glbasic/icon48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -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}
]
}

View File

@ -29,3 +29,4 @@
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.28: Show distance more accurately in conjunction with new locale app (fix #1523)

View File

@ -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);

View File

@ -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",

1
apps/gsat/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: Added Source Code

3
apps/gsat/README.md Normal file
View File

@ -0,0 +1,3 @@
# Geek Squad Appointment Timer
An app dedicated to setting a 20 minute timer for Geek Squad Appointments.

1
apps/gsat/app-icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwIdah/wAof//4ECgYFB4AFBg4FB8AFBj/wh/4AoM/wEB/gFBvwCEBAU/AQP4gfAj8AgPwAoMPwED8AFBg/AAYIBDA4ngg4TB4EBApkPKgJSBJQIFTMgIFCJIIFDKoIFEvgFBGoMAnw7DP4IFEh+BAoItBg+DNIQwBMIaeCKoKxCPoIzCEgKVHUIqtFXIrFFaIrdFdIwAV"))

38
apps/gsat/app.js Normal file
View File

@ -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);

BIN
apps/gsat/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 929 B

16
apps/gsat/metadata.json Normal file
View File

@ -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}
]
}

BIN
apps/gsat/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -16,3 +16,6 @@
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
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)

View File

@ -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}\`,

View File

@ -1,7 +1,7 @@
{
"id": "locale",
"name": "Languages",
"version": "0.15",
"version": "0.17",
"description": "Translations for different countries",
"icon": "locale.png",
"type": "locale",

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -58,14 +58,22 @@ exports.pushMessage = function(event) {
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 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);

View File

@ -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",

View File

@ -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);
})

View File

@ -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

View File

@ -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=="))

182
apps/mtgwatchface/app.js Normal file

File diff suppressed because one or more lines are too long

BIN
apps/mtgwatchface/black.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
apps/mtgwatchface/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -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
}
]
}

View File

@ -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

View File

@ -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)
![](pebble_screenshot.png)
![](pebble_screenshot2.png)

View File

@ -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"},

View File

@ -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");

View File

@ -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();
}
},
});
})

View File

@ -0,0 +1 @@
0.01: Initial version

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("kMigILIgPAAYMD/ADBwcGhkAwM5wcA/+2//Av/Rn/giFoyFggkUrFggEKlAkCiApCx+AAYNGoADBkU4AYMQj4DBvEICANkAoIPBgE2B4MAiMAH4MAwECAYNALYUgBIISCHYMYAoQWBAIMEgAYBAIMBwEDDQNgDwUf/4eBg4DCAA4"))

120
apps/quicklaunch/app.js Normal file
View File

@ -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();

BIN
apps/quicklaunch/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 B

67
apps/quicklaunch/boot.js Normal file
View File

@ -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);
}
}
});
};
})();

View File

@ -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"}]
}

View File

@ -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)

View File

@ -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;

View File

@ -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",

View File

@ -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);

View File

@ -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)

View File

@ -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]);
}
});

View File

@ -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",

View File

@ -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];

View File

@ -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",

View File

@ -1 +1,2 @@
0.01: New App!
0.02: Add sunrise/sunset. Fix timer bugs.

View File

@ -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

View File

@ -1 +0,0 @@
{"id":"timerclk","name":"tclk Alarm","src":"timerclk.alarm.js","icon":"timerclk.img","version":"0.01","tags":"","files":"","sortorder":10}

File diff suppressed because one or more lines are too long

View File

@ -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);
}

View File

@ -125,3 +125,5 @@ exports.registerControls = function(o) {
});
}
};
exports.timerExpiresIn=t=>t.time-(Date.now()-t.start);

View File

@ -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

View File

@ -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 = {

View File

@ -1 +0,0 @@
{"id":"timerclk","name":"tclk Stopwatch","src":"timerclk.stopwatch.js","icon":"timerclk.img","version":"0.01","tags":"","files":"","sortorder":10}

View File

@ -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;
}
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

View File

@ -1 +0,0 @@
{"id":"timerclk","name":"tclk Timer","src":"timerclk.timer.js","icon":"timerclk.img","version":"0.01","tags":"","files":"","sortorder":10}

View File

@ -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();

View File

@ -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.
![](screenshot2.png)
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).
![](screenshot4.png)
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: "Workout", done: false, children: [] },
{ name: "Learn Rust", done: false, children: [] },
],
{
"name": "Eggs",
"done": false,
"children": []
},
{
"name": "Cheese",
"done": false,
"children": []
}
]
},
{
"name": "Workout",
"done": false,
"children": []
},
{
"name": "Learn Rust",
"done": false,
"children": []
}
]
}
]
```

View File

@ -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>

View File

@ -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 }

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

@ -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

View File

@ -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();
}
}

View File

@ -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",

View File

@ -0,0 +1,2 @@
0.01: New app!
0.02: Make Bangle.js 2 compatible

View File

@ -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.

View File

@ -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) {

View File

@ -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": [

View File

@ -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.

View File

@ -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"}
]
}

View File

@ -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);

Some files were not shown because too many files have changed in this diff Show More